使用嵌入式签名而不是单独的卸载程序脚本创建签名的卸载程序

如何解决使用嵌入式签名而不是单独的卸载程序脚本创建签名的卸载程序

为了创建一个签名的卸载程序,我不能使用通常的 NSIS WriteUninstaller 命令来创建它,因为通常脚本会创建卸载程序并将其嵌入到安装程序中,所以没有机会对卸载程序进行签名.详情请参阅https://nsis-dev.github.io/NSIS-Forums/html/t-245688.html

这意味着我必须创建一个单独的卸载程序脚本,对其进行签名,然后使用普通的 File 命令将其嵌入到安装程序中。这适用于删除文件;但是,由于正在运行的卸载程序可执行文件,它无法删除自身,并留下 uninstaller.exeMyApp 目录。这是我的卸载程序脚本:

!include "LogicLib.nsh"
!include "Sections.nsh"
    
;Include Modern UI
!include "MUI2.nsh"

!define MAJOR_VERSION "1" 
!define MINOR_VERSION "2" 
!define PATCH_VERSION "3" 
!define BUILD_VERSION "4" 
    
!define APP_COPYRIGHT "MyApp © MyCompany 2021"
!define COMPANY_NAME "MyCompany"
!define FLEX_LM "FlexLM"        
!define FLEX_DIR "FlexSMyApp"            
!define LANG_ENGLSH "English" 
!define PRODUCT_NAME "MyApp"
!define PRODUCT_VERSION "${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION}.${BUILD_VERSION}"
!define SETUP_NAME "MyAppSetup.exe"

BrandingText "MyCompany"

OutFile ${SETUP_NAME}
Name "${PRODUCT_NAME}"

InstallDir "$PROGRAMFILES64\${PRODUCT_NAME}\"
InstallDirRegKey HKLM "Software\$PRODUCT_NAME" ""
ShowInstDetails hide
ShowUnInstDetails hide

SetCompressor /SOLID lzma
SetCompressorDictSize 12

Var MyAppInstallDir
Var FlexLmInstallDir

;Request application privileges for Windows 
RequestExecutionLevel admin

!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE "MyAppLicense.txt"

!define MUI_PAGE_CUSTOMFUNCTION_PRE SelectFilesCheck
!define MUI_PAGE_CUSTOMFUNCTION_LEAVE ComponentsLeave
!insertmacro MUI_PAGE_COMPONENTS   
 
## This is the title on the MyApp Directory page
!define MUI_DIRECTORYPAGE_TEXT_TOP "$(MUI_DIRECTORYPAGE_TEXT_TOP_A)"
!define MUI_PAGE_HEADER_SUBTEXT "Please choose the folder in which to install MyApp."
 
!define MUI_PAGE_CUSTOMFUNCTION_PRE SelectFilesA
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
 
## This is the title on the FlexLM Directory page
!define MUI_DIRECTORYPAGE_TEXT_TOP "$(MUI_DIRECTORYPAGE_TEXT_TOP_B)"
!define MUI_PAGE_HEADER_SUBTEXT "Please choose the folder in which to install FlexLM."
 
!define MUI_PAGE_CUSTOMFUNCTION_PRE SelectFilesB
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES

!define MUI_PAGE_CUSTOMFUNCTION_LEAVE DeleteSectionsINI
!insertmacro MUI_PAGE_FINISH

!insertmacro MUI_UNPAGE_WELCOME
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_UNPAGE_FINISH

;--------------------------------
;Languages

!insertmacro MUI_LANGUAGE "English"

;--------------------------------

LangString NoSectionsSelected ${LANG_ENGLSH} "You haven't selected any sections!"

LangString MUI_DIRECTORYPAGE_TEXT_TOP_A ${LANG_ENGLSH} "Setup will install \
${PRODUCT_NAME} in the following folder..."
LangString MUI_DIRECTORYPAGE_TEXT_TOP_B ${LANG_ENGLSH} "Setup will install \
${FLEX_LM} in the following folder..."

;--------------------------------
; Settings
 
!define PROG1_InstDir    "$PROGRAMFILES64\${PRODUCT_NAME}\"
!define PROG1_StartIndex ${SEC1}
!define PROG1_EndIndex   ${SEC1}
 
!define PROG2_InstDir "C:\${FLEX_DIR}"
!define PROG2_StartIndex ${SEC3}
!define PROG2_EndIndex   ${SEC3}

;--------------------------------
; Function
; StrContains
; This function does a case sensitive searches for an occurrence of a substring in a string. 
; It returns the substring if it is found. 
; Otherwise it returns null(""). 
; Written by kenglish_hi
; Adapted from StrReplace written by dandaman32
 
 
Var STR_HAYSTACK
Var STR_NEEDLE
Var STR_CONTAINS_VAR_1
Var STR_CONTAINS_VAR_2
Var STR_CONTAINS_VAR_3
Var STR_CONTAINS_VAR_4
Var STR_RETURN_VAR
 
Function StrContains
  Exch $STR_NEEDLE
  Exch 1
  Exch $STR_HAYSTACK
  ; Uncomment to debug
  ;MessageBox MB_OK 'STR_NEEDLE = $STR_NEEDLE STR_HAYSTACK = $STR_HAYSTACK '
    StrCpy $STR_RETURN_VAR ""
    StrCpy $STR_CONTAINS_VAR_1 -1
    StrLen $STR_CONTAINS_VAR_2 $STR_NEEDLE
    StrLen $STR_CONTAINS_VAR_4 $STR_HAYSTACK
    loop:
      IntOp $STR_CONTAINS_VAR_1 $STR_CONTAINS_VAR_1 + 1
      StrCpy $STR_CONTAINS_VAR_3 $STR_HAYSTACK $STR_CONTAINS_VAR_2 $STR_CONTAINS_VAR_1
      StrCmp $STR_CONTAINS_VAR_3 $STR_NEEDLE found
      StrCmp $STR_CONTAINS_VAR_1 $STR_CONTAINS_VAR_4 done
      Goto loop
    found:
      StrCpy $STR_RETURN_VAR $STR_NEEDLE
      Goto done
    done:
   Pop $STR_NEEDLE ;Prevent "invalid opcode" errors and keep the
   Exch $STR_RETURN_VAR  
FunctionEnd
 
!macro _StrContainsConstructor OUT NEEDLE HAYSTACK
  Push `${HAYSTACK}`
  Push `${NEEDLE}`
  Call StrContains
  Pop `${OUT}`
!macroend
 
!define StrContains '!insertmacro "_StrContainsConstructor"'

;--------------------------------
; Start sections

Section "MyApp" SEC1
    ;MessageBox MB_OK "SEC1 #1 INSTDIR is $INSTDIR"
    ${StrContains} $0 "MyApp" "$INSTDIR"
    ;MessageBox MB_OK "SEC1 #2 0 is $0"
    StrCmp $0 "" notfoundMyApp
      StrCpy $MyAppInstallDir "$INSTDIR"
      ;MessageBox MB_OK "SEC1 #3 MyAppInstallDir is $MyAppInstallDir"
      Goto doneMyApp
    notfoundMyApp:
      ;MessageBox MB_OK 'Did not find MyApp string'
    doneMyApp:  

    ${StrContains} $0 "Flex" "$INSTDIR"
    StrCmp $0 "" notfoundFlex
      StrCpy $FlexLmInstallDir "$INSTDIR"
      ;MessageBox MB_OK "SEC1 #4 FlexLmInstallDir is $FlexLmInstallDir"
      Goto doneFlex
    notfoundFlex:
      ;MessageBox MB_OK 'Did not find Flex string'    

    ##All the files in Group 1 will be installed to the same location,$INSTDIR
    SetOutPath "$INSTDIR"

    File MyApp.exe
    File ReleaseNotes.txt
    File MyCompany_LandingPage_114.bmp
    File MyAppLicense.txt  
  
    # create a shortcut named "new shortcut" in the start menu programs directory
    CreateShortcut "$SMPROGRAMS\${PRODUCT_NAME}.lnk" "$InstDir\${PRODUCT_NAME}.exe" 

    # Add application to registry  
    ClearErrors
    WriteRegStr HKCU "SOFTWARE\${PRODUCT_NAME}" 'Company Name' "${COMPANY_NAME}"
    WriteRegStr HKCU "SOFTWARE\${PRODUCT_NAME}" 'Version' "${PRODUCT_VERSION}"
    WriteRegStr HKCU "SOFTWARE\${PRODUCT_NAME}" 'AppID' "{A0E84732-E2B2-46E5-8CA2-462B8DF92DCD}"
 
    # Add program to Add/Remove programs 
    WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
                   "DisplayIcon" "$PROGRAMFILES64\${PRODUCT_NAME}\${PRODUCT_NAME}.exe"
    WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
                   "AppID" "{A0E84732-E2B2-46E5-8CA2-462B8DF92DCD}"              
    WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
                   "DisplayName" "${PRODUCT_NAME}"
    WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
                   "DisplayVersion" "${PRODUCT_VERSION}"                 
    WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
                   "InstallLocation" "$INSTDIR"                      
    WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
                   "Publisher" "${COMPANY_NAME}"                 
    WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
                     "UninstallString" "$\"$INSTDIR\uninstaller.exe$\""

!ifndef INNER
  SetOutPath $INSTDIR
 
  ; this packages the signed uninstaller
 
  File $%TEMP%\uninstaller.exe
!endif        
    doneFlex:   

  # messagebox mb_ok sec1
SectionEnd

Section "FlexLM" SEC3
    ;MessageBox MB_OK "SEC3 #1 INSTDIR is $INSTDIR"

    ;MessageBox MB_OK "SEC1 #1 INSTDIR is $INSTDIR"
    ${StrContains} $0 "Pro" "$INSTDIR"
    ;MessageBox MB_OK "SEC1 #2 0 is $0"
    StrCmp $0 "" notfoundMyApp
      StrCpy $MyAppInstallDir "$INSTDIR"
      ;MessageBox MB_OK "SEC1 #3 MyAppInstallDir is $MyAppInstallDir"
      Goto doneMyApp
    notfoundMyApp:
      ;MessageBox MB_OK 'Did not find MyApp string'

    ${StrContains} $0 "Flex" "$INSTDIR"
    StrCmp $0 "" notfoundFlex
      StrCpy $FlexLmInstallDir "$INSTDIR"
      ;MessageBox MB_OK "SEC1 #4 FlexLmInstallDir is $FlexLmInstallDir"
      Goto doneFlex
    notfoundFlex:
      ;MessageBox MB_OK 'Did not find Flex string'
    doneFlex:     

    ##All the files in Group 2 will be installed to the same location,$INSTDIR
    SetOutPath "$INSTDIR"
    File installs.exe
    File lmdown.exe
    File lmflex.exe
    
    # define uninstaller name
    WriteUninstaller $INSTDIR\uninstaller.exe
    doneMyApp: 

  # messagebox mb_ok sec3
 
SectionEnd

;--------------------------------
;Descriptions

  ;Language strings
  LangString DESC_SecMyApp ${LANG_ENGLISH} "MyAppTM software is an easy-to-use suite of tools."
  LangString DESC_SecFlexLM ${LANG_ENGLISH} "FlexSMyApp contains all the files necessary to implement the FlexLM license server."

  ;Assign language strings to sections
  !insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN    
    !insertmacro MUI_DESCRIPTION_TEXT ${SEC1} $(DESC_SecMyApp)
    !insertmacro MUI_DESCRIPTION_TEXT ${SEC3} $(DESC_SecFlexLM)
  !insertmacro MUI_FUNCTION_DESCRIPTION_END

; ...
 
Function .onInit
!ifdef INNER
 
  ; If INNER is defined,then we aren't supposed to do anything except write out
  ; the uninstaller.  This is better than processing a command line option as it means
  ; this entire code path is not present in the final (real) installer.
  SetSilent silent
  WriteUninstaller "$%TEMP%\uninstaller.exe"
  Quit  ; just bail out quickly when running the "inner" installer
!endif
      
  ## Create $PLUGINSDIR 
  InitPluginsDir

  SetOutPath $TEMP
  File /oname=spltmp.bmp "MyCompany_LandingPage_114.bmp"

  splash::show 2000 $TEMP\spltmp

  Pop $0 ; $0 has '1' if the user closed the splash screen early,; '0' if everything closed normally,and '-1' if some error occurred.

  Delete $TEMP\spltmp.bmp  
FunctionEnd
 
; ...
 
Section "Files" ; or whatever
 
; ...
 
  ; where you would normally put WriteUninstaller ${INSTDIR}\uninstaller.exe put instead:
 
;!ifndef INNER
    ;  SetOutPath $INSTDIR
 
  ; this packages the signed uninstaller
 
    ;  File $%TEMP%\uninstaller.exe
;!endif
 
; ...
SectionEnd
 
!ifdef INNER
Section "Uninstall"  

  ; your normal uninstaller section or sections (they're not needed in the "outer"
  ; installer and will just cause warnings because there is no WriteUninstaller command)
  # Always delete uninstaller first
  Delete $INSTDIR\uninstaller.exe
 
  # now delete installed files and registry keys for MyApp
  Delete $INSTDIR\config.dat
  Delete $INSTDIR\MyApp.exe
  Delete $INSTDIR\ReleaseNotes.txt
  Delete $INSTDIR\MyCompany_LandingPage_114.bmp
  Delete $INSTDIR\MyAppLicense.txt
  Delete "$SMPROGRAMS\MyApp.lnk"
  DeleteRegKey HKCU "SOFTWARE\${PRODUCT_NAME}"
  DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
  DeleteRegKey /ifempty HKCU "Software\Modern UI Test"     
        
  # now delete installed files and registry keys for FlexLM
  Delete $INSTDIR\installs.exe
  Delete $INSTDIR\lmborrow.exe
  Delete $INSTDIR\lmflex.exe

  # Delete the MyApp and FlexLM directories
  RMDir $INSTDIR

SectionEnd
!endif

;--------------------------------
; Please don`t modify below here unless you`re a NSIS 'wiz-kid'
 
## Also if no sections are selected,warn the user!
Function ComponentsLeave
Push $R0
Push $R1
 
 Call IsPROG1Selected
  Pop $R0
 Call IsPROG2Selected
  Pop $R1
 StrCmp $R0 1 End
 StrCmp $R1 1 End
  Pop $R1
  Pop $R0
 MessageBox MB_OK|MB_ICONEXCLAMATION "$(NoSectionsSelected)"
 Abort
 
End:
Pop $R1
Pop $R0
FunctionEnd
    
Function IsPROG1Selected
Push $R0

 StrCpy $R0 ${PROG1_StartIndex} # Group 1 start
 
   SectionGetFlags 0 $R0            # Get section flags
    IntOp $R0 $R0 & ${SF_SELECTED}
    StrCmp $R0 ${SF_SELECTED} 0 +3      # If section is selected,done
     StrCpy $R0 1
 
Exch $R0
FunctionEnd
 
Function IsPROG2Selected
Push $R1
 
 StrCpy $R1 ${PROG2_StartIndex}    # Group 2 start
 
   IntOp $R1 $R1 + 1
   SectionGetFlags 1 $R1            # Get section flags
    IntOp $R1 $R1 & ${SF_SELECTED}
    StrCmp $R1 ${SF_SELECTED} 0 +3      # If section is selected,done
     StrCpy $R1 1
 
Exch $R1
FunctionEnd

## Here we are selecting first sections to install
## by unselecting all the others!
Function SelectFilesA
  ${If} ${SectionIsSelected} ${SEC1}
    ;MessageBox MB_OK "SEC1 #1 INSTDIR is $INSTDIR"
  ${Else}  
      Abort
  ${EndIf}  
 
 # If user clicks Back now,we will know to reselect Group 2`s sections for
 # Components page
 StrCpy $IfBack 1
 
 # We need to save the state of the Group 2 Sections
 # for the next InstFiles page
Push $R0
Push $R1
 
 StrCpy $R0 ${PROG2_StartIndex} # Group 2 start
 
;  Loop:
;   IntOp $R0 $R0 + 1
;   SectionGetFlags $R0 $R1                 # Get section flags
;    WriteINIStr "$PLUGINSDIR\sections.ini" Sections $R0 $R1 # Save state
;    !insertmacro UnselectSection $R0               # Then unselect it
;    StrCmp $R0 ${PROG2_EndIndex} 0 Loop
 
 # Don`t install prog 1?
 Call IsPROG1Selected
 Pop $R0
 StrCmp $R0 1 +4
  Pop $R1
  Pop $R0
  Abort
 
 # Set current $INSTDIR to PROG1_InstDir define
 StrCpy $INSTDIR "${PROG1_InstDir}"
 
Pop $R1
Pop $R0
FunctionEnd
 
## Here we need to unselect all Group 1 sections
## and then re-select those in Group 2 (that the user had selected on
## Components page)
Function SelectFilesB
  ${If} ${SectionIsSelected} ${SEC3}
    ;MessageBox MB_OK "SEC1 #3 INSTDIR is $INSTDIR"
  ${Else}  
      Abort
  ${EndIf} 

Push $R0
;Push $R1
 
 StrCpy $R0 ${PROG1_StartIndex}    # Group 1 start
 
;  Loop:
;   IntOp $R0 $R0 + 1
;    !insertmacro UnselectSection $R0       # Unselect it
;    StrCmp $R0 ${PROG1_EndIndex} 0 Loop
 
; Call ResetFiles
 
 # Don't install prog 2?
 Call IsPROG2Selected
 Pop $R0
 StrCmp $R0 1 +4
  Pop $R1
  Pop $R0
  Abort
 
 # Set current $INSTDIR to PROG2_InstDir define
 StrCpy $INSTDIR "${PROG2_InstDir}"
 
;Pop $R1
Pop $R0
FunctionEnd

大家有什么建议吗?

更新:

谢谢@Anders,我不知何故误删除了SelectFilesCheck。现在,如果我使用此代码:

!ifdef INNER
  !echo "Inner invocation"                  ; just to see what's going on
  OutFile "$%TEMP%\tempinstaller.exe"       ; not really important where this is
  SetCompress off                           ; for speed
!else
  !echo "Outer invocation"
 
  ; Call makensis again against current file,defining INNER.  This writes an installer for us which,when
  ; it is invoked,will just write the uninstaller to some location,and then exit.
 
  !makensis '/DINNER "${__FILE__}"' = 0
 
  ; So now run that installer we just created as %TEMP%\tempinstaller.exe.  Since it
  ; calls quit the return value isn't zero.
 
  !system 'set __COMPAT_LAYER=RunAsInvoker&"$%TEMP%\tempinstaller.exe"' = 2
 
  ; That will have written an uninstaller binary for us.  Now we sign it with your
  ; favorite code signing tool. 
  nsExec::ExecToStack 'cmd /c "SMyApp-SignTool.bat $%TEMP%\uninstaller.exe"'
 
  ; Good.  Now we can carry on writing the real installer.
 
  OutFile ${SETUP_NAME}
  SetCompressor /SOLID lzma
!endif
 
Section "MyApp" SEC1

    ;MessageBox MB_OK "SEC1 #1 INSTDIR is $INSTDIR"
    ${StrContains} $0 "MyApp" "$INSTDIR"
    ;MessageBox MB_OK "SEC1 #2 0 is $0"
    StrCmp $0 "" notfoundMyApp
      StrCpy $QiProInstallDir "$INSTDIR"
      ;MessageBox MB_OK "SEC1 #3 QiProInstallDir is $QiProInstallDir"
      Goto doneMyApp
    notfoundMyApp:
      ;MessageBox MB_OK 'Did not find MyApp string'
    doneMyApp:  

    ${StrContains} $0 "Flex" "$INSTDIR"
    StrCmp $0 "" notfoundFlex
      StrCpy $FlexLmInstallDir "$INSTDIR"
      ;MessageBox MB_OK "SEC1 #4 FlexLmInstallDir is $FlexLmInstallDir"
      Goto doneFlex
    notfoundFlex:
      ;MessageBox MB_OK 'Did not find Flex string'

    ##All the files in Group 1 will be installed to the same location,$INSTDIR
    SetOutPath "$INSTDIR"

    # specify files to go in output path
    File config.dat
    File MyApp.exe
    File ReleaseNotes.txt
    File MyApp_LandingPage_114.bmp
    File MyAppLicense.txt  
  
    # create a shortcut named "new shortcut" in the start menu programs directory
    CreateShortcut "$SMPROGRAMS\${PRODUCT_NAME}.lnk" "$InstDir\${PRODUCT_NAME}.exe" 

    # Add application to registry  
    ClearErrors
    WriteRegStr HKCU "SOFTWARE\${PRODUCT_NAME}" 'Contact' "https://www.mycompany.com/contact"
    WriteRegStr HKCU "SOFTWARE\${PRODUCT_NAME}" 'Company Name' "${COMPANY_NAME}"
    WriteRegStr HKCU "SOFTWARE\${PRODUCT_NAME}" 'DisplayName' "${PRODUCT_NAME}"
    WriteRegStr HKCU "SOFTWARE\${PRODUCT_NAME}" 'DisplayVersion' "${PRODUCT_VERSION}"
    WriteRegStr HKCU "SOFTWARE\${PRODUCT_NAME}" 'AppID' "{K2C40732-E2B2-46E5-8CA2-464L9DF92DCD}"
    WriteRegStr HKCU "SOFTWARE\${PRODUCT_NAME}" 'HelpLink' "http://www.mycompany.com/myapp/HelpDocs/index.htm"
    WriteRegStr HKCU "SOFTWARE\${PRODUCT_NAME}" 'URLInfoAbout' "https://www.mycompany.com/myapp"
    WriteRegStr HKCU "SOFTWARE\${PRODUCT_NAME}" 'InstallLocation' "$MyAppInstallDir"    
    WriteRegStr HKCU "SOFTWARE\${PRODUCT_NAME}" 'Publisher' "${COMPANY_NAME}"
 
    # Add program to Add/Remove programs 
    WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
                     "DisplayIcon" "$PROGRAMFILES64\${PRODUCT_NAME}\${PRODUCT_NAME}.exe"
    WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
                   "AppID" "{K2C40732-E2B2-46E5-8CA2-464L9DF92DCD}"              
    WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
                     "DisplayName" "${PRODUCT_NAME}"
    WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
                     "DisplayVersion" "${PRODUCT_VERSION}"               
    WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
                     "InstallLocation" "$INSTDIR"                        
    WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
                     "Publisher" "${COMPANY_NAME}"               
    WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
                     "UninstallString" "$\"$INSTDIR\uninstaller.exe$\"" 
    
    # Delete splash page image
    Delete $INSTDIR\MyApp_LandingPage_114.bmp           

!ifndef INNER
  ;SetOutPath $INSTDIR
 
  ; this packages the signed uninstaller
 
  File $%TEMP%\uninstaller.exe
!endif

然后我收到这些错误:

!makensis: returned 0
!system: "set __COMPAT_LAYER=RunAsInvoker&"C:\Users\moorer\AppData\Local\Temp\tempinstaller.exe""
!system: returned 2
Error: Can't add entry,no section or function is open!
Error in script "C:\Project\MsiPackaging\Installer\MyAppInstaller.nsi" on line 169 -- aborting creation process

但是如果我试着把它放在这样的部分:

;--------------------------------
; Start sections
 

Section "MyApp" SEC1
!ifdef INNER
  !echo "Inner invocation"                  ; just to see what's going on
  OutFile "$%TEMP%\tempinstaller.exe"       ; not really important where this is
  SetCompress off                           ; for speed
!else
  !echo "Outer invocation"
 
  ; Call makensis again against current file,and then exit.
 
  !makensis '/DINNER "${__FILE__}"' = 0
 
  ; So now run that installer we just created as %TEMP%\tempinstaller.exe.  Since it
  ; calls quit the return value isn't zero.
 
  !system 'set __COMPAT_LAYER=RunAsInvoker&"$%TEMP%\tempinstaller.exe"' = 2
 
  ; That will have written an uninstaller binary for us.  Now we sign it with your
  ; favorite code signing tool. 
  nsExec::ExecToStack 'cmd /c "SQI-SignTool.bat $%TEMP%\uninstaller.exe"'
 
  ; Good.  Now we can carry on writing the real installer.
 
  OutFile ${SETUP_NAME}
  SetCompressor /SOLID lzma
!endif
    
    ;MessageBox MB_OK "SEC1 #1 INSTDIR is $INSTDIR"
    ${StrContains} $0 "MyApp" "$INSTDIR"
    ;MessageBox MB_OK "SEC1 #2 0 is $0"
    StrCmp $0 "" notfoundQI
      StrCpy $QiProInstallDir "$INSTDIR"
      ;MessageBox MB_OK "SEC1 #3 QiProInstallDir is $QiProInstallDir"
      Goto doneQI
    notfoundQI:
      ;MessageBox MB_OK 'Did not find QI string'
    doneQI: 

    ${StrContains} $0 "Flex" "$INSTDIR"
    StrCmp $0 "" notfoundFlex
      StrCpy $FlexLmInstallDir "$INSTDIR"
      ;MessageBox MB_OK "SEC1 #4 FlexLmInstallDir is $FlexLmInstallDir"
      Goto doneFlex
    notfoundFlex:
      ;MessageBox MB_OK 'Did not find Flex string'

    ##All the files in Group 1 will be installed to the same location,$INSTDIR
    SetOutPath "$INSTDIR"

    # specify files to go in output path
    File config.dat
    File MyApp.exe
    File ReleaseNotes.txt
    File MyApp_LandingPage_114.bmp
    File MyAppLicense.txt  
  
    # create a shortcut named "new shortcut" in the start menu programs directory
    CreateShortcut "$SMPROGRAMS\${PRODUCT_NAME}.lnk" "$InstDir\${PRODUCT_NAME}.exe" 

    # Add application to registry  
    ClearErrors
    WriteRegStr HKCU "SOFTWARE\${PRODUCT_NAME}" 'Contact' "https://www.mycompany.com/contact"
    WriteRegStr HKCU "SOFTWARE\${PRODUCT_NAME}" 'Company Name' "${COMPANY_NAME}"
    WriteRegStr HKCU "SOFTWARE\${PRODUCT_NAME}" 'DisplayName' "${PRODUCT_NAME}"
    WriteRegStr HKCU "SOFTWARE\${PRODUCT_NAME}" 'DisplayVersion' "${PRODUCT_VERSION}"
    WriteRegStr HKCU "SOFTWARE\${PRODUCT_NAME}" 'AppID' "{K2C40732-E2B2-46E5-8CA2-464L9DF92DCD}"
    WriteRegStr HKCU "SOFTWARE\${PRODUCT_NAME}" 'HelpLink' "http://www.mycompany.com/myapp/HelpDocs/index.htm"
    WriteRegStr HKCU "SOFTWARE\${PRODUCT_NAME}" 'URLInfoAbout' "https://www.mycompany.com/myapp"
    WriteRegStr HKCU "SOFTWARE\${PRODUCT_NAME}" 'InstallLocation' "$MyAppInstallDir"    
    WriteRegStr HKCU "SOFTWARE\${PRODUCT_NAME}" 'Publisher' "${COMPANY_NAME}"
 
    # Add program to Add/Remove programs 
    WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
                     "DisplayIcon" "$PROGRAMFILES64\${PRODUCT_NAME}\${PRODUCT_NAME}.exe"
    WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
                   "AppID" "{K2C40732-E2B2-46E5-8CA2-464L9DF92DCD}"              
    WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
                     "DisplayName" "${PRODUCT_NAME}"
    WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
                     "DisplayVersion" "${PRODUCT_VERSION}"               
    WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
                     "InstallLocation" "$INSTDIR"                        
    WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
                     "Publisher" "${COMPANY_NAME}"               
    WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
                     "UninstallString" "$\"$INSTDIR\uninstaller.exe$\"" 

    # Delete splash page image
    Delete $INSTDIR\MyApp_LandingPage_114.bmp           

!ifndef INNER
  ;SetOutPath $INSTDIR
 
  ; this packages the signed uninstaller
 
  File $%TEMP%\uninstaller.exe
!endif

    doneFlex:   

  # messagebox mb_ok sec1
SectionEnd

然后我得到这个错误:

Error: command OutFile not valid in Section
Error in script "MyAppInstaller.nsi" on line 157 -- aborting creation process
!makensis: returned 1,aborting
Error in script "C:\Projects\MsiPackaging\Installer\MyAppInstaller.nsi" on line 165 -- aborting creation process

你有什么建议吗? TIA。

解决方法

来自 Wiki 的签名代码根本不使用 StrCpy,它必须来自您的 StrCpy $IfBack 1 行。也许您忘记了函数上方的 Var IfBack

code from the wiki 应该可以工作,但这里有一个替代版本:

#TODO: RequestExecutionLevel
#TODO: InstallDir
!include MUI2.nsh

!macro WriteSignedUninstaller Destination
!makensis '"/DGENRATINGUNINST=$%TEMP%\Uninst.exe" "${__FILE__}" "/XOutfile `$%TEMP%\tempinstaller.exe`"' = 0 ; Create fake installer
!system 'set __COMPAT_LAYER=RunAsInvoker&"$%TEMP%\tempinstaller.exe"' = 2 ; Run fake installer to generate the uninstaller
!system 'SIGNCODE <signing options> "$%TEMP%\Uninst.exe"' = 0 ; Change this line. As a demonstration,use !system 'echo Dummy >> "$%TEMP%\Uninst.exe"'
File "/oname=${Destination}" "$%TEMP%\Uninst.exe"
!macroend

!macro DeclareLanguages
!insertmacro MUI_LANGUAGE English
!macroend


!ifndef GENRATINGUNINST
# Installer:
############
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
!insertmacro DeclareLanguages

Section
    SetOutPath $InstDir
    !insertmacro WriteSignedUninstaller "$InstDir\Uninst.exe"
    ; File MyApp.exe
SectionEnd

!else
# Uninstaller:
##############
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro DeclareLanguages

!verbose push 2
SilentInstall Silent
Section
WriteUninstaller "${GENRATINGUNINST}"
Quit
SectionEnd
!verbose pop

Section -Uninstall
    ; Delete "$InstDir\MyApp.exe"
    Delete "$InstDir\Uninst.exe"
    RMDir "$InstDir"
SectionEnd

!endif # EOF
,

感谢您的反馈,@Anders。最后,我找到了这个 SelfDel 插件,它允许我使用单独的安装程序和卸载程序脚本,同时仍然删除卸载程序:

https://nsis.sourceforge.io/SelfDel_plug-in

Name `SelfDel plug-in example`
OutFile Example.exe
XPStyle on

; Administrator privileges required.
RequestExecutionLevel admin

Page InstFiles

Section
  ;SelfDel::Del /RMDIR
  ;SelfDel::Del /REBOOT
  ;SelfDel::Del /SHUTDOWN
  SelfDel::Del
  SetAutoClose true
SectionEnd

更新:

当我使用 use SelfDel 时,它会删除 uninstaller.exe 但即使我使用 SelfDel::Del /RMDIR 也不会删除原始应用目录,所以我最终选择了上面的 Anders 答案.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res