Issue #109: Call `RegDeleteKeyExW()` indirectly via function pointer jv
authorJan Vrany <jan.vrany@fit.cvut.cz>
Wed, 28 Dec 2016 22:58:47 +0000
branchjv
changeset 21248 2346b1744906
parent 21247 9ee1206fc247
child 21249 86c01ee5a76e
Issue #109: Call `RegDeleteKeyExW()` indirectly via function pointer ...to allow running Smalltalk/X on (old) Windows XP. To be able to delete 32bit / 64bit key we need to use RegDeleteKeyExW() which is, however, not available on (old) 32bit Windows XP. Yet we'd like Smalltalk/X to run on these. To do so, call RegDeleteKeyExW() indirectly via a function pointer obtained by GetProcAddress(). If GetProcAddress() fails - meaning we're running on such an old Windows - we call old RegDeleteKeyW(). https://swing.fit.cvut.cz/projects/stx-jv/ticket/109
Win32OperatingSystem.st
--- a/Win32OperatingSystem.st	Mon Dec 26 10:13:07 2016 +0000
+++ b/Win32OperatingSystem.st	Wed Dec 28 22:58:47 2016 +0000
@@ -15856,27 +15856,43 @@
 !
 
 deleteSubKeyNamed:subKeyString flags:flags
-    "delete a key below mySelf.
-     Return true on success.
-     flags may be one of:
+    "Delete a key below myself. return true on success.
+     
+     `flags` may be one of:
 	#KEY_WOW64_64KEY to force access to the 64Bit Windows key,
 	#KEY_WOW64_32KEY to force access to the 32Bit Windows key,
 	or nil, to access the key (32/64) for the current application.
-
-     CAVEAT: due to a missing library entry in the BCC system,
-	     the flags are currently ignored"
+    "
 
     |subKeyStringZ errorNumber|
 
     subKeyStringZ := subKeyString asUnicode16StringZ.
 
 %{
-#ifndef KEY_WOW64_64KEY
-// this is missing in BCC header files
-# define KEY_WOW64_64KEY    0x0100
-# define KEY_WOW64_32KEY    0x0200
-#endif
-
+    /* 
+     * Following is a little tricky. To be able to access 32bit / 64bit keys
+     * we need to use RegDeleteKeyExW() which not however available on (old)\
+     * 32bit Windows XP. Yet we'd like Smalltalk/X to run on those. 
+     *
+     * To do so, call RegDeleteKeyExW() indirectly via a function pointer
+     * obtained by GetProcAddress(). If GetProcAddress() fails - meaning 
+     * we're running on such old Windows - we call old RegDeleteKeyW(). 
+     */
+    static int initialized = 0;
+    static LONG (WINAPI *RegDeleteKeyExWPtr)(HKEY hKey, LPCTSTR lpSubKey, REGSAM  samDesired, DWORD   Reserved) = NULL;
+    if (!initialized) {
+        initialized = 1;
+        HMODULE advapi32 = LoadLibrary("advapi32.dll");
+        if (!advapi32) {
+            /* 
+             * Hmm, failed to load Advapi32.dll, can this happen?
+             */            
+            errorNumber = __MKSMALLINT(GetLastError());
+            goto out;
+        }
+        RegDeleteKeyExWPtr = GetProcAddress(advapi32, "RegDeleteKeyExW");        
+    }    
+    
     HKEY myKey, subKey = 0;
     int _retVal;
     int _flags = 0;
@@ -15895,14 +15911,27 @@
     if (__isExternalAddressLike(__INST(handle))
      && __isUnicode16String(subKeyStringZ)) {
 	myKey = (HKEY)__externalAddressVal(__INST(handle));
-#ifdef __BORLANDC__
-	_retVal = RegDeleteKeyW(myKey, __unicode16StringVal(subKeyStringZ));
-#else
-	_retVal = RegDeleteKeyExW(myKey,
-		    __unicode16StringVal(subKeyStringZ),
-		    _flags,
-		    0); // reserved
-#endif
+	if (RegDeleteKeyExWPtr) {
+	    _retVal = RegDeleteKeyExWPtr(myKey,
+		                      __unicode16StringVal(subKeyStringZ),
+		                      _flags,
+		                      0); // reserved	
+	} else {
+	    /* 
+	     * Some old Windows without RegDeleteKeyExW() such as
+	     * 32bit Windows XP. 
+	     * In that case, try hard and call RegDeleteKeyW() - but only
+	     * if no flags are passed, i.e., `flags` parameter is nil.
+	     * This is to avoid silent failures when one explicitly asks
+	     * for 32bit or 64bit view on Windows that does not support 
+	     * RegDeleteKeyExW().
+	     */
+	    if (flags != nil) {
+	        errorNumber = __MKSMALLINT(-1); // really stupid, should do better
+	        goto out;
+	    }
+	    _retVal = RegDeleteKeyW(myKey, __unicode16StringVal(subKeyStringZ));    
+	}
 	if (_retVal == ERROR_SUCCESS) {
 	    RETURN (true);
 	}
@@ -15915,7 +15944,11 @@
 %}.
 
     errorNumber notNil ifTrue:[
-	(OperatingSystem errorHolderForNumber:errorNumber) reportError.
+        errorNumber == -1 ifTrue:[
+            self primitiveFailed: '32/64bit registry view requested but no RegDeleteKeyExW(). Windows too old?'
+        ] ifFalse:[
+	    (OperatingSystem errorHolderForNumber:errorNumber) reportError.
+        ]
     ].
     ^ false