Existe-t-il une commande pour actualiser les variables d'environnement à partir de l'invite de commandes dans Windows?

481

Si je modifie ou ajoute une variable d'environnement, je dois redémarrer l'invite de commande. Y a-t-il une commande que je pourrais exécuter qui ferait cela sans redémarrer CMD?

Eric Schoonover
la source
38
En fait, chaque programme qui a besoin de les voir doit être redémarré. L'environnement est copié dans la mémoire du processus au démarrage et n'a donc plus de connexion avec les envvars définis par le système.
Joey
15
après avoir lu ces derniers, j'ai réalisé qu'il n'y a pas de cuillère ;) dans le monde réel, vous redémarrez simplement cmd.
n611x007
Pas une commande, donc pas tout à fait une réponse, mais elle est prise en charge à l'aide de l'API Win32 si je lis correctement ce qui suit: support.microsoft.com/en-us/help/104011/… Doit pouvoir compiler cette ligne dans un programme C simple et exécutez-le après les mises à jour des variables d'environnement.
Charles Grunwald
WM_SETTINGCHANGE (l'api win32 mentionnée par @CharlesGrunwald) ne fonctionne pas pour les fenêtres cmd.exe selon ce fil: github.com/chocolatey/choco/issues/1589 - c'est la raison pour laquelle ils ont écrit la commande
refreshenv

Réponses:

137

Vous pouvez capturer les variables d'environnement système avec un script vbs, mais vous avez besoin d'un script bat pour réellement changer les variables d'environnement actuelles, c'est donc une solution combinée.

Créez un fichier nommé resetvars.vbscontenant ce code et enregistrez-le sur le chemin:

Set oShell = WScript.CreateObject("WScript.Shell")
filename = oShell.ExpandEnvironmentStrings("%TEMP%\resetvars.bat")
Set objFileSystem = CreateObject("Scripting.fileSystemObject")
Set oFile = objFileSystem.CreateTextFile(filename, TRUE)

set oEnv=oShell.Environment("System")
for each sitem in oEnv 
    oFile.WriteLine("SET " & sitem)
next
path = oEnv("PATH")

set oEnv=oShell.Environment("User")
for each sitem in oEnv 
    oFile.WriteLine("SET " & sitem)
next

path = path & ";" & oEnv("PATH")
oFile.WriteLine("SET PATH=" & path)
oFile.Close

créez un autre nom de fichier resetvars.bat contenant ce code, même emplacement:

@echo off
%~dp0resetvars.vbs
call "%TEMP%\resetvars.bat"

Lorsque vous souhaitez actualiser les variables d'environnement, exécutez simplement resetvars.bat


Apologétique :

Les deux principaux problèmes que j'ai rencontrés avec cette solution étaient

une. Je n'ai pas pu trouver un moyen simple d'exporter des variables d'environnement d'un script vbs vers l'invite de commande, et

b. la variable d'environnement PATH est une concaténation de l'utilisateur et des variables système PATH.

Je ne sais pas quelle est la règle générale pour les variables en conflit entre l'utilisateur et le système, j'ai donc choisi de faire remplacer le système par l'utilisateur, sauf dans la variable PATH qui est gérée spécifiquement.

J'utilise le mécanisme étrange vbs + bat + bat temporaire pour contourner le problème de l'exportation de variables à partir de vbs.

Remarque : ce script ne supprime pas les variables.

Cela peut probablement être amélioré.

AJOUTÉE

Si vous devez exporter l'environnement d'une fenêtre cmd vers une autre, utilisez ce script (appelons-le exportvars.vbs):

Set oShell = WScript.CreateObject("WScript.Shell")
filename = oShell.ExpandEnvironmentStrings("%TEMP%\resetvars.bat")
Set objFileSystem = CreateObject("Scripting.fileSystemObject")
Set oFile = objFileSystem.CreateTextFile(filename, TRUE)

set oEnv=oShell.Environment("Process")
for each sitem in oEnv 
    oFile.WriteLine("SET " & sitem)
next
oFile.Close

Exécuter exportvars.vbsdans la fenêtre que vous voulez exporter à partir , puis passer à la fenêtre que vous souhaitez exporter vers et tapez:

"%TEMP%\resetvars.bat"
itsadok
la source
2
Vous pouvez peut-être éviter le fichier temporaire en utilisant les jetons FOR / F "= 1, *" %% c IN ('resetvars.vbs') DO
tzot
2
Comme je l'ai dit dans ma réponse "ou, ajoutez manuellement à l'aide de SET dans l'invite de commande existante." c'est ce que cela fait effectivement. Bonne réponse cependant.
Kev
2
@itsadok - étant donné que c'est maintenant la réponse acceptée, vous devez ajouter une brève explication au début pour mettre le script en contexte. c'est-à-dire qu'il n'est pas possible de propager une modification env var vers un cmd.exe ouvert sans mettre à jour manuellement comme ci-dessus ou en redémarrant cmd.exe.
Kev
Le script gère le cas d'utilisation de la modification globale des variables d'environnement dans "Poste de travail ... Variables d'environnement", mais si une variable d'environnement est modifiée dans un cmd.exe, le script ne la propage pas vers un autre cmd.exe en cours d'exécution qui, je pense, est probablement un scénario courant.
Kev
1
@Keyslinger: Ce n'est pas vraiment possible. Tout programme généré peut mettre à jour son propre environnement, mais pas celui de l'instance cmd.exe en cours d'exécution. Un fichier de commandes PEUT mettre à jour l'environnement cmd.exe, car il s'exécute dans la même instance de cmd.exe.
Ben Voigt
112

Voici ce que Chocolatey utilise.

https://github.com/chocolatey/choco/blob/master/src/chocolatey.resources/redirects/RefreshEnv.cmd

@echo off
::
:: RefreshEnv.cmd
::
:: Batch file to read environment variables from registry and
:: set session variables to these values.
::
:: With this batch file, there should be no need to reload command
:: environment every time you want environment changes to propagate

echo | set /p dummy="Reading environment variables from registry. Please wait... "

goto main

:: Set one environment variable from registry key
:SetFromReg
    "%WinDir%\System32\Reg" QUERY "%~1" /v "%~2" > "%TEMP%\_envset.tmp" 2>NUL
    for /f "usebackq skip=2 tokens=2,*" %%A IN ("%TEMP%\_envset.tmp") do (
        echo/set %~3=%%B
    )
    goto :EOF

:: Get a list of environment variables from registry
:GetRegEnv
    "%WinDir%\System32\Reg" QUERY "%~1" > "%TEMP%\_envget.tmp"
    for /f "usebackq skip=2" %%A IN ("%TEMP%\_envget.tmp") do (
        if /I not "%%~A"=="Path" (
            call :SetFromReg "%~1" "%%~A" "%%~A"
        )
    )
    goto :EOF

:main
    echo/@echo off >"%TEMP%\_env.cmd"

    :: Slowly generating final file
    call :GetRegEnv "HKLM\System\CurrentControlSet\Control\Session Manager\Environment" >> "%TEMP%\_env.cmd"
    call :GetRegEnv "HKCU\Environment">>"%TEMP%\_env.cmd" >> "%TEMP%\_env.cmd"

    :: Special handling for PATH - mix both User and System
    call :SetFromReg "HKLM\System\CurrentControlSet\Control\Session Manager\Environment" Path Path_HKLM >> "%TEMP%\_env.cmd"
    call :SetFromReg "HKCU\Environment" Path Path_HKCU >> "%TEMP%\_env.cmd"

    :: Caution: do not insert space-chars before >> redirection sign
    echo/set Path=%%Path_HKLM%%;%%Path_HKCU%% >> "%TEMP%\_env.cmd"

    :: Cleanup
    del /f /q "%TEMP%\_envset.tmp" 2>nul
    del /f /q "%TEMP%\_envget.tmp" 2>nul

    :: Set these variables
    call "%TEMP%\_env.cmd"

    echo | set /p dummy="Done"
    echo .
lâche anonyme
la source
66
+1 Si Chocolatey est installé, vous pouvez simplement exécuter RefreshEnvpour obtenir des variables d'environnement mises à jour dans votre session actuelle.
Martin Valgur
2
Il s'agit d'un logiciel utilitaire incroyablement utile, merci beaucoup pour le partage.
Sabuncu
10
Remarque: Chocolatey a déplacé les dépôts et la dernière version de ce script peut être trouvée ici (avec quelques corrections de bugs): github.com/chocolatey/choco/blob/master/src/…
Michael Burr
1
cela devrait-il fonctionner Powershellaussi? Cela ne semble fonctionner que cmd.exepour moi.
craq
1
Cela fonctionne pour moi dans PowerShell, @craq. Sous Windows10 x64.
mazunki
100

Sur Windows 7/8/10, vous pouvez installer Chocolatey, qui a un script pour ce intégré.

Après avoir installé Chocolatey, tapez simplement refreshenv.

gai
la source
ceci est une réponse valide, ce serait bien d'entendre un gars qui a
voté contre
2
Qu'est-ce que je fais mal? $> refreshenv 'refreshenv' n'est pas reconnu comme une commande interne ou externe, un programme exploitable ou un fichier de commandes.
aclokay
@aclokay Pas sûr. Veuillez fournir plus de détails sur votre configuration système pour déboguer. En attendant, vous pouvez vous référer à un problème ouvert similaire ici. github.com/chocolatey/choco/issues/250
jolly
Ça ne marche pas non plus pour moi. Je suis sur W7 professionnel, peut-être que cela ne fonctionne que dans des versions plus complètes.
alseether
1
Si refreshenv existe prématurément votre script (comme il l'a fait pour moi), vous pouvez utiliser "call RefreshEnv.cmd" à la place. (voir github.com/chocolatey/choco/issues/1461 )
sfiss
59

De par sa conception, il n'existe pas de mécanisme intégré pour Windows pour propager une variable d'environnement ajouter / modifier / supprimer à un cmd.exe déjà en cours d'exécution, à partir d'un autre cmd.exe ou de "Poste de travail -> Propriétés -> Paramètres avancés -> Variables d'environnement".

Si vous modifiez ou ajoutez une nouvelle variable d'environnement en dehors de la portée d'une invite de commandes ouverte existante, vous devez soit redémarrer l'invite de commandes, soit ajouter manuellement à l'aide de SET dans l'invite de commandes existante.

La dernière réponse acceptée montre une solution de contournement partielle en actualisant manuellement toutes les variables d'environnement dans un script. Le script gère le cas d'utilisation de la modification globale des variables d'environnement dans "Poste de travail ... Variables d'environnement", mais si une variable d'environnement est modifiée dans un cmd.exe, le script ne la propage pas vers un autre cmd.exe en cours d'exécution.

Kev
la source
Si quelqu'un a une solution de travail à ce problème, je vérifierai périodiquement et je pourrai changer la réponse acceptée.
Eric Schoonover
1
Cela ne devrait pas être la réponse acceptée simplement parce qu'elle ne répond pas à la question posée. cette question doit rester sans réponse acceptée jusqu'à ce qu'elle soit trouvée.
shoosh
4
Et ennuyeusement, les instances supplémentaires de cmd.exe ne comptent pas. Ils doivent tous être supprimés avant que la modification ne soit reflétée dans les nouveaux cmd.exe.
6
Les commentaires négatifs et le marquage vers le bas de cette réponse montrent à quel point le débordement de pile est cassé parfois. Kev a donné la bonne réponse. Ce n'est pas parce que vous ne l'aimez pas qu'il faut le noter.
David Arno
Kev répond définitivement à la question. La question est qu'il n'y a pas de solution intégrée.
Eric Schoonover
40

Je suis tombé sur cette réponse avant de trouver finalement une solution plus simple.

Redémarrez simplement explorer.exedans le Gestionnaire des tâches.

Je n'ai pas testé, mais vous devrez peut-être également rouvrir votre invite de commande.

Remerciements à Timo Huovinen ici: Noeud non reconnu bien installé avec succès (si cela vous a aidé, veuillez donner le crédit du commentaire de cet homme).

wharding28
la source
Je suis arrivé ici parce que j'essayais d'ajouter un outil externe à Visual Studio pour pouvoir ouvrir une invite de commande à la racine de ma solution, comme décrit dans ce blog: neverindoubtnet.blogspot.com/2012/10/… ... et J'ai eu des problèmes similaires ... J'essayais de faire apparaître "git" dans ma variable de chemin. J'ai ajouté les répertoires git à la variable PATH, mais ils n'apparaîtraient pas dans l'invite de commande que j'ouvrirais à partir de Visual Studio. La solution simple était de redémarrer Visual Studio. Ensuite, les nouveaux ajouts à la variable PATH étaient visibles en cmd.
David Barrows
4
Cette solution m'aide dans Windows 10
ganchito55
7
La question était: "Existe-t-il une commande que je pourrais exécuter qui ferait cela sans redémarrer CMD ?"
Florian F
Ok depuis le gestionnaire de tâches, je n'ai pas pu redémarrer explorer.exe, seulement le terminer. Je l'ai fait mais ma barre des tâches a été cassée. Pour démarrer l'explorateur, c'est vraiment simple. Laissez "Ctrl + shift + escape" -> fichier -> "exécuter une nouvelle tâche" -> "explorer.exe" a fait le travail pour moi. Et oui, après tout, env var a été utilisé dans la nouvelle fenêtre cmd. Merci pour tous
Oscar
Bonne solution, merci! Pour développer et répondre au commentaire de @Oscar: Ouvrez une cmdfenêtre en tant qu'administrateur. Utilisez la commande taskkill /f /im explorer.exe && explorer.exe. Cela tuera le processus explorer.exe et le redémarrera.
S3DEV
32

Cela fonctionne sur Windows 7: SET PATH=%PATH%;C:\CmdShortcuts

testé en tapant echo% PATH% et cela a fonctionné, très bien. également défini si vous ouvrez une nouvelle cmd, plus besoin de ces redémarrages embêtants :)

kristofer månsson
la source
1
Ne fonctionne pas pour "nouveau cmd" pour moi (Win7 x64). Voir la vidéo de l'écran
Igor
26
Cela ne résout pas la question posée et ne devrait pas non plus le faire. La question d'origine est de savoir comment actualiser une variable d'environnement à une valeur qui a été définie en dehors de ce terminal.
csauve
Bien que cela ne réponde pas à la question, il fournit la moitié de la meilleure solution de travail. Je l'utilise - pour n'importe quelle variable que je définis - puis j'ouvre le panneau de contrôle et j'ajoute la variable d'environnement globalement. Je n'aime pas utiliser setxcar il hérite de l'environnement actuel qui peut avoir des variables qui ont été modifiées et pas ce que je veux en permanence. Le faire de cette façon me permet d'éviter de redémarrer la console afin d'utiliser les variables, tout en évitant le problème de ne pas les avoir disponibles globalement à l'avenir.
dgo
25

Utilisez "setx" et redémarrez l'invite cmd

Il existe un outil en ligne de commande nommé " setx " pour ce travail. C'est pour lire et écrire des variables env. Les variables persistent après la fermeture de la fenêtre de commande.

Il "crée ou modifie des variables d'environnement dans l'environnement utilisateur ou système, sans nécessiter de programmation ou de script. La commande setx récupère également les valeurs des clés de registre et les écrit dans des fichiers texte."

Remarque: les variables créées ou modifiées par cet outil seront disponibles dans les futures fenêtres de commandes mais pas dans la fenêtre de commandes CMD.exe actuelle. Vous devez donc redémarrer.

S'il setxmanque:


Ou modifiez le registre

MSDN dit:

Pour ajouter ou modifier par programme des variables d'environnement système, ajoutez-les à la clé de Registre HKEY_LOCAL_MACHINE \ System \ CurrentControlSet \ Control \ Session Manager \ Environment , puis diffusez un message WM_SETTINGCHANGE avec lParam défini sur la chaîne " Environment ".

Cela permet aux applications, telles que le shell, de récupérer vos mises à jour.

Jens A. Koch
la source
1
Pourriez-vous développer comment utiliser setx pour lire une variable d'environnement? J'ai parcouru les divers documents et je ne les vois tout simplement pas. : - /
Mark Ribau
2
setx VARIABLE -k "HKEY_LOCAL_MACHINE \ Software \ Microsoft \ WindowsNT \ CurrentVersion \ CurrentVersion" echo% VARIABLE%
Jens A. Koch
3
environnement système actuel: environnement HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\VARIABLEutilisateur actuel: HKEY_CURRENT_USER\Environment\VARIABLE
Mark Ribau
5
setx /? dit dans une note: "Sur un système local, les variables créées ou modifiées par cet outil seront disponibles dans les futures fenêtres de commandes mais pas dans la fenêtre de commandes CMD.exe actuelle ." OP voulait mettre à jour la cmd actuelle.
Superole
4
Acheteur, méfiez-vous! Si vous avez un particulièrement long %PATH%alors setxpeut tronquer cette 1024 octets! Et juste comme ça, sa soirée a disparu
FaCE
15

L'appel de cette fonction a fonctionné pour moi:

VOID Win32ForceSettingsChange()
{
    DWORD dwReturnValue;
    ::SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM) "Environment", SMTO_ABORTIFHUNG, 5000, &dwReturnValue);
}
Brian Weed
la source
8
et tous les programmes n'écoutent pas ce message (en fait, la plupart d'entre eux ne le font probablement pas)
Rehan Khwaja
Non, cela fonctionne également pour les programmes non GUI. En ce qui concerne les programmes d'écoute ... le problème est de s'assurer qu'un programme redémarré recevra l'environnement mis à jour, et cela vous donne cela.
user2023370
11

La meilleure méthode que j'ai trouvée était de simplement faire une requête de registre. Voici mon exemple.

Dans mon exemple, j'ai effectué une installation à l'aide d'un fichier batch qui a ajouté de nouvelles variables d'environnement. J'avais besoin de faire des choses avec cela dès que l'installation était terminée, mais je n'ai pas pu générer un nouveau processus avec ces nouvelles variables. J'ai testé la création d'une autre fenêtre d'explorateur et j'ai rappelé cmd.exe et cela a fonctionné, mais sur Vista et Windows 7, Explorer ne fonctionne que comme une seule instance et normalement en tant que personne connectée. Cela échouerait avec l'automatisation car j'ai besoin de mes crédits d'administrateur faire des choses indépendamment de l'exécution à partir du système local ou en tant qu'administrateur sur la boîte. La limitation à cela est qu'il ne gère pas des choses comme le chemin, cela ne fonctionnait que sur des variables d'environnement simples. Cela m'a permis d'utiliser un lot pour accéder à un répertoire (avec des espaces) et copier dans des fichiers exécutés .exes et etc. Cela a été écrit aujourd'hui à partir des ressources de mai sur stackoverflow.com

Appels de lot d'origine vers un nouveau lot:

testenvget.cmd SDROOT (ou quelle que soit la variable)

@ECHO OFF
setlocal ENABLEEXTENSIONS
set keyname=HKLM\System\CurrentControlSet\Control\Session Manager\Environment
set value=%1
SET ERRKEY=0

REG QUERY "%KEYNAME%" /v "%VALUE%" 2>NUL| FIND /I "%VALUE%"
IF %ERRORLEVEL% EQU 0 (
ECHO The Registry Key Exists 
) ELSE (
SET ERRKEY=1
Echo The Registry Key Does not Exist
)

Echo %ERRKEY%
IF %ERRKEY% EQU 1 GOTO :ERROR

FOR /F "tokens=1-7" %%A IN ('REG QUERY "%KEYNAME%" /v "%VALUE%" 2^>NUL^| FIND /I "%VALUE%"') DO (
ECHO %%A
ECHO %%B
ECHO %%C
ECHO %%D
ECHO %%E
ECHO %%F
ECHO %%G
SET ValueName=%%A
SET ValueType=%%B
SET C1=%%C
SET C2=%%D
SET C3=%%E
SET C4=%%F
SET C5=%%G
)

SET VALUE1=%C1% %C2% %C3% %C4% %C5%
echo The Value of %VALUE% is %C1% %C2% %C3% %C4% %C5%
cd /d "%VALUE1%"
pause
REM **RUN Extra Commands here**
GOTO :EOF

:ERROR
Echo The the Enviroment Variable does not exist.
pause
GOTO :EOF

Il y a aussi une autre méthode que j'ai imaginée à partir de différentes idées. Veuillez voir ci-dessous. Cela obtiendra essentiellement la variable de chemin la plus récente du registre.Cependant, cela entraînera un certain nombre de problèmes car la requête de registre va donner des variables en soi, ce qui signifie que partout où il y a une variable, cela ne fonctionnera pas, donc pour lutter contre ce problème, je essentiellement doubler le chemin. Très sale. La méthode la plus avantageuse serait de faire: Définir le chemin d'accès =% Chemin d'accès%; C: \ Program Files \ Software .... \

Quoi qu'il en soit, voici le nouveau fichier de commandes, veuillez faire preuve de prudence.

@ECHO OFF
SETLOCAL ENABLEEXTENSIONS
set org=%PATH%
for /f "tokens=2*" %%A in ('REG QUERY "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /v Path ^|FIND /I "Path"') DO (
SET path=%%B
)
SET PATH=%org%;%PATH%
set path
Christopher Holmes
la source
8

La façon la plus simple d'ajouter une variable au chemin sans redémarrer pour la session en cours est d'ouvrir l'invite de commande et de taper:

PATH=(VARIABLE);%path%

et appuyez sur enter.

pour vérifier si votre variable est chargée, tapez

PATH

et appuyez sur enter. Cependant, la variable ne sera qu'une partie du chemin jusqu'au redémarrage.

Richard Woodruff
la source
n'a pas fonctionné pour moi, impossible d'accéder aux fichiers binaires dans la variable de chemin
user2305193
7

Il est possible de le faire en remplaçant la table d'environnement dans un processus spécifié lui-même.

Comme preuve de concept, j'ai écrit cet exemple d'application, qui vient de modifier une seule variable d'environnement (connue) dans un processus cmd.exe:

typedef DWORD (__stdcall *NtQueryInformationProcessPtr)(HANDLE, DWORD, PVOID, ULONG, PULONG);

int __cdecl main(int argc, char* argv[])
{
    HMODULE hNtDll = GetModuleHandleA("ntdll.dll");
    NtQueryInformationProcessPtr NtQueryInformationProcess = (NtQueryInformationProcessPtr)GetProcAddress(hNtDll, "NtQueryInformationProcess");

    int processId = atoi(argv[1]);
    printf("Target PID: %u\n", processId);

    // open the process with read+write access
    HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, 0, processId);
    if(hProcess == NULL)
    {
        printf("Error opening process (%u)\n", GetLastError());
        return 0;
    }

    // find the location of the PEB
    PROCESS_BASIC_INFORMATION pbi = {0};
    NTSTATUS status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), NULL);
    if(status != 0)
    {
        printf("Error ProcessBasicInformation (0x%8X)\n", status);
    }
    printf("PEB: %p\n", pbi.PebBaseAddress);

    // find the process parameters
    char *processParamsOffset = (char*)pbi.PebBaseAddress + 0x20; // hard coded offset for x64 apps
    char *processParameters = NULL;
    if(ReadProcessMemory(hProcess, processParamsOffset, &processParameters, sizeof(processParameters), NULL))
    {
        printf("UserProcessParameters: %p\n", processParameters);
    }
    else
    {
        printf("Error ReadProcessMemory (%u)\n", GetLastError());
    }

    // find the address to the environment table
    char *environmentOffset = processParameters + 0x80; // hard coded offset for x64 apps
    char *environment = NULL;
    ReadProcessMemory(hProcess, environmentOffset, &environment, sizeof(environment), NULL);
    printf("environment: %p\n", environment);

    // copy the environment table into our own memory for scanning
    wchar_t *localEnvBlock = new wchar_t[64*1024];
    ReadProcessMemory(hProcess, environment, localEnvBlock, sizeof(wchar_t)*64*1024, NULL);

    // find the variable to edit
    wchar_t *found = NULL;
    wchar_t *varOffset = localEnvBlock;
    while(varOffset < localEnvBlock + 64*1024)
    {
        if(varOffset[0] == '\0')
        {
            // we reached the end
            break;
        }
        if(wcsncmp(varOffset, L"ENVTEST=", 8) == 0)
        {
            found = varOffset;
            break;
        }
        varOffset += wcslen(varOffset)+1;
    }

    // check to see if we found one
    if(found)
    {
        size_t offset = (found - localEnvBlock) * sizeof(wchar_t);
        printf("Offset: %Iu\n", offset);

        // write a new version (if the size of the value changes then we have to rewrite the entire block)
        if(!WriteProcessMemory(hProcess, environment + offset, L"ENVTEST=def", 12*sizeof(wchar_t), NULL))
        {
            printf("Error WriteProcessMemory (%u)\n", GetLastError());
        }
    }

    // cleanup
    delete[] localEnvBlock;
    CloseHandle(hProcess);

    return 0;
}

Exemple de sortie:

>set ENVTEST=abc

>cppTest.exe 13796
Target PID: 13796
PEB: 000007FFFFFD3000
UserProcessParameters: 00000000004B2F30
environment: 000000000052E700
Offset: 1528

>set ENVTEST
ENVTEST=def

Remarques

Cette approche serait également limitée aux restrictions de sécurité. Si la cible est exécutée à une altitude plus élevée ou sur un compte supérieur (tel que SYSTEM), nous n'aurions pas la permission de modifier sa mémoire.

Si vous vouliez le faire pour une application 32 bits, les décalages codés en dur ci-dessus changeraient respectivement en 0x10 et 0x48. Ces décalages peuvent être trouvés en vidant les structures _PEB et _RTL_USER_PROCESS_PARAMETERS dans un débogueur (par exemple dans WinDbg dt _PEBet dt _RTL_USER_PROCESS_PARAMETERS)

Pour changer la preuve de concept en ce dont l'OP a besoin, il suffit d'énumérer les variables d'environnement système et utilisateur actuelles (telles que documentées par la réponse de @ tsadok) et d'écrire toute la table d'environnement dans la mémoire du processus cible.

Edit: La taille du bloc d'environnement est également stockée dans la structure _RTL_USER_PROCESS_PARAMETERS, mais la mémoire est allouée sur le tas du processus. Donc, à partir d'un processus externe, nous n'aurions pas la possibilité de le redimensionner et de l'agrandir. J'ai joué avec VirtualAllocEx pour allouer de la mémoire supplémentaire dans le processus cible pour le stockage de l'environnement, et j'ai pu définir et lire une table entièrement nouvelle. Malheureusement, toute tentative de modification de l'environnement à partir de moyens normaux se bloquera et brûlera car l'adresse ne pointe plus vers le tas (elle se bloquera dans RtlSizeHeap).

josh poley
la source
6

Les variables d'environnement sont conservées dans HKEY_LOCAL_MACHINE \ SYSTEM \ ControlSet \ Control \ Session Manager \ Environment.

La plupart des variables env utiles, telles que Path, sont stockées en tant que REG_SZ. Il existe plusieurs façons d'accéder au registre, notamment REGEDIT:

REGEDIT /E &lt;filename&gt; "HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Session Manager\Environment"

La sortie commence par des nombres magiques. Donc, pour le rechercher avec la commande find, il doit être tapé et redirigé:type <filename> | findstr -c:\"Path\"

Donc, si vous souhaitez simplement actualiser la variable de chemin dans votre session de commande actuelle avec ce qui se trouve dans les propriétés système, le script de commandes suivant fonctionne correctement:

RefreshPath.cmd:

    @Écho off

    REM Cette solution demande une élévation afin de lire à partir du registre.

    s'il existe% temp% \ env.reg del% temp% \ env.reg / q / f

    REGEDIT / E% temp% \ env.reg "HKEY_LOCAL_MACHINE \ SYSTEM \ ControlSet001 \ Control \ Session Manager \ Environment"

    s'il n'existe pas% temp% \ env.reg (
       echo "Impossible d'écrire le registre à l'emplacement temporaire"
       sortie 1
       )

    SETLOCAL EnableDelayedExpansion

    pour / f "tokens = 1,2 * delims ==" %% i in ('type% temp% \ env.reg ^ | findstr -c: \ "Path \" =') do (
       set upath = %% ~ j
       echo! upath: \\ = \! >% temp% \ newpath
       )

     ENDLOCAL

     pour / f "tokens = *" %% i in (% temp% \ newpath), définissez path = %% i
Algonaut
la source
5
Les variables d'environnement ne sont pas conservées dans le registre. Ce qui est conservé dans le registre est un modèle , à partir duquel des programmes comme l'Explorateur Windows (re) construisent leurs variables d'environnement lorsqu'ils sont invités à le faire . Les variables d'environnement réelles sont par processus et sont stockées dans le propre espace d'adressage de chaque processus, initialement héritées de son processus parent et modifiables par la suite au gré du processus.
JdeBP
5

Essayez d'ouvrir une nouvelle invite de commande en tant qu'administrateur. Cela a fonctionné pour moi sur Windows 10. (Je sais que c'est une ancienne réponse, mais j'ai dû la partager car avoir à écrire un script VBS juste pour cela est absurde).

estebro
la source
5

Le redémarrage de l'explorateur l'a fait pour moi, mais uniquement pour les nouveaux terminaux cmd.

Le terminal sur lequel j'ai défini le chemin pouvait déjà voir la nouvelle variable Path (dans Windows 7).

taskkill /f /im explorer.exe && explorer.exe
Vince
la source
5

La chose déroutante pourrait être qu'il existe quelques endroits pour démarrer la cmd. Dans mon cas, j'ai exécuté cmd à partir de l'explorateur Windows et les variables d' environnement n'ont pas changé tandis qu'au démarrage de cmd à partir de la «course» (touche Windows + r) les variables d' environnement ont été modifiées .

Dans mon cas, je venais de tuer le processus de l'explorateur Windows à partir du gestionnaire de tâches, puis de le redémarrer à nouveau à partir du gestionnaire de tâches .

Une fois que j'ai fait cela, j'ai eu accès à la nouvelle variable d'environnement à partir d'une cmd générée à partir de l'explorateur Windows.

Daniel Fensterheim
la source
3

J'utilise le code suivant dans mes scripts batch:

if not defined MY_ENV_VAR (
    setx MY_ENV_VAR "VALUE" > nul
    set MY_ENV_VAR=VALUE
)
echo %MY_ENV_VAR%

En utilisant SET après SETX, il est possible d'utiliser directement la variable "locale" sans redémarrer la fenêtre de commande. Et lors de la prochaine exécution, la variable d'environnement sera utilisée.

Sébastien
la source
Pendant que je comprends ce que vous avez fait, il veut probablement quelque chose pour des scripts parallèles, un script définit des globaux tandis qu'un autre les lit. Sinon, il est inutile d'impliquer setx, set serait suffisant.
JasonXA
3

J'ai aimé l'approche suivie par le chocolat, comme indiqué dans la réponse lâche anonyme, car il s'agit d'une approche par lots pure. Cependant, il laisse un fichier temporaire et quelques variables temporaires qui traînent. Je me suis fait une version plus propre.

Créez un fichier refreshEnv.batquelque part sur votre PATH. Actualisez votre environnement de console en exécutant refreshEnv.

@ECHO OFF
REM Source found on https://github.com/DieterDePaepe/windows-scripts
REM Please share any improvements made!

REM Code inspired by http://stackoverflow.com/questions/171588/is-there-a-command-to-refresh-environment-variables-from-the-command-prompt-in-w

IF [%1]==[/?] GOTO :help
IF [%1]==[/help] GOTO :help
IF [%1]==[--help] GOTO :help
IF [%1]==[] GOTO :main

ECHO Unknown command: %1
EXIT /b 1 

:help
ECHO Refresh the environment variables in the console.
ECHO.
ECHO   refreshEnv       Refresh all environment variables.
ECHO   refreshEnv /?        Display this help.
GOTO :EOF

:main
REM Because the environment variables may refer to other variables, we need a 2-step approach.
REM One option is to use delayed variable evaluation, but this forces use of SETLOCAL and
REM may pose problems for files with an '!' in the name.
REM The option used here is to create a temporary batch file that will define all the variables.

REM Check to make sure we don't overwrite an actual file.
IF EXIST %TEMP%\__refreshEnvironment.bat (
  ECHO Environment refresh failed!
  ECHO.
  ECHO This script uses a temporary file "%TEMP%\__refreshEnvironment.bat", which already exists. The script was aborted in order to prevent accidental data loss. Delete this file to enable this script.
  EXIT /b 1
)

REM Read the system environment variables from the registry.
FOR /F "usebackq tokens=1,2,* skip=2" %%I IN (`REG QUERY "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment"`) DO (
  REM /I -> ignore casing, since PATH may also be called Path
  IF /I NOT [%%I]==[PATH] (
    ECHO SET %%I=%%K>>%TEMP%\__refreshEnvironment.bat
  )
)

REM Read the user environment variables from the registry.
FOR /F "usebackq tokens=1,2,* skip=2" %%I IN (`REG QUERY HKCU\Environment`) DO (
  REM /I -> ignore casing, since PATH may also be called Path
  IF /I NOT [%%I]==[PATH] (
    ECHO SET %%I=%%K>>%TEMP%\__refreshEnvironment.bat
  )
)

REM PATH is a special variable: it is automatically merged based on the values in the
REM system and user variables.
REM Read the PATH variable from the system and user environment variables.
FOR /F "usebackq tokens=1,2,* skip=2" %%I IN (`REG QUERY "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /v PATH`) DO (
  ECHO SET PATH=%%K>>%TEMP%\__refreshEnvironment.bat
)
FOR /F "usebackq tokens=1,2,* skip=2" %%I IN (`REG QUERY HKCU\Environment /v PATH`) DO (
  ECHO SET PATH=%%PATH%%;%%K>>%TEMP%\__refreshEnvironment.bat
)

REM Load the variable definitions from our temporary file.
CALL %TEMP%\__refreshEnvironment.bat

REM Clean up after ourselves.
DEL /Q %TEMP%\__refreshEnvironment.bat

ECHO Environment successfully refreshed.
DieterDP
la source
est-ce aussi pour% CLIENTNAME%? - n'a pas fonctionné pour moi - stackoverflow.com/questions/37550160/…
Igor L.
% CLIENTNAME% n'est pas disponible dans mon environnement, et en lisant votre question, je vais supposer que c'est quelque chose défini par un processus externe. (Lorsqu'un processus démarre un processus enfant, il est capable d'ajuster l'environnement pour cet enfant.) Comme il ne fait pas partie des variables d'environnement réelles, il ne sera pas mis à jour par ce script.
DieterDP
Salut @DieterDP, votre solution fonctionne pour moi! J'utilise Windows 10 sur une machine 64 bits. J'obtiens une erreur: "ERREUR: le système n'a pas pu trouver la clé ou la valeur de registre spécifiée.". Néanmoins, la mise à jour des variables d'environnement est réussie. D'où vient l'erreur?
K.Mulier
Difficile à dire sans le tester moi-même, mais je suppose que la structure du registre sur W10 peut légèrement différer. Si vous en avez envie, essayez de rechercher l'erreur en exécutant les commandes sur la ligne de commande.
DieterDP
2

Si cela ne concerne qu'un (ou quelques) vars spécifiques que vous souhaitez modifier, je pense que le moyen le plus simple est une solution de contournement : il suffit de l'installer dans votre environnement ET dans votre session de console actuelle

  • Set mettra le var dans votre session en cours
  • SetX mettra le var dans l'environnement, mais PAS dans votre session actuelle

J'ai ce simple script batch pour changer mon Maven de java7 à Java8 (qui sont à la fois env. Vars) Le lot dossier est dans mon PATH var donc je peux toujours appeler « J8 » et dans ma console et dans l'environnement mon JAVA_HOME var se change:

j8.bat:

@echo off
set JAVA_HOME=%JAVA_HOME_8%
setx JAVA_HOME "%JAVA_HOME_8%"

Jusqu'à présent, je trouve que cela fonctionne mieux et plus facilement. Vous voulez probablement que cela soit dans une seule commande, mais ce n'est tout simplement pas là dans Windows ...

Jeroen van Dijk-Jun
la source
2

La solution que j'utilise depuis quelques années maintenant:

@echo off
rem Refresh PATH from registry.
setlocal
set USR_PATH=
set SYS_PATH=
for /F "tokens=3* skip=2" %%P in ('%SystemRoot%\system32\reg.exe query "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /v PATH') do @set "SYS_PATH=%%P %%Q"
for /F "tokens=3* skip=2" %%P in ('%SystemRoot%\system32\reg.exe query "HKCU\Environment" /v PATH') do @set "USR_PATH=%%P %%Q"
if "%SYS_PATH:~-1%"==" " set "SYS_PATH=%SYS_PATH:~0,-1%"
if "%USR_PATH:~-1%"==" " set "USR_PATH=%USR_PATH:~0,-1%"
endlocal & call set "PATH=%SYS_PATH%;%USR_PATH%"
goto :EOF

Edit: Woops, voici la version mise à jour.

Charles Grunwald
la source
J'aime ta réponse. Publier la même réponse à ma question ici stackoverflow.com/q/61473551/1082063 et je l' accepter comme la réponse. Merci.
David I. McIntosh
1

Il n'y a pas de chemin droit, comme l'a dit Kev. Dans la plupart des cas, il est plus simple de générer une autre boîte CMD. Plus gênant, les programmes en cours d'exécution ne sont pas non plus au courant des changements (bien que l'IIRC puisse avoir un message diffusé à surveiller pour être notifié d'un tel changement).

Cela a été pire: dans les anciennes versions de Windows, vous deviez vous déconnecter puis vous reconnecter pour prendre en compte les changements ...

PhiLho
la source
1

J'utilise ce script Powershell pour ajouter à la variable PATH . Avec un petit ajustement, cela peut aussi fonctionner dans votre cas, je crois.

#REQUIRES -Version 3.0

if (-not ("win32.nativemethods" -as [type])) {
    # import sendmessagetimeout from win32
    add-type -Namespace Win32 -Name NativeMethods -MemberDefinition @"
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr SendMessageTimeout(
   IntPtr hWnd, uint Msg, UIntPtr wParam, string lParam,
   uint fuFlags, uint uTimeout, out UIntPtr lpdwResult);
"@
}

$HWND_BROADCAST = [intptr]0xffff;
$WM_SETTINGCHANGE = 0x1a;
$result = [uintptr]::zero

function global:ADD-PATH
{
    [Cmdletbinding()]
    param ( 
        [parameter(Mandatory=$True, ValueFromPipeline=$True, Position=0)] 
        [string] $Folder
    )

    # See if a folder variable has been supplied.
    if (!$Folder -or $Folder -eq "" -or $Folder -eq $null) { 
        throw 'No Folder Supplied. $ENV:PATH Unchanged'
    }

    # Get the current search path from the environment keys in the registry.
    $oldPath=$(Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH).Path

    # See if the new Folder is already in the path.
    if ($oldPath | Select-String -SimpleMatch $Folder){ 
        return 'Folder already within $ENV:PATH' 
    }

    # Set the New Path and add the ; in front
    $newPath=$oldPath+';'+$Folder
    Set-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH -Value $newPath -ErrorAction Stop

    # Show our results back to the world
    return 'This is the new PATH content: '+$newPath

    # notify all windows of environment block change
    [win32.nativemethods]::SendMessageTimeout($HWND_BROADCAST, $WM_SETTINGCHANGE, [uintptr]::Zero, "Environment", 2, 5000, [ref]$result)
}

function global:REMOVE-PATH {
    [Cmdletbinding()]
    param ( 
        [parameter(Mandatory=$True, ValueFromPipeline=$True, Position=0)]
        [String] $Folder
    )

    # See if a folder variable has been supplied.
    if (!$Folder -or $Folder -eq "" -or $Folder -eq $NULL) { 
        throw 'No Folder Supplied. $ENV:PATH Unchanged'
    }

    # add a leading ";" if missing
    if ($Folder[0] -ne ";") {
        $Folder = ";" + $Folder;
    }

    # Get the Current Search Path from the environment keys in the registry
    $newPath=$(Get-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH).Path

    # Find the value to remove, replace it with $NULL. If it's not found, nothing will change and you get a message.
    if ($newPath -match [regex]::Escape($Folder)) { 
        $newPath=$newPath -replace [regex]::Escape($Folder),$NULL 
    } else { 
        return "The folder you mentioned does not exist in the PATH environment" 
    }

    # Update the Environment Path
    Set-ItemProperty -Path 'Registry::HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment' -Name PATH -Value $newPath -ErrorAction Stop

    # Show what we just did
    return 'This is the new PATH content: '+$newPath

    # notify all windows of environment block change
    [win32.nativemethods]::SendMessageTimeout($HWND_BROADCAST, $WM_SETTINGCHANGE, [uintptr]::Zero, "Environment", 2, 5000, [ref]$result)
}


# Use ADD-PATH or REMOVE-PATH accordingly.

#Anything to Add?

#Anything to Remove?

REMOVE-PATH "%_installpath_bin%"
Iulian Dita
la source
1

Merci de poster cette question qui est assez intéressante, même en 2019 (en effet, il n'est pas facile de renouveler le shell cmd car il s'agit d'une seule instance comme mentionné ci-dessus), car le renouvellement des variables d'environnement dans windows permet d'accomplir de nombreuses tâches d'automatisation sans avoir à redémarrer manuellement la ligne de commande.

Par exemple, nous l'utilisons pour permettre le déploiement et la configuration de logiciels sur un grand nombre de machines que nous réinstallons régulièrement. Et je dois admettre que devoir redémarrer la ligne de commande lors du déploiement de notre logiciel serait très peu pratique et nous obligerait à trouver des solutions de contournement qui ne sont pas forcément agréables. Passons à notre problème. Nous procédons comme suit.

1 - Nous avons un script batch qui à son tour appelle un script PowerShell comme celui-ci

[fichier: task.cmd] .

cmd > powershell.exe -executionpolicy unrestricted -File C:\path_here\refresh.ps1

2 - Après cela, le script refresh.ps1 renouvelle les variables d'environnement à l'aide de clés de registre (GetValueNames (), etc.). Ensuite, dans le même script powershell, il suffit d'appeler les nouvelles variables d'environnement disponibles. Par exemple, dans un cas typique, si nous venons d'installer nodeJS auparavant avec cmd en utilisant des commandes silencieuses, après avoir appelé la fonction, nous pouvons appeler directement npm pour installer, dans la même session, des packages particuliers comme suit.

[fichier: refresh.ps1]

function Update-Environment {
    $locations = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session  Manager\Environment',
                 'HKCU:\Environment'
    $locations | ForEach-Object {
        $k = Get-Item $_
        $k.GetValueNames() | ForEach-Object {
            $name  = $_
            $value = $k.GetValue($_)

            if ($userLocation -and $name -ieq 'PATH') {
                $env:Path += ";$value"
            } else {

                Set-Item -Path Env:\$name -Value $value
            }
        }
        $userLocation = $true
    }
}
Update-Environment
#Here we can use newly added environment variables like for example npm install.. 
npm install -g create-react-app serve

Une fois le script powershell terminé, le script cmd continue avec d'autres tâches. Maintenant, une chose à garder à l'esprit est qu'après la fin de la tâche, cmd n'a toujours pas accès aux nouvelles variables d'environnement, même si le script powershell a mis à jour celles de sa propre session. C'est pourquoi nous faisons toutes les tâches nécessaires dans le script powershell qui peut appeler les mêmes commandes que cmd bien sûr.

Andy McRae
la source
0

Modifier: cela ne fonctionne que si les changements d'environnement que vous effectuez résultent de l'exécution d'un fichier de commandes.

Si un fichier de commandes commence par, SETLOCALil se détachera toujours vers votre environnement d'origine à la sortie même si vous oubliez d'appelerENDLOCAL avant la fermeture du lot, ou s'il s'arrête de manière inattendue.

Presque tous les fichiers batch que j'écris commencent par, SETLOCALcar dans la plupart des cas, je ne veux pas que les effets secondaires des changements d'environnement restent. Dans les cas où je souhaite que certaines modifications de variable d'environnement se propagent en dehors du fichier de commandes, ma dernière ENDLOCALressemble à ceci:

ENDLOCAL & (
  SET RESULT1=%RESULT1%
  SET RESULT2=%RESULT2%
)
gardiens
la source
-1

Pour résoudre ce problème, j'ai modifié la variable d'environnement à l'aide des DEUX setx et set, puis redémarré toutes les instances de explorer.exe. De cette façon, tout processus démarré par la suite aura la nouvelle variable d'environnement.

Mon script batch pour ce faire:

setx /M ENVVAR "NEWVALUE"
set ENVVAR="NEWVALUE"

taskkill /f /IM explorer.exe
start explorer.exe >nul
exit

Le problème avec cette approche est que toutes les fenêtres d'explorateur actuellement ouvertes seront fermées, ce qui est probablement une mauvaise idée - Mais consultez le post de Kev pour savoir pourquoi cela est nécessaire

Jens Hykkelbjerg
la source