Set environment variables with NSIS in Window 7

前端 未结 3 711
温柔的废话
温柔的废话 2021-01-29 03:39

I want to set environment variable using NSIS installer. I will run script on Windows 7, if it is important. Thanks!

3条回答
  •  后悔当初
    2021-01-29 04:37

    UPDATE: The latest version of the RegAddPathToVar function is here: https://sf.net/p/nsisplus (full test example: https://sf.net/p/nsisplus/NsisSetupLib/HEAD/tree/trunk/tests/test_RegAddRemovePathGUI/main.nsi)

    Here is full and direct implementation example for the NSIS 3.0.

    Consist of 3 files.

    build.bat - build script file.

    @echo off
    
    set "MAKENSIS_EXE=makensis.exe"
    
    "%MAKENSIS_EXE%" /V4 "/Obuild.log" "/XOutFile '%~dp0test.exe'" "%~dp0main.nsi"
    
    echo.Return code: %ERRORLEVEL%
    
    pause
    

    Edit the MAKENSIS_EXE variable to there the makensis executable is on your system.

    stack.nsi - helper functions file.

    !define PushStack11 "!insertmacro PushStack11"
    !macro PushStack11 var0 var1 var2 var3 var4 var5 var6 var7 var8 var9 var10
    Push `${var0}`
    Push `${var1}`
    Push `${var2}`
    Push `${var3}`
    Push `${var4}`
    Push `${var5}`
    Push `${var6}`
    Push `${var7}`
    Push `${var8}`
    Push `${var9}`
    Push `${var10}`
    !macroend
    
    !define ExchStack4 "!insertmacro ExchStack4"
    !macro ExchStack4 var0 var1 var2 var3
    Exch `${var3}`
    Exch 1
    Exch `${var2}`
    Exch 1
    Exch 2
    Exch `${var1}`
    Exch 2
    Exch 3
    Exch `${var0}`
    Exch 3
    !macroend
    
    !define PopStack15 "!insertmacro PopStack15"
    !macro PopStack15 var0 var1 var2 var3 var4 var5 var6 var7 var8 var9 var10 var11 var12 var13 var14
    Pop `${var14}`
    Pop `${var13}`
    Pop `${var12}`
    Pop `${var11}`
    Pop `${var10}`
    Pop `${var9}`
    Pop `${var8}`
    Pop `${var7}`
    Pop `${var6}`
    Pop `${var5}`
    Pop `${var4}`
    Pop `${var3}`
    Pop `${var2}`
    Pop `${var1}`
    Pop `${var0}`
    !macroend
    

    main.nsi - full NSIS implementation example.

    !include "nsDialogs.nsh"
    !include "WinCore.nsh"
    !include "LogicLib.nsh"
    !include "stack.nsi"
    
    RequestExecutionLevel admin ; for all users
    
    Page Custom Show Leave
    
    Var /GLOBAL DialogID
    Var /GLOBAL EditID
    Var /GLOBAL Edit
    Var /GLOBAL ButtonAppendID
    
    !define GotoIf "!insertmacro GotoIf"
    !macro GotoIf label exp
    ${If} ${exp}
      Goto `${label}`
    ${EndIf}
    !macroend
    
    !define RegGetKeyMap "!insertmacro RegGetKeyMap"
    !macro RegGetKeyMap var value
      ${Switch} ${value}
        ${Case} "HKCR"
          StrCpy ${var} ${HKEY_CLASSES_ROOT}
        ${Break}
        ${Case} "HKCU"
          StrCpy ${var} ${HKEY_CURRENT_USER}
        ${Break}
        ${Case} "HKLM"
          StrCpy ${var} ${HKEY_LOCAL_MACHINE}
        ${Break}
        ${Case} "HKU"
          StrCpy ${var} ${HKEY_USERS}
        ${Break}
        ${Case} "HKPD"
          StrCpy ${var} ${HKEY_PERFORMANCE_DATA}
        ${Break}
        ${Case} "HKDD"
          StrCpy ${var} ${HKEY_DYN_DATA}
        ${Break}
        ${Case} "HKCC"
          StrCpy ${var} ${HKEY_CURRENT_CONFIG}
        ${Break}
        ${Case} "HKEY_CLASSES_ROOT"
          StrCpy ${var} ${HKEY_CLASSES_ROOT}
        ${Break}
        ${Case} "HKEY_CURRENT_USER"
          StrCpy ${var} ${HKEY_CURRENT_USER}
        ${Break}
        ${Case} "HKEY_LOCAL_MACHINE"
          StrCpy ${var} ${HKEY_LOCAL_MACHINE}
        ${Break}
        ${Case} "HKEY_USERS"
          StrCpy ${var} ${HKEY_USERS}
        ${Break}
        ${Case} "HKEY_PERFORMANCE_DATA"
          StrCpy ${var} ${HKEY_PERFORMANCE_DATA}
        ${Break}
        ${Case} "HKEY_DYN_DATA"
          StrCpy ${var} ${HKEY_DYN_DATA}
        ${Break}
        ${Case} "HKEY_CURRENT_CONFIG"
          StrCpy ${var} ${HKEY_CURRENT_CONFIG}
        ${Break}
        ${Default}
          StrCpy ${var} ${HKEY_CURRENT_USER}
        ${Break}
      ${EndSwitch}
    !macroend
    
    ; Usage:
    ; All users:
    ;   ${Push} ""
    ;   ${Push} "HKLM"
    ;   ${Push} "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
    ;   ${Push} ""
    ;   Call RegAddPathToVar
    ; Current user only:
    ;   ${Push} ""
    ;   ${Push} "HKCU"
    ;   ${Push} "Environment"
    ;   ${Push} ""
    ;   Call RegAddPathToVar
    !macro Func_RegAddPathToVar un
    !ifndef ${un}RegAddPathToVar_INCLUDED
    !define ${un}RegAddPathToVar_INCLUDED
    Function ${un}RegAddPathToVar
      ${ExchStack4} $R0 $R1 $R2 $R3
    
      ${PushStack11} $R4 $R5 $R6 $R7 $R8 $R9 $0 $1 $2 $8 $9
    
      ; WARNING:
      ;   NSIS ReadRegStr returns empty string on string overflow, so native calls are used here:
      ;   1. To check actual length of .
      ;   2. To process the PATH variable of any length long.
    
      ; The IDEAL algorithm for any length long PATH variable, where each subpath is not longer than ${NSIS_MAX_STRLEN}-${NSIS_CHAR_SIZE} bytes:
      ;   1. Init current string list if does not have any before or take as current has created after the previous algorithm run.
      ;   2. Read string of ${NSIS_MAX_STRLEN}-${NSIS_CHAR_SIZE} bytes length into the array of ${NSIS_MAX_STRLEN} bytes length from the input address, add NSIS_CHAR_SIZE nulls at the end.
      ;   3. Go to 20 if empty or nothing else except the ; characters in the array.
      ;   4. Truncate all in the array after the last ; character, where the ; character has found not under " character quoted string (see description in the 6).
      ;   5. Split strings in the array by the ; character if it has found not under " character quoted string into the list.
      ;   6. Move the last string from the list into the next repeat cycle list if it begins by the " character but not ends by the same character (not completely fitted into the limited to ${NSIS_MAX_STRLEN} bytes window).
      ;   7. Unquote all strings in the list and create the second list with flags marked where the quotes has removed.
      ;   8. Search for "$R0" or "$R0\" in the list, if found then raise a flag and leave the algorithm.
      ;   9. Move ${NSIS_MAX_STRLEN} byte window by the array current string length long multiple to NSIS_CHAR_SIZE value along the input address.
      ;  10. Repeat the algorithm.
      ;  20. Append path to the list.
      ;  21. Restore quotes for those strings in the first list what been quoted before by the second list.
      ;  22. Join first list by the separator into one string.
    
      ; The REAL algorithm for any length long PATH variable, where each subpath is not longer than ${NSIS_MAX_STRLEN}-${NSIS_CHAR_SIZE} bytes:
      ;   1. Read string from registry into dynamic buffer enough to store more characters: length of being searched string + length of separator + length of string to search + length of null character.
      ;   2. Copy string from the buffer to the second dynamic buffer enough to store more characters: length of separator + length of being searched string + length of separator + length of null character.
      ;   3. Prepend and append separator character to second buffer.
      ;   4. Try to find multiple instances of the string to search in the second buffer through the shlwapi::StrStrI, where search instances are:
      ;      `'
      ;      `\'
      ;   5. If found any instance then leave the algorithm.
      ;   6. Append separator character to the first buffer if it does not ending by it.
      ;   7. Append the string to search to the first buffer.
    
      ; handles and pointers init
      StrCpy $R7 0
      StrCpy $R9 0
      StrCpy $0 0
      StrCpy $1 0
      StrCpy $2 0
    
      ; keys map
      ${RegGetKeyMap} $R8 $R1
    
      System::Call "advapi32::RegOpenKey(i R8, t R2, *i.R6) i.R4"
      ${If} $R4 <> 0
        DetailPrint "RegAddPathToVar: advapi32::RegOpenKey error: code=$R4 hive=$\"$R8$\" key=$\"$R2$\""
        MessageBox MB_OK "RegAddPathToVar: advapi32::RegOpenKey error: code=$R4 hive=$\"$R8$\" key=$\"$R2$\"" /SD IDOK
        Goto done
      ${EndIf}
    
      System::Call "advapi32::RegQueryValueEx(i R6, t R3, i 0, *i .r9, p 0, *i 0 R7) i.R4"
      ${If} $R4 <> 0
        DetailPrint "RegAddPathToVar: advapi32::RegQueryValueEx (1) is failed, unexpected error code: code=$R4 length=$\"$R7$\""
        MessageBox MB_OK "RegAddPathToVar: advapi32::RegQueryValueEx (1) is failed, unexpected error code: code=$R4 length=$\"$R7$\"" /SD IDOK
        Goto done
      ${EndIf}
    
      ; remove trailing "\" character from the string to search
      StrCpy $R5 $R0 "" -1
      ${If} $R5 == "\"
        StrCpy $R0 $R0 -1
      ${EndIf}
    
      StrLen $R8 $R0
      ; first buffer: length of being searched string + length of separator + length of string to search + length of null character
      IntOp $R5 $R8 + 1 ; ";"
      IntOp $R5 $R5 * ${NSIS_CHAR_SIZE}
      IntOp $R5 $R5 + $R7 ; already in bytes including null character
    
      ; allocate first dynamic buffer
      System::Alloc $R5
      Pop $0
      ${If} $0 = 0
        DetailPrint "RegAddPathToVar: System::Alloc (1) is failed: size=$R5"
        MessageBox MB_OK "RegAddPathToVar: System::Alloc (1) is failed: size=$R5" /SD IDOK
        Goto done
      ${EndIf}
    
      System::Call "advapi32::RegQueryValueEx(i R6, t R3, i 0, i 0, p r0, *i R5 R7) i.R4"
      ${If} $R4 <> 0
        DetailPrint "RegAddPathToVar: advapi32::RegQueryValueEx (2) is failed, unexpected error: code=$R4 length=$\"$R7$\""
        MessageBox MB_OK "RegAddPathToVar: advapi32::RegQueryValueEx (2) is failed, unexpected error: code=$R4 length=$\"$R7$\"" /SD IDOK
        Goto done
      ${EndIf}
    
      ; strip separator characters from the first buffer end
      ${If} $R7 > ${NSIS_CHAR_SIZE}
        ; excluding null character
        IntOp $R5 $R7 - ${NSIS_CHAR_SIZE}
        IntOp $R5 $R5 - ${NSIS_CHAR_SIZE}
        IntOp $R9 $0 + $R5
    strip_loop1:
        System::Call "*$R9(&t1 .r8)"
        ${If} $8 == ";"
          System::Call "*$R9(&t1 '')" ; null character
          IntOp $R7 $R7 - ${NSIS_CHAR_SIZE}
          ${If} $R9 >= ${NSIS_CHAR_SIZE}
            IntOp $R9 $R9 - ${NSIS_CHAR_SIZE}
            Goto strip_loop1
          ${EndIf}
        ${EndIf}
      ${EndIf}
    
      ; second buffer: length of separator + length of being searched string + length of separator + length of null character
      IntOp $R5 2 * ${NSIS_CHAR_SIZE} ; 2 x ";"
      IntOp $R5 $R5 + $R7 ; already in bytes including null character
    
      ; allocate second dynamic buffer
      System::Alloc $R5
      Pop $1
      ${If} $1 = 0
        DetailPrint "RegAddPathToVar: System::Alloc (2) is failed: size=$R5"
        MessageBox MB_OK "RegAddPathToVar: System::Alloc (2) is failed: size=$R5" /SD IDOK
        Goto done
      ${EndIf}
    
      System::Call "*$1(&t1 ';')"
    
      IntOp $R9 $1 + ${NSIS_CHAR_SIZE}
      System::Call "kernel32::lstrcpyn(p R9, p r0, i R7) p.R4"
      ${If} $R4 = 0
        DetailPrint "RegAddPathToVar: kernel32::lstrcpyn (1) is failed"
        MessageBox MB_OK "RegAddPathToVar: kernel32::lstrcpyn (1) is failed" /SD IDOK
        Goto done
      ${EndIf}
    
      IntOp $R9 $R9 + $R7
      IntOp $R9 $R9 - ${NSIS_CHAR_SIZE} ; exclude last null character
      System::Call "*$R9(&t1 ';')"
      IntOp $R9 $R9 + ${NSIS_CHAR_SIZE}
      System::Call "*$R9(&t1 '')" ; null character
    
      ; buffer for the string to search
      IntOp $R5 0 + 4 ; 2 x ";" + "\" + length of null character
      IntOp $R5 $R5 + $R8 ; excluding null character
      IntOp $R5 $R5 * ${NSIS_CHAR_SIZE}
    
      System::Alloc $R5
      Pop $2
      ${If} $2 = 0
        DetailPrint "RegAddPathToVar: System::Alloc (3) is failed: size=$R5"
        MessageBox MB_OK "RegAddPathToVar: System::Alloc (3) is failed: size=$R5" /SD IDOK
        Goto done
      ${EndIf}
    
      ; convert R8 (length of R0) to bytes
      IntOp $R8 $R8 * ${NSIS_CHAR_SIZE}
    
      ; `'
      System::Call "*$2(&t1 ';')"
    
      IntOp $R9 $2 + ${NSIS_CHAR_SIZE}
      System::Call "kernel32::lstrcpy(p R9, t R0) p.R4"
      ${If} $R4 = 0
        DetailPrint "RegAddPathToVar: kernel32::lstrcpy (2) is failed"
        MessageBox MB_OK "RegAddPathToVar: kernel32::lstrcpy (2) is failed" /SD IDOK
        Goto done
      ${EndIf}
    
      IntOp $R9 $R9 + $R8 ; length does not include the last null character
      System::Call "*$R9(&t1 ';')"
      IntOp $R9 $R9 + ${NSIS_CHAR_SIZE}
      System::Call "*$R9(&t1 '')" ; null character
    
      System::Call "shlwapi::StrStrI(p r1, p r2) p.R4"
      ${GotoIf} done "$R4 <> 0"
    
      ; `\'
      System::Call "*$2(&t1 ';')"
    
      IntOp $R9 $2 + ${NSIS_CHAR_SIZE}
      IntOp $R9 $R9 + $R8
      System::Call "*$R9(&t1 '\')"
      IntOp $R9 $R9 + ${NSIS_CHAR_SIZE}
      System::Call "*$R9(&t1 ';')"
      IntOp $R9 $R9 + ${NSIS_CHAR_SIZE}
      System::Call "*$R9(&t1 '')" ; null character
    
      System::Call "shlwapi::StrStrI(p r1, p r2) p.R4"
      ${GotoIf} done "$R4 <> 0"
    
      ; append to the first buffer
      IntOp $R9 0 + $0
      ${If} $R7 > ${NSIS_CHAR_SIZE}
        IntOp $R9 $R9 + $R7
        IntOp $R9 $R9 - ${NSIS_CHAR_SIZE} ; exclude last null character
        System::Call "*$R9(&t1 ';')"
        IntOp $R9 $R9 + ${NSIS_CHAR_SIZE}
      ${EndIf}
    
      System::Call "kernel32::lstrcpy(p R9, t R0) p.R4"
      ${If} $R4 = 0
        DetailPrint "RegAddPathToVar: kernel32::lstrcpy (3) is failed"
        MessageBox MB_OK "RegAddPathToVar: kernel32::lstrcpy (3) is failed" /SD IDOK
        Goto done
      ${EndIf}
    
      IntOp $R9 $R9 + $R8 ; length does not include the last null character
      System::Call "*$R9(&t1 '')" ; null character
    
      IntOp $R9 $R9 + ${NSIS_CHAR_SIZE}
      IntOp $R5 $R9 - $0
    
      System::Call "advapi32::RegSetValueEx(i R6, t R3, i 0, i r9, p r0, i R5) i.R4"
      ${If} $R4 <> 0
        DetailPrint "RegAddPathToVar: advapi32::RegSetValueEx (1) is failed"
        MessageBox MB_OK "RegAddPathToVar: advapi32::RegSetValueEx (1) is failed" /SD IDOK
        Goto done
      ${EndIf}
    
      ; broadcast global event
      SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
    
    done:
      System::Call "advapi32::RegCloseKey(i $R6)"
    
      ${If} $0 <> 0
        System::Free $0
      ${EndIf}
      ${If} $1 <> 0
        System::Free $1
      ${EndIf}
      ${If} $2 <> 0
        System::Free $2
      ${EndIf}
    
      ${PopStack15} $R0 $R1 $R2 $R3 $R4 $R5 $R6 $R7 $R8 $R9 $0 $1 $2 $8 $9
    FunctionEnd
    !endif
    !macroend
    
    !define Call_RegAddPathToVar "!insertmacro Call_RegAddPathToVar"
    !macro Call_RegAddPathToVar prefix path hkey hkey_path env_var
    Push `${path}`
    Push `${hkey}`
    Push `${hkey_path}`
    Push `${env_var}`
    Call ${prefix}RegAddPathToVar
    !macroend
    
    !define RegAddPathToVar "!insertmacro RegAddPathToVar"
    !macro RegAddPathToVar
    !insertmacro Func_RegAddPathToVar ""
    !undef RegAddPathToVar
    !define RegAddPathToVar "${Call_RegAddPathToVar} ''"
    !macroend
    
    !define un.RegAddPathToVar "!insertmacro un.RegAddPathToVar"
    !macro un.RegAddPathToVar
    !insertmacro Func_RegAddPathToVar "un."
    !undef un.RegAddPathToVar
    !define un.RegAddPathToVar "${Call_RegAddPathToVar} 'un.'"
    !macroend
    
    ; include for install only
    ${RegAddPathToVar}
    
    Function Show
      nsDialogs::Create 1018
      Pop $DialogID
    
      ${NSD_CreateText} 0 16u 80% 14u "C:\MyPath\bin"
      Pop $EditID
    
      ${NSD_OnChange} $EditID WndProc
    
      ${NSD_CreateButton} 80% 16u 20% 14u "Append"
      Pop $ButtonAppendID
    
      ${NSD_OnClick} $ButtonAppendID WndProc
    
      StrCpy $R0 -1
      Call Update
    
      nsDialogs::Show
    FunctionEnd
    
    Function Leave
    FunctionEnd
    
    Function WndProc
      System::Store SR0
      Call Update
      System::Store L
    FunctionEnd
    
    Function Update
      ; read values
      ${If} $R0 = $EditID
      ${OrIf} $R0 = -1
        ${NSD_GetText} $EditID $Edit
      ${EndIf}
      ${If} $R0 = $ButtonAppendID
        ${If} $Edit != ""
          #${RegAddPathToVar} "$Edit" HKCU "Environment" PATH ; for current user
          ${RegAddPathToVar} "$Edit" HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" PATH ; for all users
        ${EndIf}
      ${EndIf}
    FunctionEnd
    
    Section -Hidden
    SectionEnd
    

提交回复
热议问题