为什么System::Call正在返回";0“;在32位机器上运行32位安装程序时,但在64位机器上运行时,NSIS中返回空白

为什么System::Call正在返回";0“;在32位机器上运行32位安装程序时,但在64位机器上运行时,NSIS中返回空白,nsis,Nsis,我已经使用NSIS创建了一个32位安装程序,它应该在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.

我已经使用NSIS创建了一个32位安装程序,它应该在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 $1 
  ${If} $0 = 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;
}
下面的代码在32位计算机中运行良好,它使用System::Call和相应的函数IsSplashScreenDisabled()调用DLL(Profile.DLL)。此函数返回false并显示消息框。它正在按预期工作

但当我在64位机器上运行同一个安装程序时,System::Call没有返回“0”,而是显示为空“”。所以我没有收到留言框

而且,如果我将“$PROGRAMFILES32”更改为“$PROGRAMFILES64”,那么它也将显示为空“”,而不显示消息框

因此,在这里我需要您的建议或想法来解释为什么System::Call在32位/64位安装程序中与在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 $1 
  ${If} $0 = 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
将路径设置为当前目录,而
adddldirectory
将目录添加到要从中加载的有效目录列表中

B)

手动加载.DLL并直接调用地址:

SetOutPath $InstDir
System::Call 'KERNEL32::AddDllDirectory(w "$INSTDIR")'
File MyFile.dll

System::Call 'KERNEL32::LoadLibrary(t "$InstDir\MyFile.dll")p.r1'
${If} $1 P<> 0
    System::Call 'KERNEL32::GetProcAddress(pr1,m "MyFunction")p.r2'
    ${If} $2 P<> 0
        System::Call '::$2()i.r0'
    ${EndIf}
    System::Call 'KERNEL32::FreeLibrary(pr1)'
${EndIf}
SetOutPath$InstDir
系统::调用'KERNEL32::AddDllDirectory(w“$INSTDIR”)'
文件MyFile.dll
系统::调用'KERNEL32::LoadLibrary(t“$InstDir\MyFile.dll”)p.r1'
${If}$1 P 0
系统::调用'KERNEL32::GetProcAddress(pr1,m“MyFunction”)p.r2'
${If}$2p0
系统::调用“::$2()i.r0”
${EndIf}
系统::调用“内核32::免费库(pr1)”
${EndIf}

你能发布一些关于Profile.dll的更多信息吗。您是否编写了IsSplashScreenDisabled?在Profile.dll中,我编写了函数IsSplashScreenDisabled。此函数返回false,因为我没有在注册表中注册它。我在上面的帖子中写过这个函数。请看一下,是stdcall还是cdecl?当没有参数时,这并不重要,但除非你能将sample.dll上传到某个地方,否则我可能不得不尝试制作自己的.Hi@Anders,这是cdecl函数。而且我正在使用u declspec(dllexport)导出此函数。即使我给出Profile.dll,它也不会工作,因为这个dll需要很多依赖dll。已经有一个NSIS错误报告,我会看看我能做些什么来修复它。