--- a/Win32OperatingSystem.st Tue Nov 13 15:58:15 2018 +0100
+++ b/Win32OperatingSystem.st Wed Dec 05 13:03:24 2018 +0100
@@ -16073,17 +16073,42 @@
remoteKeyOnHost:hostName
"return the corresponding registry entry from
- a remote computers registry."
-
- |newEntry remoteHandle errorNumber|
-
+ a remote computers registry.
+ Note: The registry key must be form a predefined list defined by Microsoft."
+
+ |hostNameUtf16Z newEntry remoteHandle errorNumber|
+
+ hostNameUtf16Z := hostName notEmptyOrNil ifTrue:[hostName asUnicode16StringZ].
+
%{
HKEY myKey, remoteKey = 0;
int _retVal;
- if (__isExternalAddressLike(__INST(handle)) && __isStringLike(hostName)) {
+/* Notes from MSDN:
+ * link: https://docs.microsoft.com/en-us/windows/desktop/api/winreg/nf-winreg-regconnectregistryw
+ *
+ * 1) RegConnectRegistry requires the Remote Registry service to be running on the remote computer.
+ * By default, this service is configured to be started manually. To configure the Remote Registry
+ * service to start automatically, run Services.msc and change the Startup Type of the service to Automatic.
+ *
+ * 2) If the computer is joined to a workgroup and the "Force network logons using local accounts to authenticate as
+ * Guest" policy is enabled, the function fails. Note that this policy is enabled by default if the computer is joined
+ * to a workgroup.
+ *
+ * 3) If the current user does not have proper access to the remote computer, the call to RegConnectRegistry fails. To connect
+ * to a remote registry, call LogonUser with LOGON32_LOGON_NEW_CREDENTIALS and ImpersonateLoggedOnUser before calling
+ * RegConnectRegistry.
+ *
+ * 4) myKey must be a predefined registry handle.
+ * more at: https://docs.microsoft.com/en-us/windows/desktop/SysInfo/predefined-keys
+ *
+ * 5) When a handle returned by RegConnectRegistry is no longer needed, it should be closed by calling RegCloseKey. (done by registerForFinaliation)
+ *
+ */
+
+ if (__isExternalAddressLike(__INST(handle)) && __isUnicode16String(hostNameUtf16Z)) {
myKey = (HKEY)__externalAddressVal(__INST(handle));
- if ((_retVal = RegConnectRegistryA(__stringVal(hostName), myKey, &remoteKey)) == ERROR_SUCCESS) {
+ if ((_retVal = RegConnectRegistryW(__unicode16StringVal(hostNameUtf16Z), myKey, &remoteKey)) == ERROR_SUCCESS) {
remoteHandle = __MKEXTERNALADDRESS(remoteKey);
} else {
if ((_retVal != ERROR_PATH_NOT_FOUND)
@@ -16100,7 +16125,8 @@
].
errorNumber notNil ifTrue:[
(OperatingSystem errorHolderForNumber:errorNumber) reportError.
- ].
+ ].
+
^ nil
"
@@ -16131,31 +16157,46 @@
subKeyAtIndex:subKeyIndex
"return a new registry entry below mySelf for the given subKey index.
- Return nil if no such key exists"
+ Return nil if no such key exists
+ WARNING: subKeyIndex is 0-based!!"
+
|subKeyName subKeyClassName errorNumber|
%{
HKEY myKey, subKey = 0;
- char nameBuffer[256];
- DWORD nameSize = sizeof(nameBuffer) - 1;
- char classNameBuffer[256];
- DWORD classNameSize = sizeof(classNameBuffer) - 1;
+
+/* lpName (nameBuffer):
+ * A pointer to a buffer that receives the name of the subkey, including the
+ * terminating null character. The function copies only the name of the subkey,
+ * not the full key hierarchy, to the buffer. If the function fails, no information is copied to this buffer.
+ *
+ * note: This actually means that the if the path fits within 255 chacaters you could get another 255 characters for the key itself.
+ * This could help if you are having issues with the registry path lenght.
+ */
+ wchar_t nameBuffer[256]; // 256 is due to Key name limit (including path)
+ DWORD nameSize = sizeof(nameBuffer);
+
+ /* lpClass (classNameBuffer):
+ * A pointer to a buffer that receives the user-defined class of the enumerated subkey.
+ */
+ wchar_t classNameBuffer[256];
+ DWORD classNameSize = sizeof(classNameBuffer);
FILETIME modificationTime;
int _retVal;
if (__isExternalAddressLike(__INST(handle))
&& __isSmallInteger(subKeyIndex)) {
myKey = (HKEY)__externalAddressVal(__INST(handle));
- if ((_retVal = RegEnumKeyExA(myKey, __intVal(subKeyIndex),
+ if ((_retVal = RegEnumKeyExW(myKey, __intVal(subKeyIndex),
nameBuffer, &nameSize,
NULL,
classNameBuffer, &classNameSize,
&modificationTime)) == ERROR_SUCCESS) {
nameBuffer[nameSize] = '\0';
classNameBuffer[classNameSize] = '\0';
- subKeyName = __MKSTRING(nameBuffer);
- subKeyClassName = __MKSTRING(classNameBuffer);
+ subKeyName = __MKU16STRING(nameBuffer);
+ subKeyClassName = __MKU16STRING(classNameBuffer);
} else {
if ((_retVal != ERROR_PATH_NOT_FOUND)
&& (_retVal != ERROR_FILE_NOT_FOUND)
@@ -16183,31 +16224,46 @@
subKeyNameAndClassAtIndex:subKeyIndex
"return the name and className of the given subKey at index as a pair.
- Return nil if no such key exists"
+ Return nil if no such key exists
+ WARNING: subKeyIndex is 0-based!!"
|subKeyName subKeyClassName errorNumber|
%{
HKEY myKey, subKey = 0;
- char nameBuffer[256];
- DWORD nameSize = sizeof(nameBuffer) - 1;
- char classNameBuffer[256];
- DWORD classNameSize = sizeof(classNameBuffer) - 1;
+
+/* lpName (nameBuffer):
+ * A pointer to a buffer that receives the name of the subkey, including the
+ * terminating null character. The function copies only the name of the subkey,
+ * not the full key hierarchy, to the buffer. If the function fails, no information is copied to this buffer.
+ *
+ * note: This actually means that the if the path fits within 255 chacaters you could get another 255 characters for the key itself.
+ * This could help if you are having issues with the registry path lenght.
+ */
+ wchar_t nameBuffer[256]; // limiting the nameBuffer to 255 characters + null terminator
+ DWORD nameSize = sizeof(nameBuffer);
+
+ /* lpClass (classNameBuffer):
+ * A pointer to a buffer that receives the user-defined class of the enumerated subkey.
+ */
+ wchar_t classNameBuffer[256]; // limiting the classNameBuffer to 255 characters + nul terminator
+ DWORD classNameSize = sizeof(classNameBuffer);
+
FILETIME modificationTime;
int _retVal;
if (__isExternalAddressLike(__INST(handle))
&& __isSmallInteger(subKeyIndex)) {
myKey = (HKEY)__externalAddressVal(__INST(handle));
- if ((_retVal = RegEnumKeyExA(myKey, __intVal(subKeyIndex),
+ if ((_retVal = RegEnumKeyExW(myKey, __intVal(subKeyIndex),
nameBuffer, &nameSize,
NULL,
classNameBuffer, &classNameSize,
&modificationTime)) == ERROR_SUCCESS) {
nameBuffer[nameSize] = '\0';
- classNameBuffer[classNameSize] = '\0';
- subKeyName = __MKSTRING(nameBuffer);
- subKeyClassName = __MKSTRING(classNameBuffer);
+ classNameBuffer[classNameSize] = '\0';
+ subKeyName = __MKU16STRING(nameBuffer);
+ subKeyClassName = __MKU16STRING(classNameBuffer);
} else {
if ((_retVal != ERROR_PATH_NOT_FOUND)
&& (_retVal != ERROR_FILE_NOT_FOUND)
@@ -16432,27 +16488,43 @@
valueNameAtIndex:valueIndex
"return a values name for the given value index.
- Return nil if no such value exists"
+ Return nil if no such value exists
+ WARNING: valueIndex is 0-based!!"
|valueName errorNumber|
%{
HKEY myKey;
- char nameBuffer[256];
- DWORD nameSize = sizeof(nameBuffer) - 1;
+
+/* nameBuffer (lpValueName in RegEnumValue function):
+ * A pointer to a buffer that receives the name of the value as a null-terminated string.
+ * This buffer must be large enough to include the terminating null character.
+ * For more information, see Registry Element Size Limits.
+ */
+ wchar_t nameBuffer[256]; // 256 is due to Key name limit (including path) + the null character
+
+/* nameSize (lpcchValueName in RegEnumValue function):
+ *
+ * A pointer to a variable that specifies the size of the buffer pointed to by the lpValueName parameter, in characters.
+ * When the function returns, the variable receives the number of characters stored in the buffer, not including the terminating null character.
+ *
+ * Registry value names are limited to 32,767 bytes. The ANSI version of this function treats this parameter as a SHORT value.
+ * Therefore, if you specify a value greater than 32,767 bytes, there is an overflow and the function may return ERROR_MORE_DATA.
+ */
+ DWORD nameSize = sizeof(nameBuffer);
DWORD valueType;
int _retVal;
if (__isExternalAddressLike(__INST(handle))
&& __isSmallInteger(valueIndex)) {
myKey = (HKEY)__externalAddressVal(__INST(handle));
- if ((_retVal = RegEnumValueA(myKey, __intVal(valueIndex),
+ if ((_retVal = RegEnumValueW(myKey, __intVal(valueIndex),
nameBuffer, &nameSize,
NULL,
&valueType,
NULL, NULL)) == ERROR_SUCCESS) {
nameBuffer[nameSize] = '\0';
- valueName = __MKSTRING(nameBuffer);
+ valueName = __MKU16STRING(nameBuffer);
} else {
if ((_retVal != ERROR_PATH_NOT_FOUND)
&& (_retVal != ERROR_FILE_NOT_FOUND)
@@ -16475,7 +16547,7 @@
"
!
-valueNamed:aValueName
+valueNamed:name
"retrieve a value; the returned object depends upon the type:
REG_BINARY -> ByteArray
REG_SZ -> String
@@ -16484,59 +16556,108 @@
REG_NONE -> nil
"
- |stringArray retVal errorNumber|
+ |nameUtf16Z stringArray retVal errorNumber|
+
+ "/ name must be a string
+ name isString ifFalse: [
+ Transcript showCR: 'The registry value name must be a String!!'.
+ ^ nil
+ ].
+
+ "/ adding terminating null into empty string
+ name notNil ifTrue:[
+ name isEmpty ifTrue:[nameUtf16Z := (name, (Character codePoint: 0)) asUnicode16String] "/ needed for defaultValue
+ ifFalse:[nameUtf16Z := name asUnicode16StringZ]
+ ].
%{ /* STACK: 20000 */
HKEY myKey;
DWORD valueType;
+ int val;
union {
- DWORD dWord;
- unsigned char dWordBytes[4];
- unsigned char smallDataBuffer[1024*16];
+ DWORD dWord;
+ unsigned char dWordBytes[4]; // needed for the shifts at REG_DWORD_LITTLE_ENDIAN and REG_DWORD_BIG_ENDIAN
+ ULONGLONG qWord; // needed for the 64-bit int (QWORD)
+ wchar_t wstringBuffer[1024*8]; // buffer for wide characters
} quickData;
- int val;
+
+ wchar_t *dataBuffer = NULL;
DWORD dataSize = sizeof(quickData);
- unsigned char *dataBuffer = NULL;
-#define xxUSE_UNICODE
-#ifdef USE_UNICODE
-# define xRegQueryValueEx RegQueryValueExW
-# define CHAR short
-#else
-# define RegQueryValueEx RegQueryValueExA
-# define CHAR char
-#endif
if (__isExternalAddressLike(__INST(handle))
- && __isStringLike(aValueName)) {
+ && __isUnicode16String(nameUtf16Z)) {
int ret;
myKey = (HKEY)__externalAddressVal(__INST(handle));
- /*
- * try to get it with one call ...
- */
- ret = RegQueryValueExA(myKey, __stringVal(aValueName),
+ /* Reading values from registry
+ *
+ * LSTATUS RegQueryValueEx( // ends with either A(ascii) or W (unicode)
+ * HKEY hKey, // a handle of a registry key
+ * LPCWSTR lpValueName, // The name of the registry value
+ * LPDWORD lpReserved, // reserved and must be NULL
+ * LPDWORD lpType, // A pointer to a variable that recieves a code indicating the type of data stored in the specified value
+ * LPBYTE lpData, // A pointer to a buffer that receives the value's data. This parameter can be NULL if datat is not required
+ * LPDWORD lpcbData // A pointer to a variable that specifies the size of the buffer pointed to by the IpData parameter, in bytes.
+ * // When the furnction returns, this variable contains the size of the data copied to IpData
+ * );
+ */
+
+#if 0
+ console_printf("================================================\n");
+ console_printf("Before - myKey: %p\n", &myKey);
+ console_printf("Before - aValueName: %S\n", __unicode16StringVal(nameUtf16Z));
+ console_printf("Before - valueType: %p, valueTypeData: %lu\n", &valueType, valueType);
+ console_printf("Before - QuickData dWord: %lu\n", quickData.dWord);
+ console_printf("Before - QuickData dWordBytes: %S\n", quickData.dWordBytes);
+ console_printf("Before - QuickData qWord: %lu\n", quickData.qWord);
+ console_printf("Before - QuickData smallDataBuffer: %S\n", quickData.smallDataBuffer);
+ console_printf("Before - Databuffer: %p, dataBufferData: %s\n", &dataBuffer, dataBuffer);
+ console_printf("Before - dataSize: %p, dataSizeData: %lu\n", &dataSize, dataSize);
+ console_printf("================================================\n");
+#endif
+
+ /*
+ * try to get it with one call ...
+ */
+ ret = RegQueryValueExW(myKey, __unicode16StringVal(nameUtf16Z),
NULL,
&valueType,
- (char *)&quickData,
+ (LPBYTE) &quickData.wstringBuffer, // LPBYTE aka (unsigned char *)
&dataSize);
+
+#if 0
+ console_printf("------------------------------------------------\n");
+ console_printf("After - myKey: %p\n", &myKey);
+ console_printf("After - aValueName: %S\n", __unicode16StringVal(nameUtf16Z));
+ console_printf("After - valueType: %p, valueTypeData: %lu\n", &valueType, valueType);
+ console_printf("After - QuickData dWord: %lu\n", quickData.dWord);
+ console_printf("After - QuickData dWordBytes: %S\n", quickData.dWordBytes);
+ console_printf("After - QuickData qWord: %lu\n", quickData.qWord);
+ console_printf("After - QuickData smallDataBuffer: %S\n", quickData.smallDataBuffer);
+ console_printf("After - Databuffer: %p, dataBufferData: %s\n", &dataBuffer, dataBuffer);
+ console_printf("After - dataSize: %p, dataSizeData: %lu\n", &dataSize, dataSize);
+ console_printf("------------------------------------------------\n");
+#endif
+
#if 0
- console_printf("get \"%s\": dataSize=%d ret=%d\n", __stringVal(aValueName), dataSize, ret);
+ console_printf("Registry key: %p\n", (void *) myKey);
+ console_printf("get \"%S\": dataSize=%d ret=%d\n", __unicode16StringVal(nameUtf16Z), dataSize, ret);
#endif
while (ret == ERROR_MORE_DATA) {
#if 0
- console_printf("ERROR_MORE_DATA dataSize=%d valueType=%d\n", dataSize, valueType);
+ console_printf("ERROR_MORE_DATA dataSize=%d valueType=%d\n", dataSize, valueType);
#endif
/*
* nope - need another one ...
*/
- if (myKey = HKEY_PERFORMANCE_DATA) {
+ if (myKey == HKEY_PERFORMANCE_DATA) {
dataSize = dataSize * 2;
}
switch (valueType) {
case REG_BINARY:
case REG_MULTI_SZ:
- dataBuffer = malloc(dataSize);;
+ dataBuffer = malloc(dataSize);
break;
case REG_SZ:
dataBuffer = malloc(dataSize);
@@ -16546,10 +16667,10 @@
break;
}
if (dataBuffer) {
- ret = RegQueryValueEx(myKey, __stringVal(aValueName),
+ ret = RegQueryValueExW(myKey, __unicode16StringVal(nameUtf16Z),
NULL,
&valueType,
- dataBuffer,
+ (LPBYTE) dataBuffer,
&dataSize);
} else {
break;
@@ -16569,17 +16690,23 @@
retVal = nil;
break;
- case REG_BINARY:
- retVal = __MKBYTEARRAY(dataBuffer ? dataBuffer : quickData.smallDataBuffer, dataSize);
+ case REG_BINARY:
+ retVal = __MKBYTEARRAY(dataBuffer ? dataBuffer : quickData.wstringBuffer, dataSize);
break;
case REG_SZ:
case REG_EXPAND_SZ:
-#ifdef USE_UNICODE
- retVal = __MKU16STRING(dataBuffer ? dataBuffer : quickData.smallDataBuffer);
-#else
- retVal = __MKSTRING(dataBuffer ? dataBuffer : quickData.smallDataBuffer);
-#endif
+
+#if 0
+ console_printf("dataBuffer before __MKU16STTRING: %S\n", dataBuffer);
+ console_printf("QuickData smallDataBuffer before __MKU16STTRING: %S\n", quickData.wstringBuffer);
+#endif
+ retVal = __MKU16STRING(dataBuffer ? dataBuffer : quickData.wstringBuffer);
+#if 0
+ console_printf("QuickData smallDataBuffer: %S\n", quickData.wstringBuffer);
+ console_printf("dataBuffer: %S\n", dataBuffer);
+#endif
+
break;
#if 0
@@ -16603,14 +16730,20 @@
val = (val << 8) | quickData.dWordBytes[3];
retVal = __MKUINT(val);
break;
-
+
+ case REG_QWORD: // only REG_QWORD_LITTLE_ENDIAN present (no need for shifts)
+ retVal = __MKULARGEINT(quickData.qWord);
+ break;
+
case REG_MULTI_SZ:
{
- CHAR *cp, *cp0;
+ wchar_t *cp, *cp0;
int ns, i;
-
- cp0 = dataBuffer ? dataBuffer : quickData.smallDataBuffer;
+
+ cp0 = dataBuffer ? dataBuffer : quickData.wstringBuffer;
+
#if 0
+
console_printf("**************\n");
for (i=0;i<50;i++) {
console_printf("%x ", cp0[i]);
@@ -16627,30 +16760,34 @@
#endif
cp = cp0;
ns = 0;
- while (*cp) {
- while (*cp++) ;;
- ns++;
+
+ // datasize
+ while ((((cp - cp0) * sizeof(wchar_t)) < (dataSize - 1)) // limits the string
+ && *cp) { // check if the dereferenced value is NULL
+ cp = wcschr(cp, '\0'); // wide string search for terminating null character
+ *cp++;
+ ns++;
}
+
+ // clear any remaining value
+ *cp = '\0';
stringArray = __ARRAY_NEW_INT(ns);
i = 0;
while (*cp0) {
OBJ s;
- CHAR *cp;
-
- cp = cp0;
- while (*cp++) ;;
-#ifdef USE_UNICODE
- s = __MKU16STRING(cp0); __ArrayInstPtr(stringArray)->a_element[i] = s; __STORE(stringArray, s);
-#else
- s = __MKSTRING(cp0); __ArrayInstPtr(stringArray)->a_element[i] = s; __STORE(stringArray, s);
-#endif
- cp0 = cp;
+
+ cp = cp0;
+ s = __MKU16STRING(cp0); __ArrayInstPtr(stringArray)->a_element[i] = s; __STORE(stringArray, s);
+ cp = wcschr(cp, '\0');
+ *cp++;
+ cp0 = cp;
i++;
+
}
retVal = stringArray;
- break;
- }
+ break;
+ }
default:
console_printf("RegistryEntry [warning]: unhandled valueType: %d\n", valueType);
break;
@@ -16660,9 +16797,10 @@
&& (ret != ERROR_FILE_NOT_FOUND)) {
errorNumber = __MKSMALLINT(ret);
}
- }
+ }
}
if (dataBuffer) free(dataBuffer);
+
%}.
errorNumber notNil ifTrue:[
(OperatingSystem errorHolderForNumber:errorNumber) reportError.
@@ -16689,6 +16827,26 @@
|data stringArray errorNumber|
%{
+ /* Registry Element Size Limits
+ *
+ * Found at MSDN: https://docs.microsoft.com/en-us/windows/desktop/SysInfo/registry-element-size-limits
+ *
+ * Registry Element | Size Limit
+ * 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.
+ * Value name 16,383 characters; Windows 2000: 260 ANSI characters or 16,383 Unicode characters.
+ * Value Available memory (latest format)1 MB (standard format)
+ * 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.
+ *
+ * Notes:
+ * - 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.
+ * This helps the registry perform efficiently.
+ *
+ * - 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
+ * another backslash as an escape character. For example, specify "C:\\mydir\\myfile" to store the string "C:\mydir\myfile".
+ * 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,
+ * which are not allowed in key names.
+ */
+
HKEY myKey;
DWORD valueType = -1;
int val;
@@ -16918,7 +17076,7 @@
"evaluate aBlock for all value names"
^ Array streamContents:[:s |
- self valueNamesDo:[:nm | s nextPut:nm]
+ self valueNamesDo:[:nm | s nextPutUnicode:nm]
].
"Created: / 18-01-2011 / 20:24:52 / cg"