| 17578 | !Win32OperatingSystem::RegistryEntry methodsFor:'renaming'! |
| 17579 | |
| 17580 | renameSubKey:oldNameString to:newNameString |
| 17581 | "There is difficult choice when it comes to renaming registry subKey(s). You can either use undocumented |
| 17582 | RegRenameKey or NtRenameKey which could be removed in the future. The code now uses the RegRenameKey |
| 17583 | as first choice with NtRenameKey as a fallback." |
| 17584 | |
| 17585 | |oldNameUtf16Z newNameUtf16Z errorNumber| |
| 17586 | "/ checking user input |
| 17587 | oldNameString isString ifFalse:[ |
| 17588 | self error: 'oldNameString is not a string!!' |
| 17589 | ]. |
| 17590 | newNameString isString ifFalse:[ |
| 17591 | self error: 'newNameString is not a string!!' |
| 17592 | ]. |
| 17593 | oldNameUtf16Z := oldNameString asUnicode16StringZ. |
| 17594 | newNameUtf16Z := newNameString asUnicode16StringZ. |
| 17595 | |
| 17596 | %{ |
| 17597 | static int initialized = 0; |
| 17598 | static LONG (WINAPI *RegRenameKeyPtr)(HKEY hKey, LPCTSTR lpSubKeyName, LPCTSTR lpNewKeyName) = NULL; |
| 17599 | static NTSTATUS (WINAPI *NtRenameKeyPtr)(HANDLE keyHandle, PUNICODE_STRING newName) = NULL; |
| 17600 | static NTSTATUS (WINAPI *RtlInitUnicodeStringExPtr)(PUNICODE_STRING destination, PCWSTR source) = NULL; |
| 17601 | static ULONG (WINAPI *RtlNtStatusToDosErrorPtr)(NTSTATUS Status) = NULL; |
| 17602 | |
| 17603 | if (!initialized) { |
| 17604 | initialized = 1; |
| 17605 | HMODULE advapi32 = LoadLibrary("advapi32.dll"); |
| 17606 | if (!advapi32) { |
| 17607 | /* |
| 17608 | * failed to load advapi32.dll |
| 17609 | */ |
| 17610 | errorNumber = __MKSMALLINT(GetLastError()); |
| 17611 | goto loadError; |
| 17612 | } |
| 17613 | RegRenameKeyPtr = (LONG (*)(HKEY hKey, LPCTSTR lpSubKeyName, LPCTSTR lpNewKeyName)) GetProcAddress( |
| 17614 | advapi32, "RegRenameKey"); |
| 17615 | if (!RegRenameKeyPtr) { // fallback for WindowsXP, Windows 2000 or in case the undocumented RegRenameKey fails |
| 17616 | HMODULE ntdll = LoadLibrary("ntdll.dll"); |
| 17617 | if (!ntdll) { |
| 17618 | /* |
| 17619 | * failed to load ntdll.dll |
| 17620 | */ |
| 17621 | errorNumber = __MKSMALLINT(GetLastError()); |
| 17622 | goto loadError; |
| 17623 | } |
| 17624 | NtRenameKeyPtr = (NTSTATUS (*)(HANDLE keyHandle, PUNICODE_STRING newName)) GetProcAddress( |
| 17625 | ntdll, "NtRenameKey"); |
| 17626 | RtlInitUnicodeStringExPtr = (NTSTATUS (*)(PUNICODE_STRING destination, PCWSTR source)) GetProcAddress( |
| 17627 | ntdll, "RtlInitUnicodeStringEx"); |
| 17628 | RtlNtStatusToDosErrorPtr = (ULONG (*)(NTSTATUS Status)) GetProcAddress(ntdll, "RtlNtStatusToDosError"); |
| 17629 | } |
| 17630 | } |
| 17631 | |
| 17632 | HKEY myKey, oldSubKey = 0; |
| 17633 | int _retVal; |
| 17634 | |
| 17635 | if (__isExternalAddressLike(__INST(handle)) |
| 17636 | && __isUnicode16String(oldNameUtf16Z) |
| 17637 | && __isUnicode16String(newNameUtf16Z)) { |
| 17638 | |
| 17639 | myKey = (HKEY)__externalAddressVal(__INST(handle)); |
| 17640 | |
| 17641 | if (RegRenameKeyPtr) { // windows Vista and newer |
| 17642 | /* The most probable definition of undocumented RegRenameKey: |
| 17643 | * |
| 17644 | * LSTATUS WINAPI RegRenameKey( |
| 17645 | * _In_ HKEY hKey, |
| 17646 | * _In_ LPCTSTR lpSubKeyName, |
| 17647 | * _In_ LPCTSTR lpNewKeyName |
| 17648 | * ); |
| 17649 | * The links: |
| 17650 | * http://blogs.microsoft.co.il/pavely/2015/09/29/regrenamekey-hidden-registry-api/ |
| 17651 | * https://www.unsafehex.com/index.php/2018/04/14/undocumented-function-regrenamekey/ |
| 17652 | */ |
| 17653 | _retVal = RegRenameKeyPtr(myKey, |
| 17654 | (LPCTSTR)__unicode16StringVal(oldNameUtf16Z), |
| 17655 | (LPCTSTR)__unicode16StringVal(newNameUtf16Z)); |
| 17656 | } else { |
| 17657 | /* |
| 17658 | * Some old Windows without RegRenameKey() such as |
| 17659 | * 32bit Windows XP. |
| 17660 | * In that case, try hard and call NTRenameKey() which |
| 17661 | * should be supported. |
| 17662 | */ |
| 17663 | NTSTATUS status; |
| 17664 | UNICODE_STRING newNameUnicodeString; // PUNICODE_STRING <=> UNICODE_STRING* |
| 17665 | wchar_t buffer[65532]; // maximum number of bytes at buffer as defined at MSDN |
| 17666 | |
| 17667 | // open the key to be renamed |
| 17668 | _retVal = RegOpenKeyExW(myKey, |
| 17669 | __unicode16StringVal(oldNameUtf16Z), |
| 17670 | 0, |
| 17671 | KEY_ALL_ACCESS, |
| 17672 | &oldSubKey); |
| 17673 | if (_retVal == ERROR_SUCCESS) { |
| 17674 | /* NTSTATUS RtlInitUnicodeStringEx |
| 17675 | * ( |
| 17676 | * PUNICODE_STRING target, |
| 17677 | * PCWSTR source |
| 17678 | * ) |
| 17679 | * |
| 17680 | * target [In/Out] Buffered unicode string to be initialized. |
| 17681 | * source [In] '\0' terminated unicode string used to initialize target. |
| 17682 | * |
| 17683 | * Assigns source to target->Buffer. The length of source is assigned to target->Length and target->MaximumLength. |
| 17684 | * If source is NULL the length of source is assumed to be 0. (In short: initialized UNICODE_STRING) |
| 17685 | * |
| 17686 | * Success: STATUS_SUCCESS. target is initialized. |
| 17687 | * Failure: STATUS_NAME_TOO_LONG, if the source string is larger than 65532 bytes. |
| 17688 | * the UNICODE_STRING structure: |
| 17689 | * typedef struct _UNICODE_STRING { // in <winternl.h> |
| 17690 | * USHORT Length; |
| 17691 | * USHORT MaximumLength; |
| 17692 | * PWSTR Buffer; |
| 17693 | * } UNICODE_STRING, *PUNICODE_STRING; |
| 17694 | */ |
| 17695 | status = RtlInitUnicodeStringExPtr(&newNameUnicodeString, (PWSTR)__unicode16StringVal(newNameUtf16Z)); |
| 17696 | /* NT_SUCCESS(Status) |
| 17697 | * Evaluates to TRUE if the return value specified by Status is a success type (0 − 0x3FFFFFFF) or |
| 17698 | * an informational type (0x40000000 − 0x7FFFFFFF). |
| 17699 | */ |
| 17700 | if NT_SUCCESS(status) { |
| 17701 | /* __kernel_entry NTSTATUS NtRenameKey( |
| 17702 | * HANDLE KeyHandle, |
| 17703 | * PUNICODE_STRING NewName |
| 17704 | * ); |
| 17705 | * |
| 17706 | * KeyHandle: |
| 17707 | * A handle to the key to be renamed. The handle must be opened with the KEY_WRITE access right. |
| 17708 | * NewName: |
| 17709 | * A pointer to a UNICODE string that is the new name for the key. |
| 17710 | * |
| 17711 | * Return Value: |
| 17712 | * Returns an NTSTATUS or error code. An error code of STATUS_ACCESS_DENIED indicates that the caller |
| 17713 | * does not have the necessary access rights to the specified registry key or subkeys. |
| 17714 | * The forms and significance of NTSTATUS error codes are listed in the Ntstatus.h header file available |
| 17715 | * in the WDK, and are described in the WDK documentation. |
| 17716 | * |
| 17717 | * Remarks: |
| 17718 | * This function has no associated header file. The associated import library, Ntdll.lib, is available |
| 17719 | * in the WDK. You can also use the LoadLibrary and GetProcAddress functions to dynamically link to Ntdll.dll. |
| 17720 | * The NtRenameKey function can be used to rename an entire registry subtree. The caller must have |
| 17721 | * KEY_CREATE_SUB_KEY access to the parent of the specified key and DELETE access to the entire subtree being renamed. |
| 17722 | */ |
| 17723 | status = NtRenameKeyPtr(oldSubKey, &newNameUnicodeString); |
| 17724 | } |
| 17725 | // Converts the specified NTSTATUS code to its equivalent system error code. |
| 17726 | _retVal = RtlNtStatusToDosErrorPtr(status); |
| 17727 | } |
| 17728 | } |
| 17729 | if (_retVal == ERROR_SUCCESS) { |
| 17730 | RETURN (true); |
| 17731 | } |
| 17732 | if ((_retVal != ERROR_PATH_NOT_FOUND) |
| 17733 | && (_retVal != ERROR_FILE_NOT_FOUND)) { |
| 17734 | errorNumber = __MKSMALLINT(_retVal); |
| 17735 | } |
| 17736 | } |
| 17737 | loadError:; |
| 17738 | %}. |
| 17739 | errorNumber notNil ifTrue:[ |
| 17740 | (OperatingSystem errorHolderForNumber:errorNumber) reportError. |
| 17741 | ]. |
| 17742 | ^ false |
| 17743 | |
| 17744 | "|aKey| |
| 17745 | aKey := Win32OperatingSystem::RegistryEntry key: 'HKEY_CURRENT_USER\Software'. |
| 17746 | aKey renameSubKey: '<subKey name>' to: '<newSubKey name>'." |
| 17747 | ! ! |
| 17748 | |