Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get Windows environment variables passed to new process (not of current process)

Tags:

c#

windows

How do I obtain the complete set of environment variables passed to a new process by the start menu, not the set of environment variables passed to my process? Or how do I launch a process with this set of environment variables? (I technically do not need to access them in my initial process)

These can diverge, for example, if my process is launched and then the user uses the environment variable GUI to modify them.

Use-case: I'm writing a launcher utility that behaves similarly to the start menu, for starting applications.

A proper solution needs to do the following:

  • Get both machine and user environment variables
  • Merge the PATH variables from machine and user (Do any other variables merge?)
  • Get HOMEDRIVE, HOMEPATH, USERNAME, USERPROFILE, and any other variables not provided by System.Environment.GetEnvironmentVariables(System.EnvironmentVariableTarget.User)
  • NOT inherit environment from the current process

Is there a Windows API call to get this full set of environment variables that the Windows start menu passes to launched processes? Or is there a way to launch a process such that Windows automatically sets its environment variables without my intervention, and without inheriting from the current process?

like image 741
cspotcode Avatar asked Dec 17 '25 20:12

cspotcode


1 Answers

Here is code to parse the full set of environment variables into a C# Dictionary. It uses CreateEnvironmentBlock as suggested in the comments. CreateEnvironmentBlock needs a user token for the user whose variables you want to retrieve. We can get a token for the user that our process is running as via OpenProcessToken(GetCurrentProcess(), TOKEN_READ, out primaryToken);

Getting the environment block is really easy. More code is spent parsing the environment block into a C# dictionary, and I'm sure it could be much shorter.

Important bits without error checking:

IntPtr primaryToken = IntPtr.Zero;
OpenProcessToken(GetCurrentProcess(), TOKEN_READ, out primaryToken);
IntPtr lpEnvironment = IntPtr.Zero;
bool resultEnv = CreateEnvironmentBlock(out lpEnvironment, primaryToken, false);
// Do stuff with lpEnvironment here
DestroyEnvironmentBlock(lpEnvironment);
CloseHandle(primaryToken);

Full example, ready to compile:

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

public class EnvVarGetter
{
    public static Dictionary<String, String> GetEnvironmentVariables()
    {
        IntPtr primaryToken = IntPtr.Zero;
        OpenProcessToken(GetCurrentProcess(), TOKEN_READ, out primaryToken);
        if (primaryToken == IntPtr.Zero)
        {
            return null;
        }

        IntPtr lpEnvironment = IntPtr.Zero;
        bool resultEnv = CreateEnvironmentBlock(out lpEnvironment, primaryToken, false);
        if (resultEnv != true)
        {
            int nError = GetLastError();
        }

        var envVars = new Dictionary<string, string> { };

        IntPtr next = lpEnvironment;
        while (Marshal.ReadByte(next) != 0)
        {
            var str = Marshal.PtrToStringUni(next);
           // skip first character because windows allows env vars to begin with equal sign
            var splitPoint = str.IndexOf('=', 1);
            var envVarName = str.Substring(0, splitPoint);
            var envVarVal = str.Substring(splitPoint + 1);
            envVars.Add(envVarName, envVarVal);
            next = (IntPtr)((Int64)next + (str.Length * 2) + 2);
        }

        DestroyEnvironmentBlock(lpEnvironment);
        CloseHandle(primaryToken);
        return envVars;
    }

    private static uint STANDARD_RIGHTS_REQUIRED = 0x000F0000;
    private static uint STANDARD_RIGHTS_READ = 0x00020000;
    private static uint TOKEN_ASSIGN_PRIMARY = 0x0001;
    private static uint TOKEN_DUPLICATE = 0x0002;
    private static uint TOKEN_IMPERSONATE = 0x0004;
    private static uint TOKEN_QUERY = 0x0008;
    private static uint TOKEN_QUERY_SOURCE = 0x0010;
    private static uint TOKEN_ADJUST_PRIVILEGES = 0x0020;
    private static uint TOKEN_ADJUST_GROUPS = 0x0040;
    private static uint TOKEN_ADJUST_DEFAULT = 0x0080;
    private static uint TOKEN_ADJUST_SESSIONID = 0x0100;
    private static uint TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY);
    private static uint TOKEN_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_QUERY_SOURCE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID);


    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    static extern bool CloseHandle(IntPtr handle);

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    static extern int GetLastError();

    [DllImport("userenv.dll", CharSet = CharSet.Auto, SetLastError = true)]
    static extern bool CreateEnvironmentBlock(out IntPtr lpEnvironment, IntPtr hToken, bool bInherit);

    [DllImport("userenv.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool DestroyEnvironmentBlock(IntPtr lpEnvironment);

    [DllImport("advapi32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool OpenProcessToken(IntPtr ProcessHandle,
    UInt32 DesiredAccess, out IntPtr TokenHandle);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern IntPtr GetCurrentProcess();

    public static int Main()
    {
        // Loop indefinitely till user hits escape
        do
        {
            // Get environment variables
            var vars = EnvVarGetter.GetEnvironmentVariables();
            // Log them
            foreach(var pair in vars)
            {
                Console.WriteLine(pair);
            }
            while (!Console.KeyAvailable)
            {
            }
        } while (Console.ReadKey(true).Key != ConsoleKey.Escape);

        return 0;
    }
}
like image 149
cspotcode Avatar answered Dec 19 '25 11:12

cspotcode



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!