使用 NSIS 更新 %PATH% 环境变量
Update %PATH% environment variable using NSIS
我读到 “长度超过 ${NSIS_MAX_STRLEN} (1024) 的字符串将得到 truncated/corrupted”。 =19=]
如何安全地更新%PATH%
环境变量?
您可以使用来自 special builds page like the large strings build 的替代 NSIS 构建,它定义了 NSIS_MAX_STRLEN=8192
并且应该可以防止您破坏主机路径。
实际上,在台式机上,1024 字节似乎足够了,但在安装了很多工具的开发主机上(比如我的),路径可能在操作后被破坏,而 8192 字节的字符串构建从未扰乱我的机.
可以肯定的是,您可以在操作之前添加对路径长度的检查,并在尝试操作之前路径接近 NSIS_MAX_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} [=10=] = 0
System::Call 'ADVAPI32::RegQueryValueEx(ir1,tR2,i0,*i.r2,i0,*i0r3)i.r0'
${If} [=10=] <> 0
StrCpy ${REG_SZ}
StrCpy 0
${EndIf}
StrLen $R0
StrLen $R1
IntOp +
IntOp + 1 ; For [=10=]
!if ${NSIS_CHAR_SIZE} > 1
IntOp * ${NSIS_CHAR_SIZE}
!endif
IntOp +
System::Alloc
System::Call 'ADVAPI32::RegQueryValueEx(ir1,tR2,i0,i0,isr9,*ir4r4)i.r0'
${If} [=10=] = 0
${OrIf} [=10=] = ${ERROR_FILE_NOT_FOUND}
System::Call 'KERNEL32::lstrlen(t)(ir9)i.r0'
${If} [=10=] <> 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 [=10=] [=10=] + 1
!if ${NSIS_CHAR_SIZE} > 1
IntOp [=10=] [=10=] * ${NSIS_CHAR_SIZE}
!endif
System::Call 'ADVAPI32::RegSetValueEx(ir1,tR2,i0,ir2,ir9,ir0)i.r0'
${EndIf}
System::Free
System::Call 'ADVAPI32::RegCloseKey(ir1)'
${EndIf}
Push [=10=]
System::Store L
FunctionEnd
Section
Push ${HKEY_CURRENT_USER}
Push "Environment"
Push "Path"
Push ";"
Push "c:\whatever"
Call RegAppendString
Pop [=10=]
DetailPrint RegAppendString:Error=[=10=]
SectionEnd
我已经编写了 NSIS 3.0 示例来处理超过限制的情况,而无需安装任何东西。在这里回答了问题:Set environment variables with NSIS in Window 7
我更喜欢通过 NSIS nsExec::Exec
命令使用 windows 命令处理器 (cmd.exe
),它允许您像这样轻松地附加到 PATH
:
; Check if the path entry already exists and write result to [=10=]
nsExec::Exec 'echo %PATH% | find "c:\some\new\dir"'
Pop [=10=] ; gets result code
${If} [=10=] = 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%'
请注意,在构建新的 PATH
时 而不是 包含双引号很重要。命令处理器从不期望在 PATH 字符串中使用双引号,如果您添加任何双引号,它可能会以意想不到的方式运行。它仅用分号 (;
) 分隔路径。
另请注意,此方法取决于 as explained by Seki in his answer。
nsExec::Exec
与 ExecWait
的不同之处在于它在内部运行,不会弹出额外的可见 cmd 提示符 windows.
看到 NSIS 的所有复杂插件和字符限制,我感到很沮丧,所以我编写了一个名为 PathEd 的小应用程序来处理所有问题。
它旨在部署在您的安装程序中,并且可以像这样从其部署位置调用,例如:
PathEd.exe add "C:\Program Files\RepoZ"
要么
PathEd.exe remove "C:\Program Files\RepoZ"
PathEd 负责分号处理、避免重复、不区分大小写的检查、用户帐户控制提示、参数引用处理、安全和防御值删除等。
请放心使用。但是,别忘了在 GitHub 上给它加星。
我读到 “长度超过 ${NSIS_MAX_STRLEN} (1024) 的字符串将得到 truncated/corrupted”。 =19=]
如何安全地更新%PATH%
环境变量?
您可以使用来自 special builds page like the large strings build 的替代 NSIS 构建,它定义了 NSIS_MAX_STRLEN=8192
并且应该可以防止您破坏主机路径。
实际上,在台式机上,1024 字节似乎足够了,但在安装了很多工具的开发主机上(比如我的),路径可能在操作后被破坏,而 8192 字节的字符串构建从未扰乱我的机.
可以肯定的是,您可以在操作之前添加对路径长度的检查,并在尝试操作之前路径接近 NSIS_MAX_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} [=10=] = 0
System::Call 'ADVAPI32::RegQueryValueEx(ir1,tR2,i0,*i.r2,i0,*i0r3)i.r0'
${If} [=10=] <> 0
StrCpy ${REG_SZ}
StrCpy 0
${EndIf}
StrLen $R0
StrLen $R1
IntOp +
IntOp + 1 ; For [=10=]
!if ${NSIS_CHAR_SIZE} > 1
IntOp * ${NSIS_CHAR_SIZE}
!endif
IntOp +
System::Alloc
System::Call 'ADVAPI32::RegQueryValueEx(ir1,tR2,i0,i0,isr9,*ir4r4)i.r0'
${If} [=10=] = 0
${OrIf} [=10=] = ${ERROR_FILE_NOT_FOUND}
System::Call 'KERNEL32::lstrlen(t)(ir9)i.r0'
${If} [=10=] <> 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 [=10=] [=10=] + 1
!if ${NSIS_CHAR_SIZE} > 1
IntOp [=10=] [=10=] * ${NSIS_CHAR_SIZE}
!endif
System::Call 'ADVAPI32::RegSetValueEx(ir1,tR2,i0,ir2,ir9,ir0)i.r0'
${EndIf}
System::Free
System::Call 'ADVAPI32::RegCloseKey(ir1)'
${EndIf}
Push [=10=]
System::Store L
FunctionEnd
Section
Push ${HKEY_CURRENT_USER}
Push "Environment"
Push "Path"
Push ";"
Push "c:\whatever"
Call RegAppendString
Pop [=10=]
DetailPrint RegAppendString:Error=[=10=]
SectionEnd
我已经编写了 NSIS 3.0 示例来处理超过限制的情况,而无需安装任何东西。在这里回答了问题:Set environment variables with NSIS in Window 7
我更喜欢通过 NSIS nsExec::Exec
命令使用 windows 命令处理器 (cmd.exe
),它允许您像这样轻松地附加到 PATH
:
; Check if the path entry already exists and write result to [=10=]
nsExec::Exec 'echo %PATH% | find "c:\some\new\dir"'
Pop [=10=] ; gets result code
${If} [=10=] = 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%'
请注意,在构建新的 PATH
时 而不是 包含双引号很重要。命令处理器从不期望在 PATH 字符串中使用双引号,如果您添加任何双引号,它可能会以意想不到的方式运行。它仅用分号 (;
) 分隔路径。
另请注意,此方法取决于
nsExec::Exec
与 ExecWait
的不同之处在于它在内部运行,不会弹出额外的可见 cmd 提示符 windows.
看到 NSIS 的所有复杂插件和字符限制,我感到很沮丧,所以我编写了一个名为 PathEd 的小应用程序来处理所有问题。
它旨在部署在您的安装程序中,并且可以像这样从其部署位置调用,例如:
PathEd.exe add "C:\Program Files\RepoZ"
要么
PathEd.exe remove "C:\Program Files\RepoZ"
PathEd 负责分号处理、避免重复、不区分大小写的检查、用户帐户控制提示、参数引用处理、安全和防御值删除等。
请放心使用。但是,别忘了在 GitHub 上给它加星。