为什么其他文件夹路径也使用 SetX 添加到系统 PATH 中,而不仅仅是指定的文件夹路径



我有一个批处理文件,我正在使用system("name.bat")从C++调用它。在该批处理文件中,我正在尝试读取注册表项的值。从C++调用批处理文件会导致set KEY_NAME=HKEY_LOCAL_MACHINEstuff失败。

但是,当我直接运行批处理文件(双击它(时,它运行良好。不知道我做错了什么。

批处理文件:

set KEY_NAME=HKEY_LOCAL_MACHINESOFTWAREAnsoftDesigner2014.0Desktop
set VALUE_NAME=InstallationDirectory
REG QUERY %KEY_NAME% /v %VALUE_NAME%

C++文件:

int main(void)
{
    system("CALL C:\HFSS\setup_vars.bat");
    return 0;
}

更新 1:

发现密钥实际上在 64 位注册表中,我正在将我的 C++ 解决方案构建为 32 位。一旦我修复了它,它发现注册表项很好。

现在我在将该路径添加到我的 PATH 变量时遇到问题。它不是创建系统变量,而是创建一个用户变量 PATH 并将其添加到其中。

从命令行运行有效。

法典:

set KEY_NAME=HKLMSOFTWAREAnsoftDesigner2014.0Desktop
set VALUE_NAME=InstallationDirectory
FOR /F "usebackq skip=1 tokens=1,2*" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME%`) DO (
   set ValueName=%%A
   set ValueType=%%B
   set ValueValue=%%C
)
if defined ValueName (
   @echo Value Value = %ValueValue%
) else (
   @echo %KEY_NAME%%VALUE_NAME% not found.
)
:: Set PATH Variable
set path_str=%PATH%
set addPath=%ValueValue%;
echo %addPath%
echo %ValueValue%
echo %PATH%| find /i "%addPath%">NUL
if NOT ERRORLEVEL 1 (
   SETX PATH "%PATH%
) else (
   SETX PATH "%PATH%;%addPath%;" /M
)

更新 2:

我移动了选项/M 的位置,它现在正在添加到右侧 PATH 变量中。

但是,当我这样做时,它会多次添加 PATH(3 次(,然后它还添加了指向 Visual Studio amd64 文件夹的路径。

我非常确定为什么会发生这种情况。

Windows 内核库函数 CreateProcess 创建进程的整个环境表的副本,为新进程启动一个新进程。因此,在启动C++应用程序时,应用程序会从父进程、Windows 资源管理器Visual Studio 获取环境表,包括 PATH。并且此路径在批处理文件开始时复制以进行cmd.exe

考虑到从Windows桌面到批处理文件的整个进程树,已经为PATH制作了几个副本,并且某些进程可能像Visual Studio所做的那样将某些内容附加到其本地PATH副本中,甚至从PATH中删除了路径。

您现在使用 SETX PATH "%PATH% 所做的是将进程树中的父进程已经修改的 PATH 的本地副本完全附加到系统 PATH,而不检查重复路径。

更好的方法是使用 PATH 的本地副本丢弃所有代码,而是读取系统 PATH 的值,检查要添加的路径是否尚未在系统 PATH 中,如果不是这种情况,请使用 setx 将要添加到系统 PATH 的路径附加到系统路径

这应该在不扩展系统 PATH 中的环境变量的情况下完成,例如 %SystemRoot%System32C:WindowsSystem32 .

<小时 />

以下是在 Windows XP SP3 x86、Windows 7 SP1 x64 和 Windows 11 22H2 上测试的任务所需的批处理代码。

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "KeyName=HKLMSOFTWAREAnsoftDesigner2014.0Desktop"
set "ValueName=InstallationDirectory"
for /F "skip=2 tokens=1,2*" %%G in ('%SystemRoot%System32reg.exe query "%KeyName%" /v "%ValueName%" 2^>nul') do (
    if /I "%%G" == "%ValueName%" (
        set "PathToAdd=%%I"
        if defined PathToAdd goto GetSystemPath
    )
)
echo Error: Could not find non-empty value "%ValueName%" under key
echo        %KeyName%
echo(
endlocal
pause
exit /B
:GetSystemPath
for /F "skip=2 tokens=1,2*" %%G in ('%SystemRoot%System32reg.exe query "HKLMSystemCurrentControlSetControlSession ManagerEnvironment" /v "Path" 2^>nul') do (
    if /I "%%G" == "Path" (
        set "SystemPath=%%I"
        if defined SystemPath goto CheckPath
    )
)
echo Error: System environment variable PATH not found with a non-empty value.
echo(
endlocal
pause
exit /B
:CheckPath
setlocal EnableDelayedExpansion
rem The folder path to add must contain  (backslash) as directory
rem separator and not / (slash) and should not end with a backslash.
set "PathToAdd=!PathToAdd:/=!"
if "!PathToAdd:~-1!" == "" set "PathToAdd=!PathToAdd:~0,-1!"
if "!SystemPath:~-1!" == ";" (set "Separator=") else set "Separator=;"
set "PathCheck=!SystemPath!%Separator%"
rem Do nothing if the folder path to add without or with a backslash
rem at end with a semicolon appended for entire folder path check is
rem already in the system PATH value. This code does not work with
rem path to add contains an equal sign which is fortunately very rare.
if not "!PathCheck:%PathToAdd%;=!" == "!PathCheck!" goto EndBatch
if not "!PathCheck:%PathToAdd%;=!" == "!PathCheck!" goto EndBatch
set "PathToSet=!SystemPath!%Separator%!PathToAdd!"
set "UseSetx=1"
if not "!PathToSet:~1024,1!" == "" set "UseSetx="
if not exist %SystemRoot%System32setx.exe set "UseSetx="
if defined UseSetx (
    %SystemRoot%System32setx.exe Path "!PathToSet!" /M >nul
) else (
    set "ValueType=REG_EXPAND_SZ"
    if "!PathToSet:%%=!" == "!PathToSet!" set "ValueType=REG_SZ"
    %SystemRoot%System32reg.exe ADD "HKLMSystemCurrentControlSetControlSession ManagerEnvironment" /f /v Path /t !ValueType! /d "!PathToSet!" >nul
)
:EndBatch
endlocal
endlocal

上面的批处理代码使用简单的不区分大小写的字符串替换和区分大小写的字符串比较来检查要追加的文件夹路径是否已存在于系统 PATH 中。仅当已知之前如何添加文件夹路径并且用户在此期间未在 PATH 中修改此文件夹路径时,此操作才有效。有关检查 PATH 是否包含文件夹路径的更安全方法,请参阅如何检查 %PATH% 中是否存在目录的答案?由戴夫·本纳姆撰写。

注 1:默认情况下,命令 setx 在 Windows XP 上不可用。

注 2:命令setx将长度超过 1024 个字符的值截断为 1024 个字符。

因此,批处理文件使用命令 reg 替换 Windows 注册表中的系统 PATH,如果 setx 不可用或新路径值太长而无法setx。使用 reg 的缺点是WM_SETTINGCHANGE消息不会发送到所有顶级窗口,通知作为 Windows 桌面运行的 Windows 资源管理器和其他应用程序有关系统环境变量的此更改。因此,用户必须重新启动Windows,最好始终在更改持久存储的Windows系统环境变量上的某些内容时完成。

批处理脚本已使用 PATH 进行测试,该路径当前包含带有感叹号的文件夹路径,并且文件夹路径用双引号括起来,仅当文件夹路径包含分号时才需要。

要了解使用的命令及其工作原理,请打开命令提示符窗口,在其中执行以下命令,然后完整而仔细地阅读每个命令的显示帮助页面。

  • echo /?
  • endlocal /?
  • exit /?
  • for /?
  • goto /?
  • if /?
  • pause /?
  • reg /?
  • reg add /?
  • reg query /?
  • set /?
  • setlocal /?
  • setx /?

最新更新