Ticket #269: libbasic_fix_1_of_1_rev_bc3d3a13e7cb_Issue__269__Renaming_a_registry_subKey_via_RegRenameKey___or_if_it_fails_via_NtRenameKey__.patch

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

implementation of registry rename

  • Win32OperatingSystem.st

    # HG changeset patch
    # User Patrik Svestka <patrik.svestka@gmail.com>
    # Date 1554802444 -7200
    #      Tue Apr 09 11:34:04 2019 +0200
    # Branch jv
    # Node ID bc3d3a13e7cb551bf1f36dbfda7601bfafb6349e
    # Parent  c2522e4a1b3c7df9567b8e33fcfd11505561cb56
    Issue #269: Renaming a registry subKey via RegRenameKey() or if it fails via NtRenameKey()
    
    diff -r c2522e4a1b3c -r bc3d3a13e7cb Win32OperatingSystem.st
    a b  
    360360#  include <windowsx.h>
    361361# endif
    362362
     363# ifndef WINTERNL_H_INCLUDED
     364#  define WINTERNL_H_INCLUDED
     365#  include <winternl.h> /* required for UNICODE_STRING */
     366# endif
     367
    363368# define _WIN32_DCOM     /*required for CoInitializeEx*/
    364369# include <objbase.h>    //for COM
    365370
     
    370375#  define _STRING_H_INCLUDED_
    371376#  include <string.h>
    372377# endif
    373 
     378   
    374379# ifdef __DEF_Array
    375380#  define Array __DEF_Array
    376381# endif
     
    1617916184        myKey = (HKEY)__externalAddressVal(__INST(handle));
    1618016185        if (RegDeleteKeyExWPtr) {
    1618116186            _retVal = RegDeleteKeyExWPtr(myKey,
    16182                                       (LPCTSTR) __unicode16StringVal(subKeyStringUtf16Z),
     16187                                      (LPCTSTR)__unicode16StringVal(subKeyStringUtf16Z),
    1618316188                                      _flags,
    1618416189                                      0); // reserved   
    1618516190        } else {
     
    1681716822valueNamed:nameString
    1681816823    "retrieve a value; the returned object depends upon the type:
    1681916824    REG_NONE        -> nil
    16820         REG_BINARY      -> ByteArray
    16821         REG_SZ          -> String
    16822         REG_DWORD       -> Integer
    16823         REG_QWORD       -> 64 bit Integer
     16825    REG_BINARY      -> ByteArray
     16826    REG_SZ          -> String
     16827    REG_DWORD       -> Integer
     16828    REG_QWORD       -> 64 bit Integer
    1682416829    REG_MULTI_SZ    -> Array of strings
    1682516830    "
    1682616831
     
    1757017575    "Created: / 19.5.1999 / 21:45:05 / cg"
    1757117576! !
    1757217577
     17578!Win32OperatingSystem::RegistryEntry methodsFor:'renaming'!
     17579
     17580renameSubKey: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    }
     17737loadError:;
     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
    1757317749!Win32OperatingSystem::TextMetricsStructure class methodsFor:'instance creation'!
    1757417750
    1757517751new