Win32OperatingSystem.st
branchjv
changeset 24093 0f94f6c8c9d4
parent 23767 c0e32a3e2ca0
child 25434 74d82599196a
--- a/Win32OperatingSystem.st	Wed Mar 27 13:42:44 2019 +0100
+++ b/Win32OperatingSystem.st	Tue Apr 09 11:34:04 2019 +0200
@@ -360,6 +360,11 @@
 #  include <windowsx.h>
 # endif
 
+# ifndef WINTERNL_H_INCLUDED
+#  define WINTERNL_H_INCLUDED
+#  include <winternl.h> /* required for UNICODE_STRING */
+# endif
+
 # define _WIN32_DCOM     /*required for CoInitializeEx*/
 # include <objbase.h>    //for COM
 
@@ -370,7 +375,7 @@
 #  define _STRING_H_INCLUDED_
 #  include <string.h>
 # endif
-
+    
 # ifdef __DEF_Array
 #  define Array __DEF_Array
 # endif
@@ -16179,7 +16184,7 @@
 	myKey = (HKEY)__externalAddressVal(__INST(handle));
 	if (RegDeleteKeyExWPtr) {
 	    _retVal = RegDeleteKeyExWPtr(myKey,
-		                      (LPCTSTR) __unicode16StringVal(subKeyStringUtf16Z),
+		                      (LPCTSTR)__unicode16StringVal(subKeyStringUtf16Z),
 		                      _flags,
 		                      0); // reserved	
 	} else {
@@ -16817,10 +16822,10 @@
 valueNamed:nameString
     "retrieve a value; the returned object depends upon the type:
     REG_NONE        -> nil
-	REG_BINARY      -> ByteArray
-	REG_SZ          -> String
-	REG_DWORD       -> Integer
-	REG_QWORD       -> 64 bit Integer
+    REG_BINARY      -> ByteArray
+    REG_SZ          -> String
+    REG_DWORD       -> Integer
+    REG_QWORD       -> 64 bit Integer
     REG_MULTI_SZ    -> Array of strings
     "
 
@@ -17570,6 +17575,177 @@
     "Created: / 19.5.1999 / 21:45:05 / cg"
 ! !
 
+!Win32OperatingSystem::RegistryEntry methodsFor:'renaming'!
+
+renameSubKey:oldNameString to:newNameString
+"There is difficult choice when it comes to renaming registry subKey(s).  You can either use undocumented
+ RegRenameKey or NtRenameKey which could be removed in the future.  The code now uses the RegRenameKey 
+ as first choice with NtRenameKey as a fallback."
+
+    |oldNameUtf16Z newNameUtf16Z errorNumber|
+    "/ checking user input
+    oldNameString isString ifFalse:[
+        self error: 'oldNameString is not a string!!'
+    ].
+    newNameString isString ifFalse:[
+        self error: 'newNameString is not a string!!'
+    ].
+    oldNameUtf16Z := oldNameString asUnicode16StringZ.
+    newNameUtf16Z := newNameString asUnicode16StringZ.
+
+%{
+    static int initialized = 0;
+    static LONG (WINAPI *RegRenameKeyPtr)(HKEY hKey, LPCTSTR lpSubKeyName, LPCTSTR lpNewKeyName) = NULL;
+    static NTSTATUS (WINAPI *NtRenameKeyPtr)(HANDLE keyHandle, PUNICODE_STRING newName) = NULL;
+    static NTSTATUS (WINAPI *RtlInitUnicodeStringExPtr)(PUNICODE_STRING destination, PCWSTR source) = NULL;
+    static ULONG (WINAPI *RtlNtStatusToDosErrorPtr)(NTSTATUS Status) = NULL;
+
+    if (!initialized) {
+        initialized = 1;
+        HMODULE advapi32 = LoadLibrary("advapi32.dll");
+        if (!advapi32) {
+            /* 
+             * failed to load advapi32.dll
+             */
+            errorNumber = __MKSMALLINT(GetLastError());
+            goto loadError;
+        }
+        RegRenameKeyPtr = (LONG (*)(HKEY hKey, LPCTSTR lpSubKeyName, LPCTSTR lpNewKeyName)) GetProcAddress(
+            advapi32, "RegRenameKey");
+        if (!RegRenameKeyPtr) { // fallback for WindowsXP, Windows 2000 or in case the undocumented RegRenameKey fails
+            HMODULE ntdll = LoadLibrary("ntdll.dll");
+            if (!ntdll) {
+                /* 
+                 * failed to load ntdll.dll
+                 */
+                errorNumber = __MKSMALLINT(GetLastError());
+                goto loadError;
+            }
+            NtRenameKeyPtr = (NTSTATUS (*)(HANDLE keyHandle, PUNICODE_STRING newName)) GetProcAddress(
+                ntdll, "NtRenameKey");
+            RtlInitUnicodeStringExPtr = (NTSTATUS (*)(PUNICODE_STRING destination, PCWSTR source)) GetProcAddress(
+                ntdll, "RtlInitUnicodeStringEx");
+            RtlNtStatusToDosErrorPtr = (ULONG (*)(NTSTATUS Status)) GetProcAddress(ntdll, "RtlNtStatusToDosError");    
+        }
+    }
+
+    HKEY myKey, oldSubKey = 0;
+    int _retVal;
+
+    if (__isExternalAddressLike(__INST(handle))
+     && __isUnicode16String(oldNameUtf16Z)
+     && __isUnicode16String(newNameUtf16Z)) {
+
+    myKey = (HKEY)__externalAddressVal(__INST(handle));
+
+    if (RegRenameKeyPtr) { // windows Vista and newer
+        /* The most probable definition of undocumented RegRenameKey:
+         *
+         * LSTATUS WINAPI RegRenameKey( 
+         *   _In_         HKEY    hKey, 
+         *   _In_         LPCTSTR lpSubKeyName,
+         *   _In_         LPCTSTR lpNewKeyName 
+         * );
+         * The links:
+         * http://blogs.microsoft.co.il/pavely/2015/09/29/regrenamekey-hidden-registry-api/
+         * https://www.unsafehex.com/index.php/2018/04/14/undocumented-function-regrenamekey/
+         */
+        _retVal = RegRenameKeyPtr(myKey,
+            (LPCTSTR)__unicode16StringVal(oldNameUtf16Z),
+            (LPCTSTR)__unicode16StringVal(newNameUtf16Z));
+    } else {
+        /* 
+         * Some old Windows without RegRenameKey() such as
+         * 32bit Windows XP. 
+         * In that case, try hard and call NTRenameKey() which 
+         * should be supported.
+         */
+        NTSTATUS status;
+        UNICODE_STRING newNameUnicodeString;  // PUNICODE_STRING <=> UNICODE_STRING*
+        wchar_t buffer[65532]; // maximum number of bytes at buffer as defined at MSDN
+
+        // open the key to be renamed
+        _retVal = RegOpenKeyExW(myKey,
+            __unicode16StringVal(oldNameUtf16Z),
+            0,
+            KEY_ALL_ACCESS,
+            &oldSubKey); 
+        if (_retVal == ERROR_SUCCESS) {
+            /* NTSTATUS RtlInitUnicodeStringEx
+             * (
+             *      PUNICODE_STRING target,
+             *      PCWSTR          source
+             * )
+             *
+             *  target  [In/Out]    Buffered unicode string to be initialized.
+             *  source  [In]    '\0' terminated unicode string used to initialize target.
+             *
+             * Assigns source to target->Buffer. The length of source is assigned to target->Length and target->MaximumLength. 
+             * If source is NULL the length of source is assumed to be 0. (In short: initialized UNICODE_STRING)
+             *
+             * Success: STATUS_SUCCESS. target is initialized.
+             * Failure: STATUS_NAME_TOO_LONG, if the source string is larger than 65532 bytes. 
+             * the UNICODE_STRING structure: 
+             * typedef struct _UNICODE_STRING { // in <winternl.h>
+             *     USHORT Length;
+             *     USHORT MaximumLength;
+             *     PWSTR  Buffer;
+             * } UNICODE_STRING, *PUNICODE_STRING;
+             */
+            status = RtlInitUnicodeStringExPtr(&newNameUnicodeString, (PWSTR)__unicode16StringVal(newNameUtf16Z));
+            /* NT_SUCCESS(Status)
+             * Evaluates to TRUE if the return value specified by Status is a success type (0 − 0x3FFFFFFF) or 
+             * an informational type (0x40000000 − 0x7FFFFFFF).
+             */
+            if NT_SUCCESS(status) {
+                /* __kernel_entry NTSTATUS NtRenameKey(
+                 * HANDLE          KeyHandle,
+                 * PUNICODE_STRING NewName
+                 * );
+                 *
+                 * KeyHandle:
+                 * A handle to the key to be renamed. The handle must be opened with the KEY_WRITE access right.
+                 * NewName:
+                 * A pointer to a UNICODE string that is the new name for the key.
+                 * 
+                 * Return Value:
+                 * Returns an NTSTATUS or error code. An error code of STATUS_ACCESS_DENIED indicates that the caller 
+                 * does not have the necessary access rights to the specified registry key or subkeys.
+                 * The forms and significance of NTSTATUS error codes are listed in the Ntstatus.h header file available 
+                 * in the WDK, and are described in the WDK documentation.
+                 *
+                 * Remarks:
+                 * This function has no associated header file. The associated import library, Ntdll.lib, is available 
+                 * in the WDK. You can also use the LoadLibrary and GetProcAddress functions to dynamically link to Ntdll.dll.
+                 * The NtRenameKey function can be used to rename an entire registry subtree. The caller must have 
+                 * KEY_CREATE_SUB_KEY access to the parent of the specified key and DELETE access to the entire subtree being renamed.
+                 */
+                status = NtRenameKeyPtr(oldSubKey, &newNameUnicodeString);
+            }
+            // Converts the specified NTSTATUS code to its equivalent system error code.
+            _retVal = RtlNtStatusToDosErrorPtr(status);
+        }
+    }
+    if (_retVal == ERROR_SUCCESS) {
+        RETURN (true);
+    }
+    if ((_retVal != ERROR_PATH_NOT_FOUND)
+     && (_retVal != ERROR_FILE_NOT_FOUND)) {
+        errorNumber = __MKSMALLINT(_retVal);
+    }
+    }
+loadError:;
+%}.
+    errorNumber notNil ifTrue:[
+    (OperatingSystem errorHolderForNumber:errorNumber) reportError.
+    ].
+    ^ false
+
+     "|aKey|
+      aKey := Win32OperatingSystem::RegistryEntry  key: 'HKEY_CURRENT_USER\Software'.
+      aKey renameSubKey: '<subKey name>' to: '<newSubKey name>'."
+! !
+
 !Win32OperatingSystem::TextMetricsStructure class methodsFor:'instance creation'!
 
 new