Windows 使用NSIS更新%PATH%环境变量

Windows 使用NSIS更新%PATH%环境变量,windows,path,installation,environment-variables,nsis,Windows,Path,Installation,Environment Variables,Nsis,我读过“长度超过${NSIS_MAX_STRLEN}(1024)的字符串将被截断/损坏。” 如何安全地更新%PATH%环境变量?您可以使用类似的NSIS构建,该构建定义了NSIS\u MAX\u STRLEN=8192,并应防止您中断主机路径 实际上,在桌面计算机上,1024字节似乎足够了,但在安装了许多工具(如我的工具)的开发主机上,路径可能在操作后中断,而8192字节的字符串构建从未扰乱过我的计算机 可以肯定的是,,您可以在操作之前添加对路径长度的检查,如果路径接近NSIS\u MAX\u

我读过“长度超过${NSIS_MAX_STRLEN}(1024)的字符串将被截断/损坏。”


如何安全地更新
%PATH%
环境变量?

您可以使用类似的NSIS构建,该构建定义了
NSIS\u MAX\u STRLEN=8192
,并应防止您中断主机路径

实际上,在桌面计算机上,1024字节似乎足够了,但在安装了许多工具(如我的工具)的开发主机上,路径可能在操作后中断,而8192字节的字符串构建从未扰乱过我的计算机


可以肯定的是,,您可以在操作之前添加对路径长度的检查,如果路径接近
NSIS\u MAX\u STRLEN
常量,则可以在尝试操作之前使用消息中止安装程序。

真正的解决方案是编写自定义插件或使用系统插件直接调用Windows API,这样可以避免NSIS缓冲区长度限制:

!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 
!包括LogicLib.nsh
!包括WinCore.nsh
!ifndef NSIS\U字符大小
!定义NSIS\u字符大小1
!恩迪夫
函数RegAppendString
系统::商店
Pop$R0;追加
Pop$R1;分离器
Pop$R2;注册值
Pop$R3;注册路径
Pop$R4;雷格hkey
系统::调用'ADVAPI32::RegCreateKey(i$R4,tR3,*i.r1)i.r0'
${If}$0=0
系统::调用'ADVAPI32::regqueryvaluex(ir1、tR2、i0、*i.r2、i0、*i0r3)i.r0'
${If}$0
StrCpy$2${REG_SZ}
StrCpy$30
${EndIf}
斯特伦4美元R0
斯特伦$5$R1
输入$4$4+5
IntOp$4$4+1;对于\0
!如果${NSIS\u CHAR\u SIZE}>1
IntOp$4$4*${NSIS_CHAR_SIZE}
!恩迪夫
IntOp$4$4+3美元
系统::Alloc$4
系统::调用'ADVAPI32::regqueryvaluex(ir1、tR2、i0、i10、isr9、*ir4r4)i.r0'
${If}$0=0
${OrIf}$0=${ERROR\u FILE\u NOT\u FOUND}
系统::调用'KERNEL32::lstrlen(t)(ir9)i.r0'
${If}$0
系统::调用'KERNEL32::lstrcat(t)(ir9,tR1)'
${EndIf}
系统::调用'KERNEL32::lstrcat(t)(ir9,tR0)'
系统::调用'KERNEL32::lstrlen(t)(ir9)i.r0'
输入$0$0+1
!如果${NSIS\u CHAR\u SIZE}>1
IntOp$0$0*${NSIS\u CHAR\u SIZE}
!恩迪夫
系统::调用'ADVAPI32::RegSetValueEx(ir1,tR2,i0,ir2,ir9,ir0)i.r0'
${EndIf}
系统::免费9美元
系统::调用“ADVAPI32::RegCloseKey(ir1)”
${EndIf}
推送$0
系统::商店L
功能端
部分
推送${HKEY\U当前用户}
推动“环境”
推送“路径”
推“;”
按“c:\whatever”
调用重新出现字符串
流行音乐$0
DetailPrint RegAppendString:错误=$0
分段结束

我编写了NSIS 3.0示例来处理超出限制的情况,无需安装任何东西。这里回答了问题:

我更喜欢通过NSIS
nsExec::Exec
命令使用windows命令处理器(
cmd.exe
),它允许您轻松地附加到
路径
,如下所示:

; 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}
使用此方法,
CMD.EXE
在内部扩展
PATH
变量,不受任何NSIS字符串长度限制。或者,如果您希望在系统上以相同名称安装的任何内容之前,首先拾取程序,请更改
%PATH%
标记粘贴的顺序:

    nsExec::Exec 'setx PATH=c:\some\new\dir;%PATH%'
请注意,在构建新的
路径时,不要包含双引号,这一点很重要。命令处理器从不希望在路径字符串中使用双引号,如果添加双引号,则可能会出现意外的行为。它仅用分号(
)分隔路径

还请注意,此方法取决于as


nsExec::Exec
ExecWait
的不同之处在于,它在内部运行,没有弹出其他可见的cmd提示符窗口。

看到NSIS的所有复杂插件和字符限制,我很沮丧,所以我编写了一个名为PathEd的小应用程序来处理所有问题

它被设计为部署在安装程序中,可以从其部署位置调用,例如:

PathEd.exe添加“C:\Program Files\RepoZ”
PathEd.exe删除“C:\Program Files\RepoZ”

PathEd负责分号处理、避免重复、不区分大小写的检查、用户帐户控制提示、参数引号处理、安全和防御值删除等

请随意使用它。但是,别忘了在GitHub上给它加一颗星星


请参见

我只对RegAppendString函数进行了少量测试,理想情况下,您可以强制零终止,检查它的末尾是否已经有分隔符,并在循环中执行所有这些操作,以避免在调用RegQueryValueEx之间字符串更改的问题。这似乎会截断结果。在命令行上手动运行此操作时,我会遇到以下错误:错误:无效语法。键入“SETX/?”了解用法