Esiste un comando per aggiornare le variabili di ambiente dal prompt dei comandi in Windows?



12 Answers

Ecco cosa usa Chocolatey.

https://github.com/chocolatey/chocolatey/blob/master/src/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 .
Question

Se modifico o aggiungo una variabile di ambiente, devo riavviare il prompt dei comandi. Esiste un comando che potrei eseguire in questo modo senza riavviare CMD?




To solve this I have changed the environment variable using BOTH setx and set, and then restarted all instances of explorer.exe. This way any process subsequently started will have the new environment variable.

My batch script to do this:

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

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

The problem with this approach is that all explorer windows that are currently opened will be closed, which is probably a bad idea - But see the post by Kev to learn why this is necessary




È possibile farlo sovrascrivendo la tabella dell'ambiente all'interno di un processo specificato stesso.

Come prova del concetto ho scritto questa app di esempio, che ha appena modificato una singola variabile di ambiente (nota) in un processo 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;
}

Uscita di esempio:

>set ENVTEST=abc

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

>set ENVTEST
ENVTEST=def

Gli appunti

Questo approccio sarebbe anche limitato alle restrizioni di sicurezza. Se il bersaglio viene eseguito a quote più elevate o ad un account superiore (come SYSTEM), non avremmo il permesso di modificare la sua memoria.

Se si desidera eseguire questa operazione in un'app a 32 bit, gli offset hard codificati sopra passeranno rispettivamente a 0x10 e 0x48. Questi offset possono essere trovati scaricando le strutture _PEB e _RTL_USER_PROCESS_PARAMETERS in un debugger (ad esempio in WinDbg dt _PEB e dt _RTL_USER_PROCESS_PARAMETERS )

Per cambiare la dimostrazione di concetto in una cosa di cui l'OP ha bisogno, sarebbe sufficiente enumerare le variabili attuali di ambiente di sistema e utente (come documentato dalla risposta di @ tsadok) e scrivere l'intera tabella di ambiente nella memoria del processo di destinazione.

Modifica: la dimensione del blocco di ambiente viene anche memorizzata nella struttura _RTL_USER_PROCESS_PARAMETERS, ma la memoria viene allocata nell'heap del processo. Quindi da un processo esterno non avremmo la possibilità di ridimensionarlo e ingrandirlo. Ho provato a usare VirtualAllocEx per allocare memoria aggiuntiva nel processo di destinazione per l'ambiente storage ed è stato in grado di impostare e leggere una tabella completamente nuova. Sfortunatamente qualsiasi tentativo di modificare l'ambiente da mezzi normali si bloccherà e brucerà man mano che l'indirizzo non punta più sull'heap (si bloccherà in RtlSizeHeap).




Il modo più semplice per aggiungere una variabile al percorso senza riavviare la sessione corrente è aprire il prompt dei comandi e digitare:

PATH=(VARIABLE);%path%

e premere invio .

per verificare se la variabile è stata caricata, digitare

PATH

e premere invio . Tuttavia, la variabile sarà solo una parte del percorso fino al riavvio.




La cosa confusa potrebbe essere che ci sono alcuni punti in cui avviare il cmd. Nel mio caso ho eseguito cmd da windows explorer e le variabili di ambiente non cambiano mentre all'avvio di cmd da "run" (tasto windows + r) le variabili di ambiente sono state modificate .

Nel mio caso ho dovuto solo uccidere il processo di Windows Explorer dal task manager e poi riavviarlo di nuovo dal task manager .

Una volta fatto ciò, ho avuto accesso alla nuova variabile di ambiente da un cmd che è stato generato da Windows Explorer.




Just type "# -r" (without quotes # with -r option) in your terminal. And you're all set to default paths :)




Funziona su Windows 7: SET PATH=%PATH%;C:\CmdShortcuts

testato digitando echo% PATH% e ha funzionato, bene. imposta anche se apri un nuovo cmd, non c'è più bisogno di quei fastidiosi riavvii :)




Su Windows 7/8/10 puoi installare Chocolatey che ha uno script per questo incorporato.

Dopo aver installato Chocolatey, digita "refreshenv" senza virgolette.




basta riavviare explorer.exe >> testato su win 8 X64




no, I don't think so... you can set them manually though. So you can put them in a batch file or something.

probably could make a utility/script (if someone hasn't already) that queries the registry and sets the current enviroment to be the same




Prova ad aprire un nuovo prompt dei comandi come amministratore. Questo ha funzionato per me su Windows 10. (So che questa è una vecchia risposta, ma ho dovuto condividerla perché dover scrivere uno script VBS solo per questo è assurdo).




Uso questo script PowerShell per aggiungere alla variabile PATH . Credo che con un piccolo aggiustamento possa funzionare anche nel tuo caso.

#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%"



Chiamare questa funzione ha funzionato per me:

VOID Win32ForceSettingsChange()
{
    DWORD dwReturnValue;
    ::SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM) "Environment", SMTO_ABORTIFHUNG, 5000, &dwReturnValue);
}



Related