使用 CreateProcessAsUser 启动的 exe 在启动时随机崩溃
exe started with CreateProcessAsUser randomly crashes on start
情况如下:我有一个 C# .NET Windows 服务(运行 作为本地系统)在当前登录的用户帐户下启动卫星 C# .NET 可执行文件(无窗口 WinForms) , 使用 CreateProcessAsUser.
有时有效,有时...无效。我可以看到 exe 在任务管理器中出现了一秒钟,然后消失了。 exe的主要方法没有命中,所以我认为问题出在CreateProcessAsUser。
事件查看器显示代码为 0xc06d007e 的应用程序错误,这没有帮助。还有一个 Windows 错误报告说要查看转储文件,但给出的路径中缺少这些文件。
我不知道如何调试它。如果至少我能看到一个真正的错误。
完全随机。
编辑:
这是一些要求的代码。
public static uint? LaunchAsCurrentUser(string cmdLine)
{
IntPtr token = GetCurrentUserToken();
if (token == IntPtr.Zero)
return null;
IntPtr envBlock = GetEnvironmentBlock(token);
uint? processId = LaunchProcessAsUser(cmdLine, token, envBlock);
if (envBlock != IntPtr.Zero)
DestroyEnvironmentBlock(envBlock);
CloseHandle(token);
return processId;
}
private static uint LaunchProcessAsUser(string cmdLine, IntPtr token, IntPtr envBlock)
{
PROCESS_INFORMATION processInformation = new PROCESS_INFORMATION();
SECURITY_ATTRIBUTES saProcess = new SECURITY_ATTRIBUTES();
SECURITY_ATTRIBUTES saThread = new SECURITY_ATTRIBUTES();
saProcess.nLength = (uint)Marshal.SizeOf(saProcess);
saThread.nLength = (uint)Marshal.SizeOf(saThread);
STARTUPINFO si = new STARTUPINFO();
si.cb = (uint)Marshal.SizeOf(si);
//if this member is NULL, the new process inherits the desktop
//and window station of its parent process. If this member is
//an empty string, the process does not inherit the desktop and
//window station of its parent process; instead, the system
//determines if a new desktop and window station need to be created.
//If the impersonated user already has a desktop, the system uses the
//existing desktop.
si.lpDesktop = string.Empty; //Modify as needed
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_FORCEONFEEDBACK;
si.wShowWindow = SW_SHOW;
//Set other si properties as required.
bool result = CreateProcessAsUser(
token,
null,
cmdLine,
ref saProcess,
ref saThread,
false,
CREATE_UNICODE_ENVIRONMENT,
envBlock,
null,
ref si,
out processInformation);
if (!result)
{
int error = Marshal.GetLastWin32Error();
string message = String.Format("CreateProcessAsUser Error: {0}", error);
throw new ApplicationException(message);
}
return processInformation.dwProcessId;
}
private static IntPtr GetEnvironmentBlock(IntPtr token)
{
IntPtr envBlock = IntPtr.Zero;
bool retVal = CreateEnvironmentBlock(ref envBlock, token, false);
if (retVal == false)
{
// Environment Block, things like common paths to My Documents etc.
// Will not be created if "false"
// It should not adversley affect CreateProcessAsUser.
string message = String.Format("CreateEnvironmentBlock Error: {0}", Marshal.GetLastWin32Error());
throw new ApplicationException(message);
}
return envBlock;
}
private static IntPtr GetCurrentUserToken()
{
IntPtr currentToken = IntPtr.Zero;
IntPtr primaryToken = IntPtr.Zero;
IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;
int dwSessionId = 0;
IntPtr hUserToken = IntPtr.Zero;
IntPtr hTokenDup = IntPtr.Zero;
IntPtr pSessionInfo = IntPtr.Zero;
int dwCount = 0;
WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, ref pSessionInfo, ref dwCount);
Int32 dataSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));
Int64 current = (long)pSessionInfo;
for (int i = 0; i < dwCount; i++)
{
WTS_SESSION_INFO si =
(WTS_SESSION_INFO)Marshal.PtrToStructure((IntPtr)current, typeof(WTS_SESSION_INFO));
if (WTS_CONNECTSTATE_CLASS.WTSActive == si.State)
{
dwSessionId = si.SessionID;
break;
}
current += dataSize;
}
WTSFreeMemory(pSessionInfo);
bool bRet = WTSQueryUserToken(dwSessionId, out currentToken);
if (bRet == false)
return IntPtr.Zero;
bRet = DuplicateTokenEx(currentToken, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS, IntPtr.Zero, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenPrimary, out primaryToken);
if (bRet == false)
return IntPtr.Zero;
return primaryToken;
}
只需要改变这个:
si.lpDesktop = string.Empty;
为此:
si.lpDesktop = null;
...所以新进程继承了它的父进程桌面。现在它完美运行了。
情况如下:我有一个 C# .NET Windows 服务(运行 作为本地系统)在当前登录的用户帐户下启动卫星 C# .NET 可执行文件(无窗口 WinForms) , 使用 CreateProcessAsUser.
有时有效,有时...无效。我可以看到 exe 在任务管理器中出现了一秒钟,然后消失了。 exe的主要方法没有命中,所以我认为问题出在CreateProcessAsUser。
事件查看器显示代码为 0xc06d007e 的应用程序错误,这没有帮助。还有一个 Windows 错误报告说要查看转储文件,但给出的路径中缺少这些文件。
我不知道如何调试它。如果至少我能看到一个真正的错误。
完全随机。
编辑:
这是一些要求的代码。
public static uint? LaunchAsCurrentUser(string cmdLine)
{
IntPtr token = GetCurrentUserToken();
if (token == IntPtr.Zero)
return null;
IntPtr envBlock = GetEnvironmentBlock(token);
uint? processId = LaunchProcessAsUser(cmdLine, token, envBlock);
if (envBlock != IntPtr.Zero)
DestroyEnvironmentBlock(envBlock);
CloseHandle(token);
return processId;
}
private static uint LaunchProcessAsUser(string cmdLine, IntPtr token, IntPtr envBlock)
{
PROCESS_INFORMATION processInformation = new PROCESS_INFORMATION();
SECURITY_ATTRIBUTES saProcess = new SECURITY_ATTRIBUTES();
SECURITY_ATTRIBUTES saThread = new SECURITY_ATTRIBUTES();
saProcess.nLength = (uint)Marshal.SizeOf(saProcess);
saThread.nLength = (uint)Marshal.SizeOf(saThread);
STARTUPINFO si = new STARTUPINFO();
si.cb = (uint)Marshal.SizeOf(si);
//if this member is NULL, the new process inherits the desktop
//and window station of its parent process. If this member is
//an empty string, the process does not inherit the desktop and
//window station of its parent process; instead, the system
//determines if a new desktop and window station need to be created.
//If the impersonated user already has a desktop, the system uses the
//existing desktop.
si.lpDesktop = string.Empty; //Modify as needed
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_FORCEONFEEDBACK;
si.wShowWindow = SW_SHOW;
//Set other si properties as required.
bool result = CreateProcessAsUser(
token,
null,
cmdLine,
ref saProcess,
ref saThread,
false,
CREATE_UNICODE_ENVIRONMENT,
envBlock,
null,
ref si,
out processInformation);
if (!result)
{
int error = Marshal.GetLastWin32Error();
string message = String.Format("CreateProcessAsUser Error: {0}", error);
throw new ApplicationException(message);
}
return processInformation.dwProcessId;
}
private static IntPtr GetEnvironmentBlock(IntPtr token)
{
IntPtr envBlock = IntPtr.Zero;
bool retVal = CreateEnvironmentBlock(ref envBlock, token, false);
if (retVal == false)
{
// Environment Block, things like common paths to My Documents etc.
// Will not be created if "false"
// It should not adversley affect CreateProcessAsUser.
string message = String.Format("CreateEnvironmentBlock Error: {0}", Marshal.GetLastWin32Error());
throw new ApplicationException(message);
}
return envBlock;
}
private static IntPtr GetCurrentUserToken()
{
IntPtr currentToken = IntPtr.Zero;
IntPtr primaryToken = IntPtr.Zero;
IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;
int dwSessionId = 0;
IntPtr hUserToken = IntPtr.Zero;
IntPtr hTokenDup = IntPtr.Zero;
IntPtr pSessionInfo = IntPtr.Zero;
int dwCount = 0;
WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, ref pSessionInfo, ref dwCount);
Int32 dataSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));
Int64 current = (long)pSessionInfo;
for (int i = 0; i < dwCount; i++)
{
WTS_SESSION_INFO si =
(WTS_SESSION_INFO)Marshal.PtrToStructure((IntPtr)current, typeof(WTS_SESSION_INFO));
if (WTS_CONNECTSTATE_CLASS.WTSActive == si.State)
{
dwSessionId = si.SessionID;
break;
}
current += dataSize;
}
WTSFreeMemory(pSessionInfo);
bool bRet = WTSQueryUserToken(dwSessionId, out currentToken);
if (bRet == false)
return IntPtr.Zero;
bRet = DuplicateTokenEx(currentToken, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS, IntPtr.Zero, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenPrimary, out primaryToken);
if (bRet == false)
return IntPtr.Zero;
return primaryToken;
}
只需要改变这个:
si.lpDesktop = string.Empty;
为此:
si.lpDesktop = null;
...所以新进程继承了它的父进程桌面。现在它完美运行了。