如何获取通过开始菜单传递给新过程的完整环境变量集,而不是不是传递给我的过程的环境变量集?或如何使用这组环境变量启动流程? (从技术上讲,我不需要在初始过程中访问它们)
例如,如果启动了我的进程,然后用户使用环境变量GUI对其进行了修改,则它们可能会有所不同。
用例:我正在编写一个启动器实用程序,其行为类似于开始菜单,用于启动应用程序。
适当的解决方案需要执行以下操作:
System.Environment.GetEnvironmentVariables(System.EnvironmentVariableTarget.User)
未提供的任何其他变量是否有Windows API调用来获取Windows开始菜单传递给启动的进程的全套环境变量?还是有一种方法可以启动进程,以便Windows在没有我干预的情况下自动设置其环境变量,并且无需从当前进程继承?
答案 0 :(得分:2)
这里是将完整的环境变量集解析为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;
}
}