获取传递给新进程(不是当前进程)的Windows环境变量

时间:2019-05-13 18:33:14

标签: c# windows

如何获取通过开始菜单传递给新过程的完整环境变量集,而不是不是传递给我的过程的环境变量集?或如何使用这组环境变量启动流程? (从技术上讲,我不需要在初始过程中访问它们)

例如,如果启动了我的进程,然后用户使用环境变量GUI对其进行了修改,则它们可能会有所不同。

用例:我正在编写一个启动器实用程序,其行为类似于开始菜单,用于启动应用程序。

适当的解决方案需要执行以下操作:

  • 同时获取机器和用户环境变量
  • 合并计算机和用户的PATH变量(是否还有其他变量合并?)
  • 获取HOMEDRIVE,HOMEPATH,USERNAME,USERPROFILE和System.Environment.GetEnvironmentVariables(System.EnvironmentVariableTarget.User)未提供的任何其他变量
  • 不能从当前进程继承环境

是否有Windows API调用来获取Windows开始菜单传递给启动的进程的全套环境变量?还是有一种方法可以启动进程,以便Windows在没有我干预的情况下自动设置其环境变量,并且无需从当前进程继承?

1 个答案:

答案 0 :(得分:2)

这里是将完整的环境变量集解析为C#词典的代码。注释中建议使用CreateEnvironmentBlockCreateEnvironmentBlock需要为要检索其变量的用户提供用户令牌。我们可以通过OpenProcessToken(GetCurrentProcess(), TOKEN_READ, out primaryToken);

为用户获取进程正在运行的令牌

获取环境块确实很容易。将环境块解析为C#词典需要花费更多的代码,而且我敢肯定它会更短。

没有错误检查的重要位:

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);

完整示例,可以编译:

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;
    }
}
相关问题