为什么 System::Call 在 32 位机器中 运行 32 位安装程序返回“0”,但在 64 位机器中 运行 它在 NSIS 中返回空白
Why System::Call is returning "0" when running 32-bit installer in 32-bit machine but when running it in 64-bit machine returning blank in NSIS
我已经使用 NSIS 创建了一个 32 位安装程序,在 32 位或 64 位机器上都应该 运行。
下面的代码在使用 System::Call 和相应函数 IsSplashScreenDisabled() 调用 DLL (Profile.dll) 的 32 位机器上运行良好。此函数 returns false 并显示消息框。它按预期工作。
但是当我在 64 位机器上 运行 时,相同的安装程序 System::Call 没有返回“0”,而是显示空白“”。所以我没有收到消息框。
而且如果我将“$PROGRAMFILES32”更改为“$PROGRAMFILES64”,那么它也会显示空白“”并且不显示消息框。
因此,我需要您的建议或想法,了解为什么 System::Call 在 32-bit/64-bit 安装程序和 32 位或 64 位机器上的工作方式不同。
!include LogicLib.nsh
InstallDir $PROGRAMFILES32\MyAppTest
Page components
Page directory
Page instfiles
UninstPage uninstConfirm
UninstPage instfiles
Section
SetOutPath $INSTDIR
File E:\TestNullSoft\Test.dll
System::Call 'KERNEL32::AddDllDirectory(w "$INSTDIR")'
System::Call "$INSTDIR\Test.dll::IsSplashDisabled() i.r0 ?e"
Pop
${If} [=13=] = 0
MessageBox MB_OK|MB_ICONEXCLAMATION "Splash s Disabled.$\r$\nRolling back the installation..." IDOK
${EndIf}
SectionEnd
BOOL IsSplashDisabled()
{
BOOL bResult = FALSE;
DWORD dwSplashScreen(0);
RegistryObj regObj(SETTINGS_REG_PATH);
if (regObj.Get(SPLASH_SCREEN, testSplashScreen))
{
bResult = (BOOL) !testSplashScreen;
}
return bResult;
}
System::Call
解析字符串的方式有缺陷,如果字符串包含(..)
它将解析为函数参数并且无法加载.DLL 并且调用失败。在 64 位 Windows 上,$ProgramFiles32 包含 (x86)
作为路径的一部分,这会触发漏洞。
有两种方法可以解决此问题:
A)
结合使用 SetOutPath
和 AddDllDirectory
以确保可以从相对路径加载 .DLL:
SetOutPath $InstDir
System::Call 'KERNEL32::AddDllDirectory(w "$INSTDIR")'
File MyFile.dll
System::Call 'MyFile::MyFunction()i.r0'
SetOutPath
使路径成为当前目录,AddDllDirectory
将该目录添加到要加载的有效目录列表中。
B)
手动加载.DLL,直接调用地址:
SetOutPath $InstDir
System::Call 'KERNEL32::AddDllDirectory(w "$INSTDIR")'
File MyFile.dll
System::Call 'KERNEL32::LoadLibrary(t "$InstDir\MyFile.dll")p.r1'
${If} P<> 0
System::Call 'KERNEL32::GetProcAddress(pr1,m "MyFunction")p.r2'
${If} P<> 0
System::Call '::()i.r0'
${EndIf}
System::Call 'KERNEL32::FreeLibrary(pr1)'
${EndIf}
我已经使用 NSIS 创建了一个 32 位安装程序,在 32 位或 64 位机器上都应该 运行。
下面的代码在使用 System::Call 和相应函数 IsSplashScreenDisabled() 调用 DLL (Profile.dll) 的 32 位机器上运行良好。此函数 returns false 并显示消息框。它按预期工作。
但是当我在 64 位机器上 运行 时,相同的安装程序 System::Call 没有返回“0”,而是显示空白“”。所以我没有收到消息框。
而且如果我将“$PROGRAMFILES32”更改为“$PROGRAMFILES64”,那么它也会显示空白“”并且不显示消息框。
因此,我需要您的建议或想法,了解为什么 System::Call 在 32-bit/64-bit 安装程序和 32 位或 64 位机器上的工作方式不同。
!include LogicLib.nsh
InstallDir $PROGRAMFILES32\MyAppTest
Page components
Page directory
Page instfiles
UninstPage uninstConfirm
UninstPage instfiles
Section
SetOutPath $INSTDIR
File E:\TestNullSoft\Test.dll
System::Call 'KERNEL32::AddDllDirectory(w "$INSTDIR")'
System::Call "$INSTDIR\Test.dll::IsSplashDisabled() i.r0 ?e"
Pop
${If} [=13=] = 0
MessageBox MB_OK|MB_ICONEXCLAMATION "Splash s Disabled.$\r$\nRolling back the installation..." IDOK
${EndIf}
SectionEnd
BOOL IsSplashDisabled()
{
BOOL bResult = FALSE;
DWORD dwSplashScreen(0);
RegistryObj regObj(SETTINGS_REG_PATH);
if (regObj.Get(SPLASH_SCREEN, testSplashScreen))
{
bResult = (BOOL) !testSplashScreen;
}
return bResult;
}
System::Call
解析字符串的方式有缺陷,如果字符串包含(..)
它将解析为函数参数并且无法加载.DLL 并且调用失败。在 64 位 Windows 上,$ProgramFiles32 包含 (x86)
作为路径的一部分,这会触发漏洞。
有两种方法可以解决此问题:
A)
结合使用 SetOutPath
和 AddDllDirectory
以确保可以从相对路径加载 .DLL:
SetOutPath $InstDir
System::Call 'KERNEL32::AddDllDirectory(w "$INSTDIR")'
File MyFile.dll
System::Call 'MyFile::MyFunction()i.r0'
SetOutPath
使路径成为当前目录,AddDllDirectory
将该目录添加到要加载的有效目录列表中。
B)
手动加载.DLL,直接调用地址:
SetOutPath $InstDir
System::Call 'KERNEL32::AddDllDirectory(w "$INSTDIR")'
File MyFile.dll
System::Call 'KERNEL32::LoadLibrary(t "$InstDir\MyFile.dll")p.r1'
${If} P<> 0
System::Call 'KERNEL32::GetProcAddress(pr1,m "MyFunction")p.r2'
${If} P<> 0
System::Call '::()i.r0'
${EndIf}
System::Call 'KERNEL32::FreeLibrary(pr1)'
${EndIf}