Ticket #250: libbasic_fix_1_of_2_rev_4debcdcaf264_Issue__250__Smalltak_X_is_reading_Windows_Registry_only_in_ASCII_but_registry_is_UTF16.patch

File libbasic_fix_1_of_2_rev_4debcdcaf264_Issue__250__Smalltak_X_is_reading_Windows_Registry_only_in_ASCII_but_registry_is_UTF16.patch, 21.6 KB (added by patrik.svestka@…, 5 years ago)

First patch

  • Win32OperatingSystem.st

    # HG changeset patch
    # User Patrik Svestka <patrik.svestka@gmail.com>
    # Date 1544011404 -3600
    #      Wed Dec 05 13:03:24 2018 +0100
    # Branch jv
    # Node ID 4debcdcaf26435461bea223bcd3431307dbeae0b
    # 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
    - checking if registry value name is a string if not return nil and print message into transcript
    - defaultValue can be now used - reading a (default) value from a reagistry Key
    
    diff -r 20f35741c92d -r 4debcdcaf264 Win32OperatingSystem.st
    a b  
    1607316073
    1607416074remoteKeyOnHost:hostName
    1607516075    "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   
    1608016083%{
    1608116084    HKEY myKey, remoteKey = 0;
    1608216085    int _retVal;
    1608316086
    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)) {
    1608516110        myKey = (HKEY)__externalAddressVal(__INST(handle));
    16086         if ((_retVal = RegConnectRegistryA(__stringVal(hostName), myKey, &remoteKey)) == ERROR_SUCCESS) {
     16111        if ((_retVal = RegConnectRegistryW(__unicode16StringVal(hostNameUtf16Z), myKey, &remoteKey)) == ERROR_SUCCESS) {
    1608716112            remoteHandle = __MKEXTERNALADDRESS(remoteKey);
    1608816113        } else {
    1608916114            if ((_retVal != ERROR_PATH_NOT_FOUND)
     
    1610016125    ].
    1610116126    errorNumber notNil ifTrue:[
    1610216127        (OperatingSystem errorHolderForNumber:errorNumber) reportError.
    16103     ].
     16128    ]. 
     16129
    1610416130    ^ nil
    1610516131
    1610616132    "
     
    1613116157
    1613216158subKeyAtIndex:subKeyIndex
    1613316159    "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
    1613516163
    1613616164    |subKeyName subKeyClassName errorNumber|
    1613716165
    1613816166%{
    1613916167    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);
    1614416185    FILETIME modificationTime;
    1614516186    int _retVal;
    1614616187
    1614716188    if (__isExternalAddressLike(__INST(handle))
    1614816189     && __isSmallInteger(subKeyIndex)) {
    1614916190        myKey = (HKEY)__externalAddressVal(__INST(handle));
    16150         if ((_retVal = RegEnumKeyExA(myKey, __intVal(subKeyIndex),
     16191        if ((_retVal = RegEnumKeyExW(myKey, __intVal(subKeyIndex),
    1615116192                         nameBuffer, &nameSize,
    1615216193                         NULL,
    1615316194                         classNameBuffer, &classNameSize,
    1615416195                         &modificationTime)) == ERROR_SUCCESS) {
    1615516196            nameBuffer[nameSize] = '\0';
    1615616197            classNameBuffer[classNameSize] = '\0';
    16157             subKeyName = __MKSTRING(nameBuffer);
    16158             subKeyClassName = __MKSTRING(classNameBuffer);
     16198            subKeyName = __MKU16STRING(nameBuffer);
     16199            subKeyClassName = __MKU16STRING(classNameBuffer);
    1615916200        } else {
    1616016201            if ((_retVal != ERROR_PATH_NOT_FOUND)
    1616116202             && (_retVal != ERROR_FILE_NOT_FOUND)
     
    1618316224
    1618416225subKeyNameAndClassAtIndex:subKeyIndex
    1618516226    "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!!"
    1618716229
    1618816230    |subKeyName subKeyClassName errorNumber|
    1618916231
    1619016232%{
    1619116233    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
    1619616252    FILETIME modificationTime;
    1619716253    int _retVal;
    1619816254
    1619916255    if (__isExternalAddressLike(__INST(handle))
    1620016256     && __isSmallInteger(subKeyIndex)) {
    1620116257        myKey = (HKEY)__externalAddressVal(__INST(handle));
    16202         if ((_retVal = RegEnumKeyExA(myKey, __intVal(subKeyIndex),
     16258        if ((_retVal = RegEnumKeyExW(myKey, __intVal(subKeyIndex),
    1620316259                         nameBuffer, &nameSize,
    1620416260                         NULL,
    1620516261                         classNameBuffer, &classNameSize,
    1620616262                         &modificationTime)) == ERROR_SUCCESS) {
    1620716263            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);
    1621116267        } else {
    1621216268            if ((_retVal != ERROR_PATH_NOT_FOUND)
    1621316269             && (_retVal != ERROR_FILE_NOT_FOUND)
     
    1643216488
    1643316489valueNameAtIndex:valueIndex
    1643416490    "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!!"
    1643616493
    1643716494    |valueName errorNumber|
    1643816495
    1643916496%{
    1644016497    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);
    1644316515    DWORD valueType;
    1644416516    int _retVal;
    1644516517
    1644616518    if (__isExternalAddressLike(__INST(handle))
    1644716519     && __isSmallInteger(valueIndex)) {
    1644816520        myKey = (HKEY)__externalAddressVal(__INST(handle));
    16449         if ((_retVal = RegEnumValueA(myKey, __intVal(valueIndex),
     16521        if ((_retVal = RegEnumValueW(myKey, __intVal(valueIndex),
    1645016522                         nameBuffer, &nameSize,
    1645116523                         NULL,
    1645216524                         &valueType,
    1645316525                         NULL, NULL)) == ERROR_SUCCESS) {
    1645416526            nameBuffer[nameSize] = '\0';
    16455             valueName = __MKSTRING(nameBuffer);
     16527        valueName = __MKU16STRING(nameBuffer);
    1645616528        } else {
    1645716529            if ((_retVal != ERROR_PATH_NOT_FOUND)
    1645816530             && (_retVal != ERROR_FILE_NOT_FOUND)
     
    1647516547    "
    1647616548!
    1647716549
    16478 valueNamed:aValueName
     16550valueNamed:name
    1647916551    "retrieve a value; the returned object depends upon the type:
    1648016552        REG_BINARY      -> ByteArray
    1648116553        REG_SZ          -> String
     
    1648416556        REG_NONE        -> nil
    1648516557    "
    1648616558
    16487     |stringArray retVal errorNumber|
     16559    |nameUtf16Z stringArray retVal errorNumber|
     16560
     16561    "/ name must be a string
     16562    name isString ifFalse: [
     16563        Transcript showCR: 'The registry value name must be a String!!'.
     16564        ^ nil
     16565    ].
     16566
     16567    "/ adding terminating null into empty string
     16568    name notNil ifTrue:[
     16569        name isEmpty ifTrue:[nameUtf16Z := (name, (Character codePoint: 0)) asUnicode16String] "/ needed for defaultValue
     16570                    ifFalse:[nameUtf16Z := name asUnicode16StringZ]
     16571    ].
    1648816572
    1648916573%{  /* STACK: 20000 */
    1649016574    HKEY myKey;
    1649116575    DWORD valueType;
     16576    int val; 
    1649216577    union {
    16493         DWORD dWord;
    16494         unsigned char dWordBytes[4];
    16495         unsigned char smallDataBuffer[1024*16];
     16578        DWORD dWord;
     16579        unsigned char dWordBytes[4];   // needed for the shifts at REG_DWORD_LITTLE_ENDIAN and REG_DWORD_BIG_ENDIAN
     16580        ULONGLONG qWord;               // needed for the 64-bit int (QWORD)
     16581        wchar_t wstringBuffer[1024*8]; // buffer for wide characters
    1649616582    } quickData;
    16497     int val;
     16583
     16584    wchar_t *dataBuffer = NULL;
    1649816585    DWORD dataSize = sizeof(quickData);
    16499     unsigned char *dataBuffer = NULL;
    16500 #define xxUSE_UNICODE
    16501 #ifdef USE_UNICODE
    16502 # define xRegQueryValueEx  RegQueryValueExW
    16503 # define CHAR             short
    16504 #else
    16505 # define RegQueryValueEx  RegQueryValueExA
    16506 # define CHAR             char
    16507 #endif
    1650816586
    1650916587    if (__isExternalAddressLike(__INST(handle))
    16510      && __isStringLike(aValueName)) {
     16588     && __isUnicode16String(nameUtf16Z)) {
    1651116589        int ret;
    1651216590
    1651316591        myKey = (HKEY)__externalAddressVal(__INST(handle));
    1651416592
    16515         /*
    16516          * try to get it with one call ...
    16517          */
    16518         ret = RegQueryValueExA(myKey, __stringVal(aValueName),
     16593  /* Reading values from registry
     16594   *
     16595   * LSTATUS RegQueryValueEx(  // ends with either A(ascii) or W (unicode)
     16596   *   HKEY    hKey,         // a handle of a registry key
     16597   *   LPCWSTR lpValueName,  // The name of the registry value
     16598   *   LPDWORD lpReserved,   // reserved and must be NULL
     16599   *   LPDWORD lpType,       // A pointer to a variable that recieves a code indicating the type of data stored in the specified value
     16600   *   LPBYTE  lpData,       // A pointer to a buffer that receives the value's data.  This parameter can be NULL if datat is not required
     16601   *   LPDWORD lpcbData      // A pointer to a variable that specifies the size of the buffer pointed to by the IpData parameter, in bytes.
     16602   *                          // When the furnction returns, this variable contains the size  of the data copied to IpData
     16603   * );
     16604  */
     16605
     16606#if 0
     16607   console_printf("================================================\n");
     16608   console_printf("Before - myKey: %p\n", &myKey);
     16609   console_printf("Before - aValueName: %S\n", __unicode16StringVal(nameUtf16Z));
     16610   console_printf("Before - valueType: %p, valueTypeData: %lu\n", &valueType, valueType);
     16611   console_printf("Before - QuickData dWord: %lu\n", quickData.dWord);
     16612   console_printf("Before - QuickData dWordBytes: %S\n", quickData.dWordBytes);
     16613   console_printf("Before - QuickData qWord: %lu\n", quickData.qWord);
     16614   console_printf("Before - QuickData smallDataBuffer: %S\n", quickData.smallDataBuffer);
     16615   console_printf("Before - Databuffer: %p, dataBufferData: %s\n", &dataBuffer, dataBuffer);
     16616   console_printf("Before - dataSize: %p, dataSizeData: %lu\n", &dataSize, dataSize);
     16617   console_printf("================================================\n");
     16618#endif
     16619   
     16620    /*
     16621     * try to get it with one call ...
     16622     */
     16623    ret = RegQueryValueExW(myKey, __unicode16StringVal(nameUtf16Z),
    1651916624                         NULL,
    1652016625                         &valueType,
    16521                          (char *)&quickData,
     16626             (LPBYTE) &quickData.wstringBuffer, // LPBYTE aka (unsigned char *)
    1652216627                         &dataSize);
     16628
     16629#if 0   
     16630   console_printf("------------------------------------------------\n");
     16631   console_printf("After - myKey: %p\n", &myKey);
     16632   console_printf("After - aValueName: %S\n", __unicode16StringVal(nameUtf16Z));
     16633   console_printf("After - valueType: %p, valueTypeData: %lu\n", &valueType, valueType);
     16634   console_printf("After - QuickData dWord: %lu\n", quickData.dWord);
     16635   console_printf("After - QuickData dWordBytes: %S\n", quickData.dWordBytes);
     16636   console_printf("After - QuickData qWord: %lu\n", quickData.qWord);
     16637   console_printf("After - QuickData smallDataBuffer: %S\n", quickData.smallDataBuffer);
     16638   console_printf("After - Databuffer: %p, dataBufferData: %s\n", &dataBuffer, dataBuffer);
     16639   console_printf("After - dataSize: %p, dataSizeData: %lu\n", &dataSize, dataSize);
     16640   console_printf("------------------------------------------------\n");
     16641#endif   
     16642
    1652316643#if 0
    16524         console_printf("get \"%s\": dataSize=%d ret=%d\n", __stringVal(aValueName), dataSize, ret);
     16644    console_printf("Registry key: %p\n", (void *) myKey);
     16645        console_printf("get \"%S\": dataSize=%d ret=%d\n", __unicode16StringVal(nameUtf16Z), dataSize, ret);
    1652516646#endif
    1652616647        while (ret == ERROR_MORE_DATA) {
    1652716648#if 0
    16528             console_printf("ERROR_MORE_DATA dataSize=%d valueType=%d\n", dataSize, valueType);
     16649        console_printf("ERROR_MORE_DATA dataSize=%d valueType=%d\n", dataSize, valueType);
    1652916650#endif
    1653016651            /*
    1653116652             * nope - need another one ...
    1653216653             */
    16533             if (myKey = HKEY_PERFORMANCE_DATA) {
     16654            if (myKey == HKEY_PERFORMANCE_DATA) {
    1653416655                dataSize = dataSize * 2;
    1653516656            }
    1653616657            switch (valueType) {
    1653716658                case REG_BINARY:
    1653816659                case REG_MULTI_SZ:
    16539                     dataBuffer = malloc(dataSize);;
     16660                    dataBuffer = malloc(dataSize);
    1654016661                    break;
    1654116662                case REG_SZ:
    1654216663                    dataBuffer = malloc(dataSize);
     
    1654616667                    break;
    1654716668            }
    1654816669            if (dataBuffer) {
    16549                 ret = RegQueryValueEx(myKey, __stringVal(aValueName),
     16670                ret = RegQueryValueExW(myKey, __unicode16StringVal(nameUtf16Z),
    1655016671                                 NULL,
    1655116672                                 &valueType,
    16552                                  dataBuffer,
     16673                                 (LPBYTE) dataBuffer,
    1655316674                                 &dataSize);
    1655416675            } else {
    1655516676                break;
     
    1656916690                    retVal = nil;
    1657016691                    break;
    1657116692
    16572                 case REG_BINARY:
    16573                     retVal = __MKBYTEARRAY(dataBuffer ? dataBuffer : quickData.smallDataBuffer, dataSize);
     16693                case REG_BINARY:     
     16694                    retVal = __MKBYTEARRAY(dataBuffer ? dataBuffer : quickData.wstringBuffer, dataSize);     
    1657416695                    break;
    1657516696
    1657616697                case REG_SZ:
    1657716698                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
     16699
     16700#if 0
     16701            console_printf("dataBuffer before __MKU16STTRING: %S\n", dataBuffer);
     16702            console_printf("QuickData smallDataBuffer before __MKU16STTRING: %S\n", quickData.wstringBuffer);
     16703#endif           
     16704                    retVal = __MKU16STRING(dataBuffer ? dataBuffer : quickData.wstringBuffer);
     16705#if 0       
     16706            console_printf("QuickData smallDataBuffer: %S\n", quickData.wstringBuffer);
     16707            console_printf("dataBuffer: %S\n", dataBuffer);
     16708#endif           
     16709
    1658316710                    break;
    1658416711
    1658516712#if 0
     
    1660316730                    val = (val << 8) | quickData.dWordBytes[3];
    1660416731                    retVal = __MKUINT(val);
    1660516732                    break;
    16606 
     16733       
     16734        case REG_QWORD: // only REG_QWORD_LITTLE_ENDIAN present (no need for shifts)
     16735            retVal = __MKULARGEINT(quickData.qWord);
     16736            break;
     16737           
    1660716738                case REG_MULTI_SZ:
    1660816739                    {
    16609                         CHAR *cp, *cp0;
     16740                        wchar_t *cp, *cp0;
    1661016741                        int ns, i;
    16611 
    16612                         cp0 = dataBuffer ? dataBuffer : quickData.smallDataBuffer;
     16742 
     16743            cp0 = dataBuffer ? dataBuffer : quickData.wstringBuffer;
     16744
    1661316745#if 0
     16746
    1661416747                        console_printf("**************\n");
    1661516748                        for (i=0;i<50;i++) {
    1661616749                          console_printf("%x ", cp0[i]);
     
    1662716760#endif
    1662816761                        cp = cp0;
    1662916762                        ns = 0;
    16630                         while (*cp) {
    16631                             while (*cp++) ;;
    16632                             ns++;
     16763
     16764            // datasize
     16765            while ((((cp - cp0) * sizeof(wchar_t)) < (dataSize - 1)) // limits the string
     16766             && *cp) { // check if the dereferenced value is NULL
     16767                cp = wcschr(cp, '\0'); // wide string search for terminating null character
     16768                *cp++;
     16769                ns++;
    1663316770                        }
     16771
     16772            // clear any remaining value
     16773            *cp = '\0';
    1663416774                        stringArray = __ARRAY_NEW_INT(ns);
    1663516775
    1663616776                        i = 0;
    1663716777                        while (*cp0) {
    1663816778                            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;
     16779   
     16780                cp = cp0;
     16781                    s = __MKU16STRING(cp0); __ArrayInstPtr(stringArray)->a_element[i] = s; __STORE(stringArray, s);
     16782                cp = wcschr(cp, '\0');
     16783                *cp++;
     16784                cp0 = cp;
    1664916785                            i++;
     16786               
    1665016787                        }
    1665116788                        retVal = stringArray;
    16652                         break;
    16653                     }
     16789                        break;             
     16790                    }       
    1665416791                default:
    1665516792                    console_printf("RegistryEntry [warning]: unhandled valueType: %d\n", valueType);
    1665616793                    break;
     
    1666016797             && (ret != ERROR_FILE_NOT_FOUND)) {
    1666116798                errorNumber = __MKSMALLINT(ret);
    1666216799            }
    16663         }
     16800        } 
    1666416801    }
    1666516802    if (dataBuffer) free(dataBuffer);
     16803
    1666616804%}.
    1666716805    errorNumber notNil ifTrue:[
    1666816806        (OperatingSystem errorHolderForNumber:errorNumber) reportError.
     
    1668916827
    1669016828    |data stringArray errorNumber|
    1669116829%{
     16830    /* Registry Element Size Limits
     16831     *
     16832     * Found at MSDN: https://docs.microsoft.com/en-us/windows/desktop/SysInfo/registry-element-size-limits
     16833     *
     16834     * Registry Element | Size Limit
     16835     * 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.
     16836     * Value name         16,383 characters; Windows 2000: 260 ANSI characters or 16,383 Unicode characters.
     16837     * Value              Available memory (latest format)1 MB (standard format)
     16838     * 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.
     16839     *
     16840     * Notes:
     16841     * - 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.
     16842     * This helps the registry perform efficiently.
     16843     *
     16844     * - 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
     16845     * another backslash as an escape character. For example, specify "C:\\mydir\\myfile" to store the string "C:\mydir\myfile".
     16846     * 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,
     16847     * which are not allowed in key names.
     16848     */
     16849
    1669216850    HKEY myKey;
    1669316851    DWORD valueType = -1;
    1669416852    int val;
     
    1691817076    "evaluate aBlock for all value names"
    1691917077
    1692017078    ^ Array streamContents:[:s |
    16921         self valueNamesDo:[:nm | s nextPut:nm]
     17079        self valueNamesDo:[:nm | s nextPutUnicode:nm]
    1692217080    ].
    1692317081
    1692417082    "Created: / 18-01-2011 / 20:24:52 / cg"