Почему другие пути к папкам также добавляются в систему PATH с помощью SetX, а не только для указанного пути к папке?

У меня есть пакетный файл, который я вызываю из С++ с помощью system("name.bat"). В этом пакетном файле я пытаюсь прочитать значение раздела реестра. Вызов командного файла из С++ приводит к ошибке set KEY_NAME=HKEY_LOCAL_MACHINE\stuff.

Однако, когда я запускаю пакетный файл (дважды щелкнув его), он работает нормально. Не уверен, что я делаю неправильно.

Пакетный файл:

set KEY_NAME=HKEY_LOCAL_MACHINE\SOFTWARE\Ansoft\Designer\2014.0\Desktop
set VALUE_NAME=InstallationDirectory
REG QUERY %KEY_NAME% /v %VALUE_NAME%

Файл С++:

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

ОБНОВЛЕНИЕ 1:

Я узнал, что ключ действительно находится в 64-битном реестре, и я строил свое С++-решение как 32-разрядное. Как только я исправил это, он нашел ключ реестра в порядке.

Теперь у меня возникла проблема с добавлением этого пути к моей переменной PATH. Вместо создания системной переменной создается пользовательская переменная PATH и добавляется там.

Работает из командной строки.

код:

set KEY_NAME=HKLM\SOFTWARE\Ansoft\Designer\2014.0\Desktop\
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 раза), а затем добавляет путь к визуальной папке amd64 для студии.

Я уверен, почему это происходит.

Ответ 1

Windows создает копию всей таблицы окружения процесса, начиная новый процесс для нового процесса. Поэтому при запуске приложения C++ ваше приложение получает таблицу среды, включающую PATH, из родительского процесса, проводника Windows или, в вашем случае, Visual Studio. И этот путь копируется для cmd.exe при запуске пакетного файла.

Принимая во внимание все дерево процессов с рабочего стола Windows в пакетный файл, было сделано много копий для PATH, и некоторые процессы, возможно, добавили что-то в свою локальную копию PATH, например Visual Studio. сделал или даже удалил пути из PATH.

То, что вы делаете сейчас с SETX PATH "%PATH%, добавляет локальную копию PATH, измененную уже родительскими процессами в дереве процессов, полностью в систему PATH, не проверяя дубликаты путей.

Гораздо лучше было бы выбросить весь код, используя локальную копию PATH, и вместо этого прочитать значение системного PATH, проверить, не существует ли путь, который вы хотите добавить, в системе PATH и, если это не так, добавьте путь, который вы хотите добавить в систему PATH, с помощью setx.

И это должно быть сделано без расширения переменных среды в системе PATH, например %SystemRoot%\System32, до C:\Windows\System32.


UPDATE

Вот пакетный код, необходимый для вашей задачи, протестированной в Windows 7 x64 и Windows XP x86.

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "KeyName=HKLM\SOFTWARE\Ansoft\Designer\2014.0\Desktop"
set "ValueName=InstallationDirectory"
for /F "skip=2 tokens=1,2*" %%N in ('%SystemRoot%\System32\reg.exe query "%KeyName%" /v "%ValueName%" 2^>nul') do (
    if /I "%%N" == "%ValueName%" (
        set "PathToAdd=%%P"
        if defined PathToAdd goto GetSystemPath
    )
)
echo Error: Could not find non-empty value "%ValueName%" under key
echo        %KeyName%
echo/
endlocal
pause
goto :EOF

:GetSystemPath
for /F "skip=2 tokens=1,2*" %%N in ('%SystemRoot%\System32\reg.exe query "HKLM\System\CurrentControlSet\Control\Session Manager\Environment" /v "Path" 2^>nul') do (
    if /I "%%N" == "Path" (
        set "SystemPath=%%P"
        if defined SystemPath goto CheckPath
    )
)
echo Error: System environment variable PATH not found with a non-empty value.
echo/
endlocal
pause
goto :EOF

: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%"
set "Separator="
if not "!SystemPath:~-1!" == ";" set "Separator=;"
set "PathCheck=!SystemPath!%Separator%"
if "!PathCheck:%PathToAdd%;=!" == "!PathCheck!" (
    set "PathToSet=!SystemPath!%Separator%!PathToAdd!"
    set "UseSetx=1"
    if not "!PathToSet:~1024,1!" == "" set "UseSetx="
    if not exist %SystemRoot%\System32\setx.exe set "UseSetx="
    if defined UseSetx (
        %SystemRoot%\System32\setx.exe Path "!PathToSet!" /M >nul
    ) else (
        set "ValueType=REG_EXPAND_SZ"
        if "!PathToSet:%%=!" == "!PathToSet!" set "ValueType=REG_SZ"
        %SystemRoot%\System32\reg.exe ADD "HKLM\System\CurrentControlSet\Control\Session Manager\Environment" /f /v Path /t !ValueType! /d "!PathToSet!" >nul
    )
)
endlocal
endlocal

Приведенный выше пакетный код использует простую подстановку строк без учета регистра и сравнение строк с учетом регистра, чтобы проверить, присутствует ли путь к добавляемой папке в системе PATH. Это работает, только если хорошо известно, как путь к папке был добавлен ранее, и пользователь пока что не изменил этот путь к папке в PATH. Более безопасный способ проверки, содержит ли PATH путь к папке, приведен в ответе на вопрос Как проверить, существует ли каталог в% PATH%?, написанный Дейвом Бенхэмом.

Примечание 1: Команда setx по умолчанию недоступна в Windows XP.

Примечание 2: Команда setx усекает значения длиннее 1024 символов до 1024 символов.

По этой причине командный файл использует команду reg для замены системного PATH в реестре Windows, если либо setx недоступен, либо значение нового пути слишком длинное для setx. Недостаток использования reg заключается в том, что сообщение WM_SETTINGCHANGE не отправляется во все окна верхнего уровня, информирующее Проводник Windows, работающий в качестве рабочего стола Windows, и других приложений об этом изменении переменной среды системы. Таким образом, пользователь должен перезапустить Windows, что лучше всего делать всегда при изменении чего-либо в постоянных хранимых системных переменных Windows.

Пакетный скрипт был протестирован с PATH, который в настоящее время содержит путь к папке с восклицательным знаком и путь к папке, заключенный в двойные кавычки, что необходимо, только если путь к папке содержит точку с запятой.

Чтобы понять используемые команды и то, как они работают, откройте окно командной строки, выполните там следующие команды и внимательно прочитайте все страницы справки, отображаемые для каждой команды.

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