简述:因为新的需求,需要安装程序读出软件主程序写入注册表的值,因此使用Nsis的ReadRegStr读取注册表中REG_SZ类型的值。
步骤:
1、(公司前辈的代码)(定义了宏UNICODE)
if (RegOpenKeyEx(HKEY_CURRENT_USER,TEXT("Software\\MyApp"),0,KEY_READ | KEY_WRITE,&hKey) != ERROR_SUCCESS)
return -1;
if (RegSetValueEx(hKey,TEXT("SomeData"),0,REG_SZ,(LPCBYTE)"1",1) == ERROR_SUCCESS)
{
Sleep(500);
RegCloseKey(hKey);
return 0;
}
此处UNICODE字符集起到了关键的作用。
由于是UNICODE,LPCBYTE的字符串存入注册表就成了1个字节。
应该这样,RegSetValueExW 在存入RES_SZ的字符串时,也应该保证最后两个成员是宽字符的(即UNICODE)。
2、nsis代码
ReadRegDWORD $reg_value HKCU "Software\MyApp" "SomeData"
然而,$reg_value 总是空的。
查阅了文档:
ReadRegStr
user_var(output) root_key sub_key name
Reads from the registry into the user variable $x. Valid values for root_key are listed under WriteRegStr. The error flag will be set and $x will be set to an empty string ("") if the string is not present. If the value is present, but is of type REG_DWORD, it will be read and converted to a string and the error flag will be set.
ReadRegStr $0 HKLM Software\NSIS ""
DetailPrint "NSIS is installed at: $0"
以及看了ReadRegDWORD的文档,
ReadRegDWORD
user_var(output) root_key sub_key name
Reads a 32 bit DWORD from the registry into the user variable $x. Valid values for root_key are listed under WriteRegStr. The error flag will be set and $x will be set to an empty string ("" which is 0) if the DWORD is not present. If the value is present, but is not a DWORD, it will be read as a string and the error flag will be set.
ReadRegDWORD $0 HKLM Software\NSIS VersionBuild
尝试了各种办法,总是为空。
后来无意间通过注册表编辑器,手动将Software\\MyApp 下的SomeData修改为0 (REG_SZ类型)。结果nsis能读出来了,然后又修改为1 ,也能读取来了。
无语了,只能查看写的代码。就看到了上面的代码。
RegSetValueEx(hKey,TEXT("SomeData"),0,REG_SZ,(LPCBYTE)"1",1)
注意红色部分。
仅仅写入了字符串的第一个字节。也就是字符串“1”的后面的"\0" 没有写入注册表。然而注册表编辑器能自动适应这种数据,可以展现出对应的字符串 0 或 1 。虽然数据实际是没有结尾的"\0"。
解决办法:简单的将写入的字节数修改为2 即可。
RegSetValueEx(hKey,TEXT("SomeData"),0,REG_SZ,(LPCBYTE)"1",2)
3、继续探索写入代码:
其实RegSetValueEx的文档写的很明白,只是前辈的代码没考虑。
RegSetValueEx Function
cbData
The size of the information pointed to by the lpData parameter, in bytes. If the data is of type REG_SZ, REG_EXPAND_SZ, or REG_MULTI_SZ, cbData must include the size of the terminating null character or characters.
4、继续探索nsis的代码:
查看nsis的代码,找到ReadRegStr和ReadRegDWORD的实现。
case EW_READREGSTR: // read registry string
{
HKEY hKey=myRegOpenKey(KEY_READ);
char *p=var0;
char *buf3=GetStringFromParm(0x33); // buf3 == key name
p[0]=0;
if (hKey)
{
DWORD l = NSIS_MAX_STRLEN - 1;
DWORD t;
if (RegQueryValueEx(hKey,buf3,NULL,&t,p,&l) != ERROR_SUCCESS ||
(t != REG_DWORD && t != REG_SZ && t != REG_EXPAND_SZ))
{
p[0]=0;
exec_error++;
}
else
{
if (t==REG_DWORD)
{
exec_error += !parm4;
myitoa(p,*((DWORD*)p));
}
else
{
exec_error += parm4;
p[l]=0;
}
}
RegCloseKey(hKey);
}
else exec_error++;
}
关键就在红色这行。
另外最关键的是nsis的编译,采用的多字节字符集。
由于存入是UNICODE存入了LPBYTE的数据,而读取采用多字节方式读取,导致读出的l(字母L的小写)为0 。
----------------------------------------
5、验证一个问题:
UNICODE 下执行以下代码:
HKEY hKey;
if (RegOpenKeyEx(HKEY_CURRENT_USER,TEXT("SOFTWARE\\MyApp"),0,KEY_READ | KEY_WRITE,&hKey) != ERROR_SUCCESS)
{
cout <<1 <<endl;
return -1;
}
if (RegSetValueEx(hKey,TEXT("SomeData"),0,REG_SZ,(LPCBYTE)"1",1) == ERROR_SUCCESS)
{
RegCloseKey(hKey);
}
else
{
RegCloseKey(hKey);
}
多字节下通过以下代码,读出的长度是0 。
DWORD l = 20 - 1;
DWORD t;
unsigned char * p = new unsigned char[20];
p[0] = 0;
HKEY hKey;
int exec_error;
if (RegOpenKeyEx(HKEY_CURRENT_USER,TEXT("SOFTWARE\\MyApp"),0,KEY_READ | KEY_WRITE,&hKey) != ERROR_SUCCESS)
{
cout << 3<<endl;
return -1;
}
if (RegQueryValueEx(hKey,TEXT("SomeData"),NULL,&t,p,&l) != ERROR_SUCCESS ||
(t != REG_DWORD && t != REG_SZ && t != REG_EXPAND_SZ))
{
p[0]=0;
exec_error++;
cout << "err"<<endl;
}
else
{
if (t==REG_DWORD)
{
//exec_error += !parm4;
//myitoa(p,*((DWORD*)p));
cout << 4<<endl;
}
else
{
//exec_error += parm4;
p[l]=0;
cout <<"result "<<endl;
cout << l <<endl; // 这里输出0.
cout << p <<endl;// 这里就为空。也就是nsis读取不到数据的原因。虽然成功,但是数据为空。
}
}
从而可见,注册表编辑器也是UNICODE方式。
环境:win7 sp1.