¿Por qué también se agregan otras rutas de carpeta a la RUTA del sistema con SetX y no solo la ruta de carpeta especificada?

Resuelto user2970916 asked hace 9 años • 1 respuestas

Tengo un archivo por lotes al que llamo desde C++ usando system("name.bat"). En ese archivo por lotes estoy intentando leer el valor de una clave de registro. Llamar al archivo por lotes desde C++ provoca set KEY_NAME=HKEY_LOCAL_MACHINE\stuffun error.

Sin embargo, cuando ejecuto directamente el archivo por lotes (haciendo doble clic en él), funciona bien. No estoy seguro de qué estoy haciendo mal.

Archivo por lotes:

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

Archivo C++:

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

ACTUALIZACIÓN 1:

Descubrí que la clave está en realidad en el registro de 64 bits y estaba creando mi solución C++ como de 32 bits. Una vez que solucioné eso, encontró que la clave de registro estaba bien.

Ahora tengo un problema al agregar esa ruta a mi variable PATH . En lugar de crear una variable de sistema, se crea una variable de usuario PATH y se agrega allí.

Ejecutar desde la línea de comando funciona.

Código:

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
)

ACTUALIZACIÓN 2:

Moví la ubicación de la opción /M y ahora se agrega a la variable PATH derecha .

Sin embargo, cuando hago esto, agrego la RUTA más de una vez (3 veces) y luego también agrego una ruta a la carpeta AMD64 de Visual Studio.

No estoy seguro de por qué sucede eso.

user2970916 avatar Sep 18 '14 21:09 user2970916
Aceptado

La función CreateProcess de la biblioteca del kernel de Windows crea una copia de toda la tabla de entorno del proceso iniciando un nuevo proceso para el nuevo proceso. Por lo tanto, al iniciar su aplicación C++, su aplicación obtiene la tabla de entorno, incluida la RUTA del proceso principal, el Explorador de Windows o, en su caso, Visual Studio . Y esta RUTA se copia cmd.exeal inicio del archivo por lotes.

Teniendo en cuenta todo el árbol de procesos desde el escritorio de Windows hasta el archivo por lotes, se han realizado varias copias para PATH y algunos procesos tal vez agregaron algo a su copia local de PATH como lo hizo Visual Studio , o incluso eliminaron rutas de PATH .

Lo que debe hacer ahora SETX PATH "%PATH%es agregar completamente la copia local de PATH ya modificada por los procesos principales en el árbol de procesos a la RUTA del sistema sin verificar si hay rutas duplicadas.

Mucho mejor sería desechar todo el código usando una copia local de PATH y en su lugar leer el valor de system PATH , verificar si la ruta que desea agregar no está ya en la RUTA del sistema y, si este no es el caso, agregar la ruta que desea. para agregar a la RUTA del sistema usando setx.

Y esto debe hacerse sin expandir las variables de entorno en la RUTA del sistema como %SystemRoot%\System32lo haría C:\Windows\System32.


Aquí está el código de lote necesario para su tarea probado en Windows XP SP3 x86, Windows 7 SP1 x64 y Windows 11 22H2.

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "KeyName=HKLM\SOFTWARE\Ansoft\Designer\2014.0\Desktop"
set "ValueName=InstallationDirectory"
for /F "skip=2 tokens=1,2*" %%G in ('%SystemRoot%\System32\reg.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%\System32\reg.exe query "HKLM\System\CurrentControlSet\Control\Session Manager\Environment" /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%\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
)
:EndBatch
endlocal
endlocal

El código por lotes anterior utiliza una simple sustitución de cadenas que no distingue entre mayúsculas y minúsculas y una comparación de cadenas que distingue entre mayúsculas y minúsculas para verificar si la ruta de la carpeta a agregar ya está presente en la RUTA del sistema . Esto funciona solo si se sabe bien cómo se agregó la ruta de la carpeta antes y el usuario no ha modificado esta ruta de la carpeta en PATH mientras tanto. Para conocer un método más seguro para verificar si PATH contiene una ruta de carpeta, consulte la respuesta sobre ¿Cómo verificar si el directorio existe en% PATH%? escrito por Dave Benham .

Nota 1: El comando setxno está disponible de forma predeterminada en Windows XP.

Nota 2: el comando setxtrunca los valores de más de 1024 caracteres a 1024 caracteres.

Por esa razón, el archivo por lotes utiliza el comando regpara reemplazar la RUTA del sistema en el registro de Windows si setxno está disponible o el nuevo valor de la ruta es demasiado largo setx. La desventaja de su uso reges que el mensaje WM_SETTINGCHANGE no se envía a todas las ventanas de nivel superior para informar al Explorador de Windows que se ejecuta como escritorio de Windows y otras aplicaciones sobre este cambio de variable de entorno del sistema. Por lo tanto, el usuario debe reiniciar Windows, lo cual es mejor hacerlo siempre cambiando algo en las variables de entorno del sistema Windows almacenadas persistentemente.

El script por lotes se probó con PATH que actualmente contiene una ruta de carpeta con un signo de exclamación y una ruta de carpeta entre comillas dobles, lo cual es necesario solo si la ruta de la carpeta contiene un punto y coma.

Para comprender los comandos utilizados y cómo funcionan, abra una ventana del símbolo del sistema , ejecute allí los siguientes comandos y lea las páginas de ayuda que se muestran para cada comando, completa y cuidadosamente.

  • echo /?
  • endlocal /?
  • exit /?
  • for /?
  • goto /?
  • if /?
  • pause /?
  • reg /?
  • reg add /?
  • reg query /?
  • set /?
  • setlocal /?
  • setx /?
Mofi avatar Sep 18 '2014 17:09 Mofi