Ticket #250: libbasic_fix_1_of_1_rev_b1ce004d9025_Issue__250__Smalltak_X_is_reading_Windows_Registry_only_in_ASCII_but_registry_is_UTF16.patch
File libbasic_fix_1_of_1_rev_b1ce004d9025_Issue__250__Smalltak_X_is_reading_Windows_Registry_only_in_ASCII_but_registry_is_UTF16.patch, 21.1 KB (added by , 5 years ago) |
---|
-
Win32OperatingSystem.st
# HG changeset patch # User Patrik Svestka <patrik.svestka@gmail.com> # Date 1542973588 -3600 # Fri Nov 23 12:46:28 2018 +0100 # Branch jv # Node ID b1ce004d9025164d4aa9e26a9b764a9100f57da4 # Parent 20f35741c92ddc9da0dda0e43d8b34c2072a1285 Issue #250: Smalltak/X is reading Windows Registry only in ASCII but registry is UTF16 All methods are now unicode only Fixed reading of unicode name and values from registry via valueNamed: - Added a REG_QWORD registry type (64bit number) - Added regression tests for the changes at RegressionTests::Win32OperatingSystemTest - removed all ASCII only reading -> only unicode version is now supported - replaced while(*cp++) itteration with wide-string search (wcschr) - faster & clear intent - added a character limit read to count the string lenght (REG_MULTI_SZ) - clearing the pointer after usage - renamed a method parameter aValueName to just name - added a nameUtf16Z which formats the input name into Unicode16String with terminating NULL - Added a comment on registry size limitations at #valueNamed:put: The #valueNameAtIndex: - The method is used at (all these now support unicode): - #valueNames - replaced nextPut: with nextPutUnicode: - #valueNamesAndValuesDo: - #valueNamesDo: The #subKeyAtIndex: - The method is used at (all these now support unicode): - #subKeysDo: - #allSubKeysDo: Others fixed: - #subKeyNameAndClassAtIndex: - #remoteKeyOnHost: - - added a hostNameUtf16Z which formats the input name into Unicode16String with terminating NULL diff -r 20f35741c92d -r b1ce004d9025 Win32OperatingSystem.st
a b 16073 16073 16074 16074 remoteKeyOnHost:hostName 16075 16075 "return the corresponding registry entry from 16076 a remote computers registry." 16077 16078 |newEntry remoteHandle errorNumber| 16079 16076 a remote computers registry. 16077 Note: The registry key must be form a predefined list defined by Microsoft." 16078 16079 |hostNameUtf16Z newEntry remoteHandle errorNumber| 16080 16081 hostNameUtf16Z := hostName notEmptyOrNil ifTrue:[hostName asUnicode16StringZ]. 16082 16080 16083 %{ 16081 16084 HKEY myKey, remoteKey = 0; 16082 16085 int _retVal; 16083 16086 16084 if (__isExternalAddressLike(__INST(handle)) && __isStringLike(hostName)) { 16087 /* Notes from MSDN: 16088 * link: https://docs.microsoft.com/en-us/windows/desktop/api/winreg/nf-winreg-regconnectregistryw 16089 * 16090 * 1) RegConnectRegistry requires the Remote Registry service to be running on the remote computer. 16091 * By default, this service is configured to be started manually. To configure the Remote Registry 16092 * service to start automatically, run Services.msc and change the Startup Type of the service to Automatic. 16093 * 16094 * 2) If the computer is joined to a workgroup and the "Force network logons using local accounts to authenticate as 16095 * Guest" policy is enabled, the function fails. Note that this policy is enabled by default if the computer is joined 16096 * to a workgroup. 16097 * 16098 * 3) If the current user does not have proper access to the remote computer, the call to RegConnectRegistry fails. To connect 16099 * to a remote registry, call LogonUser with LOGON32_LOGON_NEW_CREDENTIALS and ImpersonateLoggedOnUser before calling 16100 * RegConnectRegistry. 16101 * 16102 * 4) myKey must be a predefined registry handle. 16103 * more at: https://docs.microsoft.com/en-us/windows/desktop/SysInfo/predefined-keys 16104 * 16105 * 5) When a handle returned by RegConnectRegistry is no longer needed, it should be closed by calling RegCloseKey. (done by registerForFinaliation) 16106 * 16107 */ 16108 16109 if (__isExternalAddressLike(__INST(handle)) && __isUnicode16String(hostNameUtf16Z)) { 16085 16110 myKey = (HKEY)__externalAddressVal(__INST(handle)); 16086 if ((_retVal = RegConnectRegistry A(__stringVal(hostName), myKey, &remoteKey)) == ERROR_SUCCESS) {16111 if ((_retVal = RegConnectRegistryW(__unicode16StringVal(hostNameUtf16Z), myKey, &remoteKey)) == ERROR_SUCCESS) { 16087 16112 remoteHandle = __MKEXTERNALADDRESS(remoteKey); 16088 16113 } else { 16089 16114 if ((_retVal != ERROR_PATH_NOT_FOUND) … … 16100 16125 ]. 16101 16126 errorNumber notNil ifTrue:[ 16102 16127 (OperatingSystem errorHolderForNumber:errorNumber) reportError. 16103 ]. 16128 ]. 16129 16104 16130 ^ nil 16105 16131 16106 16132 " … … 16131 16157 16132 16158 subKeyAtIndex:subKeyIndex 16133 16159 "return a new registry entry below mySelf for the given subKey index. 16134 Return nil if no such key exists" 16160 Return nil if no such key exists 16161 WARNING: subKeyIndex is 0-based!!" 16162 16135 16163 16136 16164 |subKeyName subKeyClassName errorNumber| 16137 16165 16138 16166 %{ 16139 16167 HKEY myKey, subKey = 0; 16140 char nameBuffer[256]; 16141 DWORD nameSize = sizeof(nameBuffer) - 1; 16142 char classNameBuffer[256]; 16143 DWORD classNameSize = sizeof(classNameBuffer) - 1; 16168 16169 /* lpName (nameBuffer): 16170 * A pointer to a buffer that receives the name of the subkey, including the 16171 * terminating null character. The function copies only the name of the subkey, 16172 * not the full key hierarchy, to the buffer. If the function fails, no information is copied to this buffer. 16173 * 16174 * note: This actually means that the if the path fits within 255 chacaters you could get another 255 characters for the key itself. 16175 * This could help if you are having issues with the registry path lenght. 16176 */ 16177 wchar_t nameBuffer[256]; // 256 is due to Key name limit (including path) 16178 DWORD nameSize = sizeof(nameBuffer); 16179 16180 /* lpClass (classNameBuffer): 16181 * A pointer to a buffer that receives the user-defined class of the enumerated subkey. 16182 */ 16183 wchar_t classNameBuffer[256]; 16184 DWORD classNameSize = sizeof(classNameBuffer); 16144 16185 FILETIME modificationTime; 16145 16186 int _retVal; 16146 16187 16147 16188 if (__isExternalAddressLike(__INST(handle)) 16148 16189 && __isSmallInteger(subKeyIndex)) { 16149 16190 myKey = (HKEY)__externalAddressVal(__INST(handle)); 16150 if ((_retVal = RegEnumKeyEx A(myKey, __intVal(subKeyIndex),16191 if ((_retVal = RegEnumKeyExW(myKey, __intVal(subKeyIndex), 16151 16192 nameBuffer, &nameSize, 16152 16193 NULL, 16153 16194 classNameBuffer, &classNameSize, 16154 16195 &modificationTime)) == ERROR_SUCCESS) { 16155 16196 nameBuffer[nameSize] = '\0'; 16156 16197 classNameBuffer[classNameSize] = '\0'; 16157 subKeyName = __MK STRING(nameBuffer);16158 subKeyClassName = __MK STRING(classNameBuffer);16198 subKeyName = __MKU16STRING(nameBuffer); 16199 subKeyClassName = __MKU16STRING(classNameBuffer); 16159 16200 } else { 16160 16201 if ((_retVal != ERROR_PATH_NOT_FOUND) 16161 16202 && (_retVal != ERROR_FILE_NOT_FOUND) … … 16183 16224 16184 16225 subKeyNameAndClassAtIndex:subKeyIndex 16185 16226 "return the name and className of the given subKey at index as a pair. 16186 Return nil if no such key exists" 16227 Return nil if no such key exists 16228 WARNING: subKeyIndex is 0-based!!" 16187 16229 16188 16230 |subKeyName subKeyClassName errorNumber| 16189 16231 16190 16232 %{ 16191 16233 HKEY myKey, subKey = 0; 16192 char nameBuffer[256]; 16193 DWORD nameSize = sizeof(nameBuffer) - 1; 16194 char classNameBuffer[256]; 16195 DWORD classNameSize = sizeof(classNameBuffer) - 1; 16234 16235 /* lpName (nameBuffer): 16236 * A pointer to a buffer that receives the name of the subkey, including the 16237 * terminating null character. The function copies only the name of the subkey, 16238 * not the full key hierarchy, to the buffer. If the function fails, no information is copied to this buffer. 16239 * 16240 * note: This actually means that the if the path fits within 255 chacaters you could get another 255 characters for the key itself. 16241 * This could help if you are having issues with the registry path lenght. 16242 */ 16243 wchar_t nameBuffer[256]; // limiting the nameBuffer to 255 characters + null terminator 16244 DWORD nameSize = sizeof(nameBuffer); 16245 16246 /* lpClass (classNameBuffer): 16247 * A pointer to a buffer that receives the user-defined class of the enumerated subkey. 16248 */ 16249 wchar_t classNameBuffer[256]; // limiting the classNameBuffer to 255 characters + nul terminator 16250 DWORD classNameSize = sizeof(classNameBuffer); 16251 16196 16252 FILETIME modificationTime; 16197 16253 int _retVal; 16198 16254 16199 16255 if (__isExternalAddressLike(__INST(handle)) 16200 16256 && __isSmallInteger(subKeyIndex)) { 16201 16257 myKey = (HKEY)__externalAddressVal(__INST(handle)); 16202 if ((_retVal = RegEnumKeyEx A(myKey, __intVal(subKeyIndex),16258 if ((_retVal = RegEnumKeyExW(myKey, __intVal(subKeyIndex), 16203 16259 nameBuffer, &nameSize, 16204 16260 NULL, 16205 16261 classNameBuffer, &classNameSize, 16206 16262 &modificationTime)) == ERROR_SUCCESS) { 16207 16263 nameBuffer[nameSize] = '\0'; 16208 classNameBuffer[classNameSize] = '\0'; 16209 subKeyName = __MKSTRING(nameBuffer);16210 subKeyClassName = __MKSTRING(classNameBuffer);16264 classNameBuffer[classNameSize] = '\0'; 16265 subKeyName = __MKU16STRING(nameBuffer); 16266 subKeyClassName = __MKU16STRING(classNameBuffer); 16211 16267 } else { 16212 16268 if ((_retVal != ERROR_PATH_NOT_FOUND) 16213 16269 && (_retVal != ERROR_FILE_NOT_FOUND) … … 16432 16488 16433 16489 valueNameAtIndex:valueIndex 16434 16490 "return a values name for the given value index. 16435 Return nil if no such value exists" 16491 Return nil if no such value exists 16492 WARNING: valueIndex is 0-based!!" 16436 16493 16437 16494 |valueName errorNumber| 16438 16495 16439 16496 %{ 16440 16497 HKEY myKey; 16441 char nameBuffer[256]; 16442 DWORD nameSize = sizeof(nameBuffer) - 1; 16498 16499 /* nameBuffer (lpValueName in RegEnumValue function): 16500 * A pointer to a buffer that receives the name of the value as a null-terminated string. 16501 * This buffer must be large enough to include the terminating null character. 16502 * For more information, see Registry Element Size Limits. 16503 */ 16504 wchar_t nameBuffer[256]; // 256 is due to Key name limit (including path) + the null character 16505 16506 /* nameSize (lpcchValueName in RegEnumValue function): 16507 * 16508 * A pointer to a variable that specifies the size of the buffer pointed to by the lpValueName parameter, in characters. 16509 * When the function returns, the variable receives the number of characters stored in the buffer, not including the terminating null character. 16510 * 16511 * Registry value names are limited to 32,767 bytes. The ANSI version of this function treats this parameter as a SHORT value. 16512 * Therefore, if you specify a value greater than 32,767 bytes, there is an overflow and the function may return ERROR_MORE_DATA. 16513 */ 16514 DWORD nameSize = sizeof(nameBuffer); 16443 16515 DWORD valueType; 16444 16516 int _retVal; 16445 16517 16446 16518 if (__isExternalAddressLike(__INST(handle)) 16447 16519 && __isSmallInteger(valueIndex)) { 16448 16520 myKey = (HKEY)__externalAddressVal(__INST(handle)); 16449 if ((_retVal = RegEnumValue A(myKey, __intVal(valueIndex),16521 if ((_retVal = RegEnumValueW(myKey, __intVal(valueIndex), 16450 16522 nameBuffer, &nameSize, 16451 16523 NULL, 16452 16524 &valueType, 16453 16525 NULL, NULL)) == ERROR_SUCCESS) { 16454 16526 nameBuffer[nameSize] = '\0'; 16455 valueName = __MKSTRING(nameBuffer);16527 valueName = __MKU16STRING(nameBuffer); 16456 16528 } else { 16457 16529 if ((_retVal != ERROR_PATH_NOT_FOUND) 16458 16530 && (_retVal != ERROR_FILE_NOT_FOUND) … … 16475 16547 " 16476 16548 ! 16477 16549 16478 valueNamed: aValueName16550 valueNamed:name 16479 16551 "retrieve a value; the returned object depends upon the type: 16480 16552 REG_BINARY -> ByteArray 16481 16553 REG_SZ -> String … … 16484 16556 REG_NONE -> nil 16485 16557 " 16486 16558 16487 |stringArray retVal errorNumber| 16559 |nameUtf16Z stringArray retVal errorNumber| 16560 16561 nameUtf16Z := name notEmptyOrNil ifTrue:[name asUnicode16StringZ]. 16488 16562 16489 16563 %{ /* STACK: 20000 */ 16490 16564 HKEY myKey; 16491 16565 DWORD valueType; 16566 int val; 16492 16567 union { 16493 DWORD dWord; 16494 unsigned char dWordBytes[4]; 16495 unsigned char smallDataBuffer[1024*16]; 16568 DWORD dWord; 16569 unsigned char dWordBytes[4]; // needed for the shifts at REG_DWORD_LITTLE_ENDIAN and REG_DWORD_BIG_ENDIAN 16570 ULONGLONG qWord; // needed for the 64-bit int (QWORD) 16571 wchar_t wstringBuffer[1024*8]; // buffer for wide characters 16496 16572 } quickData; 16497 int val; 16573 16574 wchar_t *dataBuffer = NULL; 16498 16575 DWORD dataSize = sizeof(quickData); 16499 unsigned char *dataBuffer = NULL;16500 #define xxUSE_UNICODE16501 #ifdef USE_UNICODE16502 # define xRegQueryValueEx RegQueryValueExW16503 # define CHAR short16504 #else16505 # define RegQueryValueEx RegQueryValueExA16506 # define CHAR char16507 #endif16508 16576 16509 16577 if (__isExternalAddressLike(__INST(handle)) 16510 && __is StringLike(aValueName)) {16578 && __isUnicode16String(nameUtf16Z)) { 16511 16579 int ret; 16512 16580 16513 16581 myKey = (HKEY)__externalAddressVal(__INST(handle)); 16514 16582 16515 /* 16516 * try to get it with one call ... 16517 */ 16518 ret = RegQueryValueExA(myKey, __stringVal(aValueName), 16583 /* Reading values from registry 16584 * 16585 * LSTATUS RegQueryValueEx( // ends with either A(ascii) or W (unicode) 16586 * HKEY hKey, // a handle of a registry key 16587 * LPCWSTR lpValueName, // The name of the registry value 16588 * LPDWORD lpReserved, // reserved and must be NULL 16589 * LPDWORD lpType, // A pointer to a variable that recieves a code indicating the type of data stored in the specified value 16590 * LPBYTE lpData, // A pointer to a buffer that receives the value's data. This parameter can be NULL if datat is not required 16591 * LPDWORD lpcbData // A pointer to a variable that specifies the size of the buffer pointed to by the IpData parameter, in bytes. 16592 * // When the furnction returns, this variable contains the size of the data copied to IpData 16593 * ); 16594 */ 16595 16596 #if 0 16597 console_printf("================================================\n"); 16598 console_printf("Before - myKey: %p\n", &myKey); 16599 console_printf("Before - aValueName: %S\n", __unicode16StringVal(nameUtf16Z)); 16600 console_printf("Before - valueType: %p, valueTypeData: %lu\n", &valueType, valueType); 16601 console_printf("Before - QuickData dWord: %lu\n", quickData.dWord); 16602 console_printf("Before - QuickData dWordBytes: %S\n", quickData.dWordBytes); 16603 console_printf("Before - QuickData qWord: %lu\n", quickData.qWord); 16604 console_printf("Before - QuickData smallDataBuffer: %S\n", quickData.smallDataBuffer); 16605 console_printf("Before - Databuffer: %p, dataBufferData: %s\n", &dataBuffer, dataBuffer); 16606 console_printf("Before - dataSize: %p, dataSizeData: %lu\n", &dataSize, dataSize); 16607 console_printf("================================================\n"); 16608 #endif 16609 16610 /* 16611 * try to get it with one call ... 16612 */ 16613 ret = RegQueryValueExW(myKey, __unicode16StringVal(nameUtf16Z), 16519 16614 NULL, 16520 16615 &valueType, 16521 (char *)&quickData, 16616 (LPBYTE) &quickData.wstringBuffer, // LPBYTE aka (unsigned char *) 16522 16617 &dataSize); 16618 16619 #if 0 16620 console_printf("------------------------------------------------\n"); 16621 console_printf("After - myKey: %p\n", &myKey); 16622 console_printf("After - aValueName: %S\n", __unicode16StringVal(nameUtf16Z)); 16623 console_printf("After - valueType: %p, valueTypeData: %lu\n", &valueType, valueType); 16624 console_printf("After - QuickData dWord: %lu\n", quickData.dWord); 16625 console_printf("After - QuickData dWordBytes: %S\n", quickData.dWordBytes); 16626 console_printf("After - QuickData qWord: %lu\n", quickData.qWord); 16627 console_printf("After - QuickData smallDataBuffer: %S\n", quickData.smallDataBuffer); 16628 console_printf("After - Databuffer: %p, dataBufferData: %s\n", &dataBuffer, dataBuffer); 16629 console_printf("After - dataSize: %p, dataSizeData: %lu\n", &dataSize, dataSize); 16630 console_printf("------------------------------------------------\n"); 16631 #endif 16632 16523 16633 #if 0 16524 console_printf("get \"%s\": dataSize=%d ret=%d\n", __stringVal(aValueName), dataSize, ret); 16634 console_printf("Registry key: %p\n", (void *) myKey); 16635 console_printf("get \"%S\": dataSize=%d ret=%d\n", __unicode16StringVal(nameUtf16Z), dataSize, ret); 16525 16636 #endif 16526 16637 while (ret == ERROR_MORE_DATA) { 16527 16638 #if 0 16528 16639 console_printf("ERROR_MORE_DATA dataSize=%d valueType=%d\n", dataSize, valueType); 16529 16640 #endif 16530 16641 /* 16531 16642 * nope - need another one ... 16532 16643 */ 16533 if (myKey = HKEY_PERFORMANCE_DATA) {16644 if (myKey == HKEY_PERFORMANCE_DATA) { 16534 16645 dataSize = dataSize * 2; 16535 16646 } 16536 16647 switch (valueType) { 16537 16648 case REG_BINARY: 16538 16649 case REG_MULTI_SZ: 16539 dataBuffer = malloc(dataSize); ;16650 dataBuffer = malloc(dataSize); 16540 16651 break; 16541 16652 case REG_SZ: 16542 16653 dataBuffer = malloc(dataSize); … … 16546 16657 break; 16547 16658 } 16548 16659 if (dataBuffer) { 16549 ret = RegQueryValueEx (myKey, __stringVal(aValueName),16660 ret = RegQueryValueExW(myKey, __unicode16StringVal(nameUtf16Z), 16550 16661 NULL, 16551 16662 &valueType, 16552 dataBuffer,16663 (LPBYTE) dataBuffer, 16553 16664 &dataSize); 16554 16665 } else { 16555 16666 break; … … 16569 16680 retVal = nil; 16570 16681 break; 16571 16682 16572 case REG_BINARY: 16573 retVal = __MKBYTEARRAY(dataBuffer ? dataBuffer : quickData. smallDataBuffer, dataSize);16683 case REG_BINARY: 16684 retVal = __MKBYTEARRAY(dataBuffer ? dataBuffer : quickData.wstringBuffer, dataSize); 16574 16685 break; 16575 16686 16576 16687 case REG_SZ: 16577 16688 case REG_EXPAND_SZ: 16578 #ifdef USE_UNICODE 16579 retVal = __MKU16STRING(dataBuffer ? dataBuffer : quickData.smallDataBuffer); 16580 #else 16581 retVal = __MKSTRING(dataBuffer ? dataBuffer : quickData.smallDataBuffer); 16582 #endif 16689 16690 #if 0 16691 console_printf("dataBuffer before __MKU16STTRING: %S\n", dataBuffer); 16692 console_printf("QuickData smallDataBuffer before __MKU16STTRING: %S\n", quickData.wstringBuffer); 16693 #endif 16694 retVal = __MKU16STRING(dataBuffer ? dataBuffer : quickData.wstringBuffer); 16695 #if 0 16696 console_printf("QuickData smallDataBuffer: %S\n", quickData.wstringBuffer); 16697 console_printf("dataBuffer: %S\n", dataBuffer); 16698 #endif 16699 16583 16700 break; 16584 16701 16585 16702 #if 0 … … 16603 16720 val = (val << 8) | quickData.dWordBytes[3]; 16604 16721 retVal = __MKUINT(val); 16605 16722 break; 16606 16723 16724 case REG_QWORD: // only REG_QWORD_LITTLE_ENDIAN present (no need for shifts) 16725 retVal = __MKULARGEINT(quickData.qWord); 16726 break; 16727 16607 16728 case REG_MULTI_SZ: 16608 16729 { 16609 CHAR*cp, *cp0;16730 wchar_t *cp, *cp0; 16610 16731 int ns, i; 16611 16612 cp0 = dataBuffer ? dataBuffer : quickData.smallDataBuffer; 16732 16733 cp0 = dataBuffer ? dataBuffer : quickData.wstringBuffer; 16734 16613 16735 #if 0 16736 16614 16737 console_printf("**************\n"); 16615 16738 for (i=0;i<50;i++) { 16616 16739 console_printf("%x ", cp0[i]); … … 16627 16750 #endif 16628 16751 cp = cp0; 16629 16752 ns = 0; 16630 while (*cp) { 16631 while (*cp++) ;; 16632 ns++; 16753 16754 // datasize 16755 while ((((cp - cp0) * sizeof(wchar_t)) < (dataSize - 1)) // limits the string 16756 && *cp) { // check if the dereferenced value is NULL 16757 cp = wcschr(cp, '\0'); // wide string search for terminating null character 16758 *cp++; 16759 ns++; 16633 16760 } 16761 16762 // clear any remaining value 16763 *cp = '\0'; 16634 16764 stringArray = __ARRAY_NEW_INT(ns); 16635 16765 16636 16766 i = 0; 16637 16767 while (*cp0) { 16638 16768 OBJ s; 16639 CHAR *cp; 16640 16641 cp = cp0; 16642 while (*cp++) ;; 16643 #ifdef USE_UNICODE 16644 s = __MKU16STRING(cp0); __ArrayInstPtr(stringArray)->a_element[i] = s; __STORE(stringArray, s); 16645 #else 16646 s = __MKSTRING(cp0); __ArrayInstPtr(stringArray)->a_element[i] = s; __STORE(stringArray, s); 16647 #endif 16648 cp0 = cp; 16769 16770 cp = cp0; 16771 s = __MKU16STRING(cp0); __ArrayInstPtr(stringArray)->a_element[i] = s; __STORE(stringArray, s); 16772 cp = wcschr(cp, '\0'); 16773 *cp++; 16774 cp0 = cp; 16649 16775 i++; 16776 16650 16777 } 16651 16778 retVal = stringArray; 16652 break; 16653 } 16779 break; 16780 } 16654 16781 default: 16655 16782 console_printf("RegistryEntry [warning]: unhandled valueType: %d\n", valueType); 16656 16783 break; … … 16660 16787 && (ret != ERROR_FILE_NOT_FOUND)) { 16661 16788 errorNumber = __MKSMALLINT(ret); 16662 16789 } 16663 } 16790 } 16664 16791 } 16665 16792 if (dataBuffer) free(dataBuffer); 16793 16666 16794 %}. 16667 16795 errorNumber notNil ifTrue:[ 16668 16796 (OperatingSystem errorHolderForNumber:errorNumber) reportError. … … 16689 16817 16690 16818 |data stringArray errorNumber| 16691 16819 %{ 16820 /* Registry Element Size Limits 16821 * 16822 * Found at MSDN: https://docs.microsoft.com/en-us/windows/desktop/SysInfo/registry-element-size-limits 16823 * 16824 * Registry Element | Size Limit 16825 * Key name 255 characters. The key name includes the absolute path of the key in the registry, always starting at a base key, for example, HKEY_LOCAL_MACHINE. 16826 * Value name 16,383 characters; Windows 2000: 260 ANSI characters or 16,383 Unicode characters. 16827 * Value Available memory (latest format)1 MB (standard format) 16828 * Tree A registry tree can be 512 levels deep. You can create up to 32 levels at a time through a single registry API call. 16829 * 16830 * Notes: 16831 * - Long values (more than 2,048 bytes) should be stored in a file, and the location of the file should be stored in the registry. 16832 * This helps the registry perform efficiently. 16833 * 16834 * - The file location can be the name of a value or the data of a value. Each backslash in the location string must be preceded by 16835 * another backslash as an escape character. For example, specify "C:\\mydir\\myfile" to store the string "C:\mydir\myfile". 16836 * A file location can also be the name of a key if it is within the 255-character limit for key names and does not contain backslashes, 16837 * which are not allowed in key names. 16838 */ 16839 16692 16840 HKEY myKey; 16693 16841 DWORD valueType = -1; 16694 16842 int val; … … 16918 17066 "evaluate aBlock for all value names" 16919 17067 16920 17068 ^ Array streamContents:[:s | 16921 self valueNamesDo:[:nm | s nextPut :nm]17069 self valueNamesDo:[:nm | s nextPutUnicode:nm] 16922 17070 ]. 16923 17071 16924 17072 "Created: / 18-01-2011 / 20:24:52 / cg"