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