--- 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