Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Update %PATH% environment variable using NSIS

I've read that "Strings longer than ${NSIS_MAX_STRLEN} (1024) will get truncated/corrupted."

How can I safely update %PATH% environment variable?

like image 698
Cyber Tailor Avatar asked Jul 10 '15 12:07

Cyber Tailor


People also ask

How to SET environment variable in NSIS?

If you want to set an environment variable only for the installer process and its sub-processes use: System::Call 'Kernel32::SetEnvironmentVariable(t, t)i ("name", "value").

How do you compile NSIS?

nsi file by simply right-clicking on it in Explorer and selecting 'compile'. If you want to use MakeNSIS on the command line, the syntax of makensis is: makensis [ option | script. nsi | - ] [...]


2 Answers

I prefer using windows command processor (cmd.exe) via the NSIS nsExec::Exec command, which allows you to append to the PATH easily like so:

; Check if the path entry already exists and write result to $0
nsExec::Exec 'echo %PATH% | find "c:\some\new\dir"'
Pop $0   ; gets result code

${If} $0 = 0
    nsExec::Exec 'setx PATH=%PATH%;c:\some\new\dir'
${EndIf}

Using this method, CMD.EXE expands the PATH variable internally, safe from any NSIS string length limits. Alternatively, change the order of %PATH% token pasting if you want your program to be picked up first, ahead of anything and everything else that might be installed on the system by the same name:

    nsExec::Exec 'setx PATH=c:\some\new\dir;%PATH%'

Note that it's important not to include double-quotes while building the new PATH. The command processor never expects double-quotes in the PATH string and may behave in unexpected ways if you add any. It delimits paths by semicolon (;) only.

Also note that this methods depends on the large strings build as explained by Seki in his answer.

nsExec::Exec differs from ExecWait in that it runs internally, without popping up additional visible cmd prompt windows.

like image 168
jstine Avatar answered Sep 27 '22 20:09

jstine


The real solution is to write a custom plugin or call the Windows API directly with the system plugin so you can avoid the NSIS buffer length limitation:

!include LogicLib.nsh
!include WinCore.nsh
!ifndef NSIS_CHAR_SIZE
!define NSIS_CHAR_SIZE 1
!endif

Function RegAppendString
System::Store S
Pop $R0 ; append
Pop $R1 ; separator
Pop $R2 ; reg value
Pop $R3 ; reg path
Pop $R4 ; reg hkey
System::Call 'ADVAPI32::RegCreateKey(i$R4,tR3,*i.r1)i.r0'
${If} $0 = 0
    System::Call 'ADVAPI32::RegQueryValueEx(ir1,tR2,i0,*i.r2,i0,*i0r3)i.r0'
    ${If} $0 <> 0
        StrCpy $2 ${REG_SZ}
        StrCpy $3 0
    ${EndIf}
    StrLen $4 $R0
    StrLen $5 $R1
    IntOp $4 $4 + $5
    IntOp $4 $4 + 1 ; For \0
    !if ${NSIS_CHAR_SIZE} > 1
        IntOp $4 $4 * ${NSIS_CHAR_SIZE}
    !endif
    IntOp $4 $4 + $3
    System::Alloc $4
    System::Call 'ADVAPI32::RegQueryValueEx(ir1,tR2,i0,i0,isr9,*ir4r4)i.r0'
    ${If} $0 = 0
    ${OrIf} $0 = ${ERROR_FILE_NOT_FOUND}
        System::Call 'KERNEL32::lstrlen(t)(ir9)i.r0'
        ${If} $0 <> 0
            System::Call 'KERNEL32::lstrcat(t)(ir9,tR1)'
        ${EndIf}
        System::Call 'KERNEL32::lstrcat(t)(ir9,tR0)'
        System::Call 'KERNEL32::lstrlen(t)(ir9)i.r0'
        IntOp $0 $0 + 1
        !if ${NSIS_CHAR_SIZE} > 1
            IntOp $0 $0 * ${NSIS_CHAR_SIZE}
        !endif
        System::Call 'ADVAPI32::RegSetValueEx(ir1,tR2,i0,ir2,ir9,ir0)i.r0'
    ${EndIf}
    System::Free $9
    System::Call 'ADVAPI32::RegCloseKey(ir1)'
${EndIf}
Push $0
System::Store L
FunctionEnd

Section

Push ${HKEY_CURRENT_USER}
Push "Environment"
Push "Path"
Push ";"
Push "c:\whatever"
Call RegAppendString
Pop $0
DetailPrint RegAppendString:Error=$0

SectionEnd 
like image 32
Anders Avatar answered Sep 27 '22 21:09

Anders