NSIS学习笔记-使用C++开发NSIS插件

S1:新建一个空的C++DLL项目,nsMessageBoxPlugin.

S2:复制“C:\Program Files (x86)\NSIS\Unicode\Examples\Plugin\nsis”文件夹到

当前solution下。

S3:当前project头文件和库文件搜索路径设为“$(SolutionDir)nsis;”

S4:把nsis中的头文件加到当前project中。

S5:为当前project添加nsMessageBoxPlugin.c文件。源文件清单如下。

#include <windows.h>
#include <pluginapi.h> // nsis plugin
HWND g_hwndParent;

// To work with Unicode version of NSIS, please use TCHAR-type
// functions for accessing the variables and the stack.
void __declspec(dllexport) myFunction(HWND hwndParent, int string_size,
	TCHAR *variables, stack_t **stacktop,
	extra_parameters *extra)
{
	g_hwndParent = hwndParent;

	EXDLL_INIT();
	
	//读取输入参数
	WCHAR szComponent[256];
	popstring(szComponent);

	//打印参数
	WCHAR buf[1024];
	//这里能正确打印出来自NSIS的中文信息。
	wsprintf(buf, L"kagula $0=[%s][中文测试]\n", szComponent);
	MessageBoxW(g_hwndParent, buf, 0, MB_OK);

	//准备返回参考
	int len = (int)wcslen(szComponent);
	// make a little change to input parameter
	// below shift a unit character.
	for (int i = 0; i < len; ++i)
		szComponent[i] += 1;

	// push back on the stack
	pushstring(szComponent);
	
}

/*
nsMessageBoxPlugin::myFunction "abcdefg[来自NSIS的中文测试]"
Pop $0
MessageBox MB_OK "ret = $0"
#打印来自C++的中文会乱码。
*/

S6:把project生成的“nsMessageBoxPlugin.dll”复制到“C:\Program Files (x86)\NSIS\Unicode\Plugins”路径下。

S7:现在运行NSIS脚本应该能看到效果了。

S8:自动复制动态链接库免得每次编译都要手动复制一遍(要求关闭操作系统的UAC)。

把下面三行代码复制到“[Configuration Properties]->[BuildEvents]->[Post Build Event]”

echo on
copy “$(OutDir)$(TargetName)$(TargetExt)” “C:\Program Files (x86)\NSIS\Unicode\Plugins”
echo off

Q 桌面快捷方式,示例

在section “install”中添加下面的代码,新建shortcut

CreateShortcut “$DESKTOP\${APPNAME}.lnk” “$INSTDIR\${APPEXENAME}”

在section “uninstall”中添加下面的代码,删除shortcut

delete “$DESKTOP\${APPNAME}.lnk”

Q 关闭正在运行的程序,示例

有两种方法,最常用的是下面这种方法

Function CloseProgram
  StrCpy $1 0
  loop:
    FindWindow $0 "${WinClassName}" ${MainExeWindowText}
    IntCmp $0 0 done
;      SendMessage $0 ${WM_DESTROY} 0 0
;      SendMessage $0 ${WM_CLOSE} 0 0
       SendMessage $0 ${WM_QUIT} 0 0
    Sleep 100 
    IntOp $1 $1 + 1 
    IntCmp $1 3 done
    Goto loop 
  done:
  
  ${nsProcess::FindProcess} ${APPEXENAME} $R0  
  ${If} $R0 == "0"  
        # it's running  
        MessageBox MB_OK "你的程序无法自动关闭,请先关闭${APPNAME}程序,再启动安装卸载程序!"  
        Quit  
  ${EndIf}
FunctionEnd

Q 判断己安装程序版本,示例

我使用的方法是:

利用NSIS官网中的VersionCompare函数,比较EXE中的版本。

Function VerCheck  
pop $0
${GetFileVersion} "$0" $VersionNumber  
FunctionEnd  

 ReadRegStr $0 HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "MainProgramLocation"  
    ${If} $0 != ""  
      ${If} ${FileExists} $0 
       push $0 
        Call VerCheck  
        ${VersionCompare} $VersionNumber ${VERSIONLONG} $R0  
        ${if} $R0 == "1"  
        ${Endif}
        ${if} $R0 == "0"  
        ${Endif}
        ${if} $R0 == "2"  
        ${Endif}
     ${EndIf}  
    ${EndIf}

Q 设置升级安装路径为上次的安装路径

从注册表中读取上次的安装路径

ReadRegStr $0 HKLM “Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}” “MainProgramLocationPath”

在安装section中把安装路径写进去

WriteRegStr HKLM “Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}” “MainProgramLocationPath” “$INSTDIR”

Q 如何使用变量,示例

声明

Var IsSkipCustom

初始化

Strcpy $IsSkipCustom 0

修改值

Strcpy $IsSkipCustom 1

判断

IntCmp $IsSkipCustom 1 skipCustom
;这里若干行代码
skipCustom:
;不等于1就跳到这里来了。

Q 如何保留用户原来的数据

#reserve config file.
    CreateDirectory "C:\Temp20150606151700\config"
    CopyFiles "$INSTDIR\config\catconfig_w.ini" "C:\Temp20150606151700\config"    
   
    # Try to remove the install directory - this will only happen if it is empty  
    rmDir /r $INSTDIR
    
    #reserve config file.
    CreateDirectory $INSTDIR
    CreateDirectory "$INSTDIR\config"    
    CopyFiles  "C:\Temp20150606151700\config\catconfig_w.ini" "$INSTDIR\config"
    rmDir /r "C:\Temp20150606151700"

Q 如何注册控件

RegDLL "$INSTDIR\XXX.ocx"
UnRegDLL "$INSTDIR\XXX.ocx"

Q 如何安装MSXML

# Install MXSML4
    IfFileExists "$SYSDIR\msxml4.dll" file_found file_not_found
    file_found:
     goto continue
    file_not_found:
        SetOutPath $SYSDIR
        SetOverwrite off    
        File msxml4.dll
        ;File msxml4a.dll
        File msxml4r.dll
        RegDLL $SYSDIR\msxml4.dll    
    continue:

或采用下面的代码,quiet方式运行xml4的msi安装包。

File msxmlchs.msi
    ExecWait "msiexec /quiet /i msxmlchs.msi"

如何判断已经装过的msi软件,guid可以从老的msi里去拿

ReadRegStr $0 HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\{36B84F1C-C2C0-4B62-8643-98B3F4DAC8BB}" "DisplayName"
    ${If} $0 != ""
   ;       MessageBox MB_OK "你已经安装${APPNAME}v1.2.0软件,这个版本太旧需要你手动卸载才能安装${VERSIONLONG}版本软件,按确定退出安装!"
       push "已安装老版本的客户端,点击确定升级到最新版。"
       Call MyMessageBox1
       ExecWait "msiexec /x {36B84F1C-C2C0-4B62-8643-98B3F4DAC8BB} /passive" $0
    ${EndIf}

StrCpy如何当中加个空格?

如何调用静默卸载?

#自动卸载上一个打包版本.begin   
   ReadRegStr $0 HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "MainProgramLocationPath"     
   ${If} $0 != ""
       Strcpy $1 $0\uninstall.exe
       #下面的Call MyMessageBox1会修改$0全局变量,所以$0要先保存到$1中。
       push "检查到老版本的安装程序,按确定自动卸载"
       Call MyMessageBox1
       #为uninstall.exe加一个/S参数是为了静默卸载。
       Strcpy $1 '$1 /S'
       ExecWait $1
   ${EndIf}
   #自动卸载上一个打包版本.end

如何防止启动多个安装实例

在Function .onInit中使用一面代码段

System::Call 'kernel32::CreateMutexA(i 0, i 0, t "Cat8637All") i .r1 ?e'
    Pop $R0
    StrCmp $R0 0 +4
    push "安装程序已经启动!"
    Call MyMessageBox1
    Abort
© 版权声明
THE END
喜欢就支持一下吧
点赞9 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容