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