Archive: Simple patch to add explicit 32/64-bit registry support


Simple patch to add explicit 32/64-bit registry support
Let me start off by saying that NSIS rocks and I really appreciate everyone who has worked so hard on this software.

The biggest problem I've run into is my need to install a .NET application on a 64-bit system using this 32-bit installer. I realized that the simplest solution to my problem was to explicitly write into the 64-bit registry space from 32-bit land. This patch provides a very simple way to accomplish this by allowing the user to pass in additional flags to the "REGSAM samDesired" parameter of the Win32 RegXXX functions.

This is probably not the correct way to do this but my total experience with the NSIS source code is about 1 hour. It definitely does what I need and I hope it helps someone else in a similar situation. If the concept fits the NSIS development model, it would be great if someone else wants to refine this code (maybe make the flags an optional parameter?).

Anyways, thanks again to the NSIS developers.

Cheers,
Ben Martz


Index: D:/SVN/3.0/installer/nsis-2.22-src/Source/tokens.cpp
===================================================================
--- D:/SVN/3.0/installer/nsis-2.22-src/Source/tokens.cpp (revision 586)
+++ D:/SVN/3.0/installer/nsis-2.22-src/Source/tokens.cpp (revision 587)
@@ -69,8 +69,8 @@
{TOK_DBOPTIMIZE,"SetDatablockOptimize",1,0,"(off|on)",TP_ALL},
{TOK_DELETEINISEC,"DeleteINISec",2,0,"ini_file section_name",TP_CODE},
{TOK_DELETEINISTR,"DeleteINIStr",3,0,"ini_file section_name entry_name",TP_CODE},
-{TOK_DELETEREGKEY,"DeleteRegKey",2,1,"[/ifempty] root_key subkey\n root_key=(HKCR|HKLM|HKCU|HKU|HKCC|HKDD|HKPD|SHCTX)",TP_CODE},
-{TOK_DELETEREGVALUE,"DeleteRegValue",3,0,"root_key subkey entry_name\n root_key=(HKCR|HKLM|HKCU|HKU|HKCC|HKDD|HKPD|SHCTX)",TP_CODE},
+{TOK_DELETEREGKEY,"DeleteRegKey",3,1,"[/ifempty] root_key subkey rootkeyflags\n root_key=(HKCR|HKLM|HKCU|HKU|HKCC|HKDD|HKPD|SHCTX)",TP_CODE},
+{TOK_DELETEREGVALUE,"DeleteRegValue",4,0,"root_key subkey entry_name rootkeyflags\n root_key=(HKCR|HKLM|HKCU|HKU|HKCC|HKDD|HKPD|SHCTX)",TP_CODE},
{TOK_DELETE,"Delete",1,1,"[/REBOOTOK] filespec",TP_CODE},
{TOK_DETAILPRINT,"DetailPrint",1,0,"message",TP_CODE},
{TOK_DIRTEXT,"DirText",0,4,"[directory_page_description] [directory_page_subtext] [browse_button_text] [browse_dlg_text]",TP_PG},
@@ -82,8 +82,8 @@
{TOK_ROOTDIRINST,"AllowRootDirInstall",1,0,"(true|false)",TP_GLOBAL},
{TOK_CHECKBITMAP,"CheckBitmap",1,0,"local_bitmap.bmp",TP_GLOBAL},
{TOK_ENABLEWINDOW,"EnableWindow",2,0,"hwnd (1|0)",TP_CODE},
-{TOK_ENUMREGKEY,"EnumRegKey",4,0,"$(user_var: output) rootkey subkey index\n root_key=(HKCR|HKLM|HKCU|HKU|HKCC|HKDD|HKPD|SHCTX)",TP_CODE},
-{TOK_ENUMREGVAL,"EnumRegValue",4,0,"$(user_var: output) rootkey subkey index\n root_key=(HKCR|HKLM|HKCU|HKU|HKCC|HKDD|HKPD|SHCTX)",TP_CODE},
+{TOK_ENUMREGKEY,"EnumRegKey",5,0,"$(user_var: output) rootkey subkey index rootkeyflags\n root_key=(HKCR|HKLM|HKCU|HKU|HKCC|HKDD|HKPD|SHCTX)",TP_CODE},
+{TOK_ENUMREGVAL,"EnumRegValue",5,0,"$(user_var: output) rootkey subkey index rootkeyflags\n root_key=(HKCR|HKLM|HKCU|HKU|HKCC|HKDD|HKPD|SHCTX)",TP_CODE},
{TOK_EXCH,"Exch",0,1,"[$(user_var)] | [stack_item_index]",TP_CODE},
{TOK_EXEC,"Exec",1,0,"command_line",TP_CODE},
{TOK_EXECWAIT,"ExecWait",1,1,"command_line [$(user_var: return value)]",TP_CODE},
@@ -117,7 +117,7 @@
{TOK_IFFILEEXISTS,"IfFileExists",2,1,"filename label_to_goto_if_file_exists [label_to_goto_otherwise]",TP_CODE},
{TOK_IFREBOOTFLAG,"IfRebootFlag",1,1,"jump_if_set [jump_if_not_set]",TP_CODE},
{TOK_IFSILENT,"IfSilent",1,1,"jump_if_silent [jump_if_not_silent]",TP_CODE},
-{TOK_INSTALLDIRREGKEY,"InstallDirRegKey",3,0,"root_key subkey entry_name\n root_key=(HKCR|HKLM|HKCU|HKU|HKCC|HKDD|HKPD)",TP_GLOBAL},
+{TOK_INSTALLDIRREGKEY,"InstallDirRegKey",4,0,"root_key subkey entry_name rootkeyflags\n root_key=(HKCR|HKLM|HKCU|HKU|HKCC|HKDD|HKPD)",TP_GLOBAL},
{TOK_INSTCOLORS,"InstallColors",1,1,"(/windows | (foreground_color background_color))",TP_GLOBAL},
{TOK_INSTDIR,"InstallDir",1,0,"default_install_directory",TP_GLOBAL},
{TOK_INSTPROGRESSFLAGS,"InstProgressFlags",0,-1,"[flag [...]]\n flag={smooth|colored}",TP_GLOBAL},
@@ -155,8 +155,8 @@
{TOK_PUSH,"Push",1,0,"string",TP_CODE},
{TOK_QUIT,"Quit",0,0,"",TP_CODE},
{TOK_READINISTR,"ReadINIStr",4,0,"$(user_var: output) ini_file section entry_name",TP_CODE},
-{TOK_READREGDWORD,"ReadRegDWORD",4,0,"$(user_var: output) rootkey subkey entry\n root_key=(HKCR|HKLM|HKCU|HKU|HKCC|HKDD|HKPD|SHCTX)",TP_CODE},
-{TOK_READREGSTR,"ReadRegStr",4,0,"$(user_var: output) rootkey subkey entry\n root_key=(HKCR|HKLM|HKCU|HKU|HKCC|HKDD|HKPD|SHCTX)",TP_CODE},
+{TOK_READREGDWORD,"ReadRegDWORD",5,0,"$(user_var: output) rootkey subkey entry rootkeyflags\n root_key=(HKCR|HKLM|HKCU|HKU|HKCC|HKDD|HKPD|SHCTX)",TP_CODE},
+{TOK_READREGSTR,"ReadRegStr",5,0,"$(user_var: output) rootkey subkey entry rootkeyflags\n root_key=(HKCR|HKLM|HKCU|HKU|HKCC|HKDD|HKPD|SHCTX)",TP_CODE},
{TOK_READENVSTR,"ReadEnvStr",2,0,"$(user_var: output) name",TP_CODE},
{TOK_REBOOT,"Reboot",0,0,"",TP_CODE},
{TOK_REGDLL,"RegDLL",1,1,"dll_path_on_target.dll [entrypoint_symbol]",TP_CODE},
@@ -229,10 +229,10 @@
{TOK_UNREGDLL,"UnRegDLL",1,0,"dll_path_on_target.dll",TP_CODE},
{TOK_WINDOWICON,"WindowIcon",1,0,"on|off",TP_GLOBAL},
{TOK_WRITEINISTR,"WriteINIStr",4,0,"ini_file section_name entry_name new_value",TP_CODE},
-{TOK_WRITEREGBIN,"WriteRegBin",4,0,"rootkey subkey entry_name hex_string_like_12848412AB\n root_key=(HKCR|HKLM|HKCU|HKU|HKCC|HKDD|HKPD|SHCTX)",TP_CODE},
-{TOK_WRITEREGDWORD,"WriteRegDWORD",4,0,"rootkey subkey entry_name new_value_dword\n root_key=(HKCR|HKLM|HKCU|HKU|HKCC|HKDD|HKPD|SHCTX)",TP_CODE},
-{TOK_WRITEREGSTR,"WriteRegStr",4,0,"rootkey subkey entry_name new_value_string\n root_key=(HKCR|HKLM|HKCU|HKU|HKCC|HKDD|HKPD|SHCTX)",TP_CODE},
-{TOK_WRITEREGEXPANDSTR,"WriteRegExpandStr",4,0,"rootkey subkey entry_name new_value_string\n root_key=(HKCR|HKLM|HKCU|HKU|HKCC|HKDD|HKPD|SHCTX)",TP_CODE},
+{TOK_WRITEREGBIN,"WriteRegBin",5,0,"rootkey subkey entry_name hex_string_like_12848412AB rootkeyflags\n root_key=(HKCR|HKLM|HKCU|HKU|HKCC|HKDD|HKPD|SHCTX)",TP_CODE},
+{TOK_WRITEREGDWORD,"WriteRegDWORD",5,0,"rootkey subkey entry_name new_value_dword rootkeyflags\n root_key=(HKCR|HKLM|HKCU|HKU|HKCC|HKDD|HKPD|SHCTX)",TP_CODE},
+{TOK_WRITEREGSTR,"WriteRegStr",5,0,"rootkey subkey entry_name new_value_string rootkeyflags\n root_key=(HKCR|HKLM|HKCU|HKU|HKCC|HKDD|HKPD|SHCTX)",TP_CODE},
+{TOK_WRITEREGEXPANDSTR,"WriteRegExpandStr",5,0,"rootkey subkey entry_name new_value_string rootkeyflags\n root_key=(HKCR|HKLM|HKCU|HKU|HKCC|HKDD|HKPD|SHCTX)",TP_CODE},
{TOK_WRITEUNINSTALLER,"WriteUninstaller",1,0,"uninstall_exe_name",TP_CODE},
{TOK_XPSTYLE, "XPStyle",1,0,"(on|off)",TP_GLOBAL},
{TOK_REQEXECLEVEL, "RequestExecutionLevel",1,0,"none|user|highest|admin",TP_GLOBAL},
Index: D:/SVN/3.0/installer/nsis-2.22-src/Source/script.cpp
===================================================================
--- D:/SVN/3.0/installer/nsis-2.22-src/Source/script.cpp (revision 586)
+++ D:/SVN/3.0/installer/nsis-2.22-src/Source/script.cpp (revision 587)
@@ -5005,7 +5005,7 @@
int k=line.gettoken_enum(2,rootkeys[0]);
if (k == -1) k=line.gettoken_enum(2,rootkeys[1]);
if (ent.offsets[0] == -1 || k == -1) PRINTHELP()
- ent.offsets[1]=(int)rootkey_tab[k];
+ ent.offsets[1]=(int)rootkey_tab[k] | line.gettoken_int(5); // benmartz: add support for root key flags
ent.offsets[2]=add_string(line.gettoken_str(3));
ent.offsets[3]=add_string(line.gettoken_str(4));
if (which_token == TOK_READREGDWORD) ent.offsets[4]=1;
@@ -5013,8 +5013,8 @@
if (line.gettoken_str(3)[0] == '\\')
warning_fl("%s: registry path name begins with \'\\\', may cause problems",line.gettoken_str(0));

- SCRIPT_MSG("%s %s %s\\%s\\%s\n",line.gettoken_str(0),
- line.gettoken_str(1),line.gettoken_str(2),line.gettoken_str(3),line.gettoken_str(4));
+ SCRIPT_MSG("%s: %s\\%s\\%s\\%s flags=0x%08x\n",line.gettoken_str(0),
+ line.gettoken_str(1),line.gettoken_str(2),line.gettoken_str(3),line.gettoken_str(4),line.gettoken_int(5));
}
return add_entry(&ent);
case TOK_DELETEREGVALUE:
@@ -5031,21 +5031,21 @@
a++;
ent.offsets[4]=3;
}
- if (line.gettoken_str(a+2)[0]) PRINTHELP()
+ if (line.gettoken_str(a+3)[0]) PRINTHELP()
}
int k=line.gettoken_enum(a,rootkeys[0]);
if (k == -1) k=line.gettoken_enum(a,rootkeys[1]);
if (k == -1) PRINTHELP()
ent.which=EW_DELREG;
- ent.offsets[1]=(int)rootkey_tab[k];
+ ent.offsets[1]=(int)rootkey_tab[k] | line.gettoken_int(a+3); // benmartz: add support for root key flags
ent.offsets[2]=add_string(line.gettoken_str(a+1));
ent.offsets[3]=(which_token==TOK_DELETEREGKEY)?0:add_string(line.gettoken_str(a+2));
if (line.gettoken_str(a+1)[0] == '\\')
warning_fl("%s: registry path name begins with \'\\\', may cause problems",line.gettoken_str(0));
if (which_token==TOK_DELETEREGKEY)
- SCRIPT_MSG("DeleteRegKey: %s\\%s\n",line.gettoken_str(a),line.gettoken_str(a+1));
+ SCRIPT_MSG("DeleteRegKey: %s\\%s flags=0x%08x\n",line.gettoken_str(a),line.gettoken_str(a+1),line.gettoken_int(a+2));
else
- SCRIPT_MSG("DeleteRegValue: %s\\%s\\%s\n",line.gettoken_str(a),line.gettoken_str(a+1),line.gettoken_str(a+2));
+ SCRIPT_MSG("DeleteRegValue: %s\\%s\\%s flags=0x%08x\n",line.gettoken_str(a),line.gettoken_str(a+1),line.gettoken_str(a+2),line.gettoken_int(a+3));
}
return add_entry(&ent);
case TOK_WRITEREGSTR:
@@ -5057,15 +5057,15 @@
if (k == -1) k=line.gettoken_enum(1,rootkeys[1]);
if (k == -1) PRINTHELP()
ent.which=EW_WRITEREG;
- ent.offsets[0]=(int)rootkey_tab[k];
+ ent.offsets[0]=(int)rootkey_tab[k] | line.gettoken_int(5); // benmartz: add support for root key flags
ent.offsets[1]=add_string(line.gettoken_str(2));
if (line.gettoken_str(2)[0] == '\\')
warning_fl("%s: registry path name begins with \'\\\', may cause problems",line.gettoken_str(0));
ent.offsets[2]=add_string(line.gettoken_str(3));
if (which_token == TOK_WRITEREGSTR || which_token == TOK_WRITEREGEXPANDSTR)
{
- SCRIPT_MSG("%s: %s\\%s\\%s=%s\n",
- line.gettoken_str(0),line.gettoken_str(1),line.gettoken_str(2),line.gettoken_str(3),line.gettoken_str(4));
+ SCRIPT_MSG("%s: %s\\%s\\%s=%s flags=0x%08x\n",
+ line.gettoken_str(0),line.gettoken_str(1),line.gettoken_str(2),line.gettoken_str(3),line.gettoken_str(4),line.gettoken_int(5));
ent.offsets[3]=add_string(line.gettoken_str(4));
ent.offsets[4]=ent.offsets[5]=REG_SZ;
if (which_token == TOK_WRITEREGEXPANDSTR)
@@ -5102,8 +5102,8 @@
data[data_len++]=c;
}
if (*p) PRINTHELP()
- SCRIPT_MSG("WriteRegBin: %s\\%s\\%s=%s\n",
- line.gettoken_str(1),line.gettoken_str(2),line.gettoken_str(3),line.gettoken_str(4));
+ SCRIPT_MSG("WriteRegBin: %s\\%s\\%s=%s flags=%08x\n",
+ line.gettoken_str(1),line.gettoken_str(2),line.gettoken_str(3),line.gettoken_str(4),line.gettoken_int(5));
ent.offsets[3]=add_db_data(data,data_len);
if (ent.offsets[3] < 0) return PS_ERROR;
ent.offsets[4]=ent.offsets[5]=REG_BINARY;
@@ -5113,8 +5113,8 @@
ent.offsets[3]=add_string(line.gettoken_str(4));
ent.offsets[4]=ent.offsets[5]=REG_DWORD;

- SCRIPT_MSG("WriteRegDWORD: %s\\%s\\%s=%s\n",
- line.gettoken_str(1),line.gettoken_str(2),line.gettoken_str(3),line.gettoken_str(4));
+ SCRIPT_MSG("WriteRegDWORD: %s\\%s\\%s=%s flags=%08x\n",
+ line.gettoken_str(1),line.gettoken_str(2),line.gettoken_str(3),line.gettoken_str(4),line.gettoken_int(5));
}
}
return add_entry(&ent);
@@ -5126,13 +5126,13 @@
int k=line.gettoken_enum(2,rootkeys[0]);
if (k == -1) k=line.gettoken_enum(2,rootkeys[1]);
if (ent.offsets[0] == -1 || k == -1) PRINTHELP()
- ent.offsets[1]=(int)rootkey_tab[k];
+ ent.offsets[1]=(int)rootkey_tab[k] | line.gettoken_int(5); // benmartz: add support for root key flags
ent.offsets[2]=add_string(line.gettoken_str(3));
ent.offsets[3]=add_string(line.gettoken_str(4));
ent.offsets[4]=which_token == TOK_ENUMREGKEY;
if (line.gettoken_str(3)[0] == '\\') warning_fl("%s: registry path name begins with \'\\\', may cause problems",line.gettoken_str(0));
- SCRIPT_MSG("%s %s %s\\%s\\%s\n",which_token == TOK_ENUMREGKEY ? "EnumRegKey" : "EnumRegValue",
- line.gettoken_str(1),line.gettoken_str(2),line.gettoken_str(3),line.gettoken_str(4));
+ SCRIPT_MSG("%s %s %s\\%s\\%s flags=%08x\n",which_token == TOK_ENUMREGKEY ? "EnumRegKey" : "EnumRegValue",
+ line.gettoken_str(1),line.gettoken_str(2),line.gettoken_str(3),line.gettoken_str(4),line.gettoken_int(5));
}
return add_entry(&ent);
#else//!NSIS_SUPPORT_REGISTRYFUNCTIONS
Index: D:/SVN/3.0/installer/nsis-2.22-src/Source/exehead/exec.c
===================================================================
--- D:/SVN/3.0/installer/nsis-2.22-src/Source/exehead/exec.c (revision 586)
+++ D:/SVN/3.0/installer/nsis-2.22-src/Source/exehead/exec.c (revision 587)
@@ -131,7 +131,7 @@
static LONG NSISCALL myRegDeleteKeyEx(HKEY thiskey, LPCTSTR lpSubKey, int onlyifempty)
{
HKEY key;
- int retval=RegOpenKeyEx(thiskey,lpSubKey,0,KEY_ENUMERATE_SUB_KEYS,&key);
+ int retval=RegOpenKeyEx((HKEY)(((int)thiskey) & ~0x300),lpSubKey,0,KEY_ENUMERATE_SUB_KEYS | ((int)thiskey & 0x300),&key);
if (retval==ERROR_SUCCESS)
{
// NB - don't change this to static (recursive function)
@@ -163,7 +163,7 @@
static HKEY NSISCALL myRegOpenKey(REGSAM samDesired)
{
HKEY hKey;
- if (RegOpenKeyEx(GetRegRootKey(parms[1]), GetStringFromParm(0x22), 0, samDesired, &hKey) == ERROR_SUCCESS)
+ if (RegOpenKeyEx(GetRegRootKey(parms[1] & ~0x300), GetStringFromParm(0x22), 0, samDesired | (parms[1] & 0x300), &hKey) == ERROR_SUCCESS)
{
return hKey;
}
@@ -1187,7 +1187,7 @@
const char *rkn=RegKeyHandleToName(rootkey);

exec_error++;
- if (RegCreateKeyEx(rootkey,buf1,0,0,REG_OPTION_NON_VOLATILE,KEY_SET_VALUE,0,&hKey,0) == ERROR_SUCCESS)
+ if (RegCreateKeyEx((HKEY)(((int)parm0) & ~0x300),buf1,0,0,REG_OPTION_NON_VOLATILE,KEY_SET_VALUE | ((int)parm0 & 0x300),0,&hKey,0) == ERROR_SUCCESS)
{
LPBYTE data = (LPBYTE) buf2;
DWORD size = 0;
Index: D:/SVN/3.0/installer/nsis-2.22-src/JPR README.txt
===================================================================
--- D:/SVN/3.0/installer/nsis-2.22-src/JPR README.txt (revision 0)
+++ D:/SVN/3.0/installer/nsis-2.22-src/JPR README.txt (revision 587)
@@ -0,0 +1,16 @@
+1. Install Python and SCONS as specified.
+2. Open your VS2005 command line and run
+ scons TEMP_MSVC2005=yes PREFIX="C:\Program Files\NSIS" install
+
+That's it! All RegXXX commands now have an additional parameter where we can pass
+${KEY_WOW64_64KEY} or ${KEY_WOW64_32KEY} as needed.
+
+You may need the following defines if not including CDMS_Common.nsh:
+
+!define KEY_WOW64_64KEY 0x0100 ; always access 64-bit key
+!define KEY_WOW64_32KEY 0x0200 ; always access 32-bit key
+
+
+Have fun!
+Ben Martz
+4 December 2006

Nice patch, thanks. Please submit it to the patch tracker so it doesn't get lost in the forum mist.

You've got the changes right. Though there are a few optimizations that can be done. For example, the entire SAM can be passed as a parameter so the compiler can do all the work. I'd also have it as a switch instead of an arbitrary integer passed as the last parameter. Maybe /WOW64...


Very good that InstallDirRegKey included to "registry reflection patch", but what about system.nsh (and other nsh) ReadRegStr, first of all - langDll defines? Both variants (rootkeyflags parameter and /wow option) require many macro to be re-worked. What about installer (changable) option (like SetDetailsView)?
Edited BTW how reflection works if RegConnectRegistry() used with NULL or local host name?


Kichik,

I agree that it could definitely use some improvement. In the meanwhile, I've posted it to the patch tracker as you requested. I'm hoping that someone might be willing to take up the torch, otherwise it may be a while before I can get to it.

Cheers,
Ben

P.S. Something that most users might not know is that if you pass in KEY_WOW64_64KEY when running 32-bit on 32-bit, the bit is ignored so the same Read/WriteReg call in your NSIS script will work "correctly" on 32-bit and 64-bit host platforms. For example, the following line will always read from the native registry:

ReadRegStr $2 HKLM "SOFTWARE\Crystal Decisions\10.2\Crystal Reports" "CommonFiles" ${KEY_WOW64_64KEY}