如何通过“开始”菜单获取传递给新进程的完整环境变量集,而不是传递给我的进程的环境变量集?或者如何使用这组环境变量启动进程?(从技术上讲,我不需要在初始过程中访问它们)
例如,如果启动了我的流程,然后用户使用环境变量gui对其进行修改,那么它们可能会出现差异。
用例:我正在编写一个启动实用程序,它的行为类似于开始菜单,用于启动应用程序。
正确的解决方案需要执行以下操作:
获取计算机和用户环境变量
合并machine和user中的路径变量(是否合并了其他变量?)
获取homedrive、homepath、username、userprofile和System.Environment.GetEnvironmentVariables(System.EnvironmentVariableTarget.User)未提供的任何其他变量
不从当前进程继承环境
是否有windows api调用来获取windows“开始”菜单传递给已启动进程的全套环境变量?或者,有没有一种方法可以启动一个进程,使windows自动设置其环境变量,而无需我的干预,也无需继承当前进程?

最佳答案

下面是将全套环境变量解析为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;
    }
}

关于c# - 获取传递给新进程(不是当前进程)的Windows环境变量,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/56118028/

10-11 22:42
查看更多