我已经搜索了很长时间,但还找不到有效的解决方案:-(
我创建了一个窗口服务,它在每个使用CreateProcessAsUser登录到计算机的用户上启动客户端(http://www.pinvoke.net/default.aspx/advapi32/createprocessasuser.html)、WTSE分子会话等…
这已经很好用了。客户端在用户会话中启动,显示任务栏图标,与服务的通信正常。
我遇到的问题是,我需要让该客户端将临时文件存储在用户的配置文件中。我试着从一个小日志文件开始,这样我就可以跟踪用户最终可能遇到的任何错误。不幸的是,我无法保存到用户的临时文件夹,因为尽管WindowsIdentity显示了正确的用户:System,但客户端似乎在LocalSystem的上下文中运行。IO.Path.GetTempPath()总是返回"C:\Windows\Temp",但我的用户没有管理权限,因此无法在那里写入。。。此外,我计划将设置存储在当前用户的注册表中,该注册表也不起作用。我认为这在某种程度上与错误的临时路径有关。
我还尝试了CreateEnvironmentBlock(http://www.pinvoke.net/default.aspx/userenv/CreateEnvironmentBlock.html)但我无法让它工作,在某个地方我发现了一篇文章,说这在Vista或更高版本上不再工作,所以我停止了对这篇文章的研究。
为了进行测试,我创建了一个小的测试表单:
MessageBox.Show("Temp: " + System.IO.Path.GetTempPath() + Environment.NewLine + "User: " + WindowsIdentity.GetCurrent().Name, "Before impersonation");
WindowsIdentity currentUserId = WindowsIdentity.GetCurrent();
WindowsImpersonationContext impersonatedUser = currentUserId.Impersonate();
MessageBox.Show("Temp: " + System.IO.Path.GetTempPath() + Environment.NewLine + "User: " + WindowsIdentity.GetCurrent().Name, "After impersonation");
这个在模拟前后总是显示相同的结果:Temp: C:WindowsTemp User:testdomaintestuser
:-(
如果这有助于启动进程(用户令牌由WTSEnumeratureSessions提供),那么这当然只在LocalSystem的上下文下有效:
public static Process StartProcessAsUser(IntPtr UserToken, string App, string AppPath, string AppParameters)
{
Process ResultProcess = null;
IntPtr hDupedToken = IntPtr.Zero;
NativeProcessAPI.PROCESS_INFORMATION oProcessInformation = new NativeProcessAPI.PROCESS_INFORMATION();
try
{
NativeProcessAPI.SECURITY_ATTRIBUTES oSecurityAttributes = new NativeProcessAPI.SECURITY_ATTRIBUTES();
oSecurityAttributes.Length = Marshal.SizeOf(oSecurityAttributes);
bool result = NativeProcessAPI.DuplicateTokenEx(
UserToken,
NativeProcessAPI.GENERIC_ALL_ACCESS,
ref oSecurityAttributes,
(int)NativeProcessAPI.SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
(int)NativeProcessAPI.TOKEN_TYPE.TokenPrimary,
ref hDupedToken
);
if (!result)
{
return null;
}
NativeProcessAPI.STARTUPINFO oStartupInfo = new NativeProcessAPI.STARTUPINFO();
oStartupInfo.cb = Marshal.SizeOf(oStartupInfo);
oStartupInfo.lpDesktop = String.Empty;
result = NativeProcessAPI.CreateProcessAsUser(
hDupedToken,
null,
App + " " + AppParameters,
ref oSecurityAttributes, ref oSecurityAttributes,
false, 0, IntPtr.Zero,
AppPath, ref oStartupInfo, ref oProcessInformation
);
if (result)
{
try
{
int ProcessID = oProcessInformation.dwProcessID;
try
{
ResultProcess = System.Diagnostics.Process.GetProcessById(ProcessID);
}
catch
{
ResultProcess = null;
}
}
catch (Exception ex)
{
ResultProcess = null;
}
}
}
catch
{
ResultProcess = null;
}
finally
{
if (oProcessInformation.hProcess != IntPtr.Zero)
NativeProcessAPI.CloseHandle(oProcessInformation.hProcess);
if (oProcessInformation.hThread != IntPtr.Zero)
NativeProcessAPI.CloseHandle(oProcessInformation.hThread);
if (hDupedToken != IntPtr.Zero)
NativeProcessAPI.CloseHandle(hDupedToken);
}
return ResultProcess;
}
关于如何在用户的上下文中而不是在LocalSystem的上下文中启动流程,有什么想法吗?
非常感谢!
这里留给其他想知道如何做到这一点的人:CreateEnvironmentBlock是您需要使用的。
DuplicateTokenEx(userToken, MAXIMUM_ALLOWED | TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE, IntPtr.Zero, SecurityIdentification, TokenPrimary, out dupUserToken);
CreateEnvironmentBlock(out envBlock, dupUserToken, false);
CreateProcessAsUserW(dupUserToken, null, cmdLine, IntPtr.Zero, IntPtr.Zero, false,
(uint)(CreateProcessFlags.CREATE_NEW_CONSOLE | CreateProcessFlags.CREATE_UNICODE_ENVIRONMENT),
envBlock, processDir, ref startupInfo, out procInfo);
好的,我找到了一个解决方法:通过使用WindowsIdentity提供的SID,我切换到使用USERS配置单元,而不是CURRENT_USER配置单元:
Microsoft.Win32.Registry.Users.OpenSubKey(System.Security.Principal.WindowsIdentity.GetCurrent().User.ToString() + ..., true)
虽然从用户的"环境"one_answers"易失性环境"注册表路径中获取环境变量而不是仅使用会让人感到有点不舒服,但这非常有效。Net的内置函数。。。
但是非常感谢你的帮助;-)
编辑:我不会将其标记为答案,因为这是a)我自己的解决方案,b)只是一个变通方法