CreateProcessAsUser 进程以 -1073741502 退出
Posted
技术标签:
【中文标题】CreateProcessAsUser 进程以 -1073741502 退出【英文标题】:CreateProcessAsUser process exits with -1073741502 【发布时间】:2018-06-01 13:08:40 【问题描述】:一旦用户登录和控制台会话连接,我有一个服务负责启动/监控用户会话中的交互式进程。 服务设置为自动启动,以便在用户登录之前启动并运行。 首次登录时一切正常,我可以正确启动/重新启动用户进程。
如果用户退出并重新登录服务将不再能够正确启动用户进程。 CreateProcessAsUser 不返回错误,但一旦用户进程启动,它就会以 -1073741502 (0xC0000142) 退出代码退出。
如果我重新启动服务,那么它可以再次启动用户进程而不会出现任何错误。
如果需要,我可以发布服务如何创建用户进程的完整源代码。
编辑
try
//WE ALREADY HAVE A CLIENT ATTACHED , DONT START A NEW ONE
if (ClientProcessId != null)
return;
var ACTIVE_CONSOLE_SESSION = ListSessions()
.Where(SESSION => SESSION.State == WTS_CONNECTSTATE_CLASS.WTSActive)
.FirstOrDefault();
if (ACTIVE_CONSOLE_SESSION == null)
return;
CONSOLE_SESSION_ID = (uint)ACTIVE_CONSOLE_SESSION.Id;
IntPtr USER_TOKEN = IntPtr.Zero;
IntPtr ENVIRONMENT = IntPtr.Zero;
IntPtr LINKED_TOKEN = IntPtr.Zero;
try
try
if (!Wtsapi32.WTSQueryUserToken(CONSOLE_SESSION_ID.Value, out USER_TOKEN))
throw new Win32Exception();
catch (Win32Exception wex)
EntryPoint.TryWriteToCacheLog($"nameof(Wtsapi32.WTSQueryUserToken) : console session id CONSOLE_SESSION_ID error wex.ErrorCode , native error wex.NativeErrorCode");
throw;
try
if (!Userenv.CreateEnvironmentBlock(out ENVIRONMENT, USER_TOKEN, true))
throw new Win32Exception();
catch (Win32Exception wex)
EntryPoint.TryWriteToCacheLog($"nameof(Userenv.CreateEnvironmentBlock) : error wex.ErrorCode , native error wex.NativeErrorCode");
throw;
try
LINKED_TOKEN = CoreProcess.GetLinkedTokeIfRequiered(USER_TOKEN);
catch (Win32Exception wex)
EntryPoint.TryWriteToCacheLog($"nameof(CoreProcess.GetLinkedTokeIfRequiered) : error wex.ErrorCode , native error wex.NativeErrorCode");
throw;
//GET PROCESS PATHS
string FILE_NAME = EntryPoint.PROCESS_FULL_FILE_NAME;
string WORKING_DIRECTORY = EntryPoint.PROCESS_DIRECTORY;
//GET CURRENT COMMAND LINE ARGUMENTS
var CMD_ARGS = Environment.GetCommandLineArgs();
//FIRST ARGUMENT WILL ALWAYS HAVE FULL PROCESS PATH,OTHER ARGUMENTS ARE OPTIONAL
var ARGUMENTS_STRING = CMD_ARGS
.Skip(1)
.DefaultIfEmpty()
.Aggregate((first, next) => ' ' + first + ' ' + next);
var ARGUMENTS = new StringBuilder(ARGUMENTS_STRING);
var START_INFO = new STARTUPINFO();
START_INFO.cb = Marshal.SizeOf(START_INFO);
START_INFO.lpDesktop = @"winsta0\default";
var PROCESS_INFO = new PROCESS_INFORMATION();
uint dwCreationFlags = NORMAL_PRIORITY_CLASS | (int)(PROCESS_CREATE_FLAG.CREATE_NEW_CONSOLE | PROCESS_CREATE_FLAG.CREATE_UNICODE_ENVIRONMENT);
try
if (!AdvApi32.CreateProcessAsUser(LINKED_TOKEN,
FILE_NAME,
ARGUMENTS,
IntPtr.Zero,
IntPtr.Zero,
true,
dwCreationFlags,
ENVIRONMENT,
WORKING_DIRECTORY,
ref START_INFO,
out PROCESS_INFO))
throw new Win32Exception();
if (PROCESS_INFO.hThread != IntPtr.Zero)
ClientProcessId = PROCESS_INFO.dwProcessId;
ClientSessionId = CONSOLE_SESSION_ID;
EntryPoint.TryWriteToCacheLog($"nameof(AdvApi32.CreateProcessAsUser) : Created porocess ClientProcessId in session CONSOLE_SESSION_ID.");
catch (Win32Exception wex)
EntryPoint.TryWriteToCacheLog($"nameof(AdvApi32.CreateProcessAsUser) : error wex.ErrorCode , native error wex.NativeErrorCode");
throw;
catch (Win32Exception wex)
switch (wex.NativeErrorCode)
case 5:
case 1008:
tryCount++;
if (tryCount >= START_RETRIES)
throw;
Thread.Sleep(RETRY_WAIT_SPAN);
if (DisableCallBacks)
return;
CreateProcess(tryCount);
break;
default:
throw;
catch
throw;
finally
Userenv.DestroyEnvironmentBlock(ENVIRONMENT);
Kernel32.CloseHandle(USER_TOKEN);
if (USER_TOKEN != LINKED_TOKEN)
Kernel32.CloseHandle(LINKED_TOKEN);
catch (Exception ex)
EntryPoint.TryWriteToCacheLog($"nameof(CreateProcess) failed after tryCount retries, console seesion id (CONSOLE_SESSION_ID != null ? CONSOLE_SESSION_ID.ToString() : "Unobtained").", ex.ToString());
finally
Monitor.Exit(CREATE_LOCK);
public static TOKEN_ELEVATION_TYPE GetTokenElevationLevel(IntPtr hToken)
int TOKEN_INFO_LENGTH = Marshal.SizeOf(typeof(int));
IntPtr TOKEN_INFO_POINTER = Marshal.AllocHGlobal(TOKEN_INFO_LENGTH);
try
if (!AdvApi32.GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenElevationType, TOKEN_INFO_POINTER, TOKEN_INFO_LENGTH, out TOKEN_INFO_LENGTH))
throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error());
return (TOKEN_ELEVATION_TYPE)Marshal.ReadInt32(TOKEN_INFO_POINTER);
catch
throw;
finally
if (TOKEN_INFO_POINTER != IntPtr.Zero)
Marshal.FreeHGlobal(TOKEN_INFO_POINTER);
public static IntPtr GetLinkedTokeIfRequiered(IntPtr hToken)
var TOKEN_ELEVATION = GetTokenElevationLevel(hToken);
if (TOKEN_ELEVATION != TOKEN_ELEVATION_TYPE.TokenElevationTypeLimited)
return hToken;
int TOKEN_INFO_LENGHT = Marshal.SizeOf(typeof(IntPtr));
IntPtr LINKED_TOKEN_INFO = Marshal.AllocHGlobal(TOKEN_INFO_LENGHT);
try
if (!AdvApi32.GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenLinkedToken, LINKED_TOKEN_INFO, TOKEN_INFO_LENGHT, out TOKEN_INFO_LENGHT))
throw new Win32Exception();
return Marshal.ReadIntPtr(LINKED_TOKEN_INFO);
finally
if (LINKED_TOKEN_INFO != IntPtr.Zero)
Marshal.Release(LINKED_TOKEN_INFO);
【问题讨论】:
这意味着您的服务缓存了用户会话中的一些数据。会话终止后 - 此数据不正确。当您重新启动服务时 - 它使用新数据而不是旧缓存 嗯,这是我的想法,但我确实释放了我获得的任何句柄:(我会重新检查! 没有足够的桌面堆来加载 user32.dll。适合“你不应该在服务中做的事情”类别。 This blog post 告诉您出了什么问题以及如何处理。 它不太可能与桌面堆相关联,因为 OP 声称要在用户会话中启动交互式进程。这应该是直截了当的。获取用户的会话号,调用WTSQueryUserToken
,然后调用CreateProcessAsUser
。可能 user32.dll 初始化失败,考虑到它不是桌面堆问题,这意味着令牌没有正确的登录会话 SID,但我不确定 OP 如何以这种状态结束。
@RbMm 似乎我没有在会话之间泄漏任何资源,所有 hadles 都正确释放:( eryksun 确实这正是我正在做的,正如我所说的服务能够启动重新启动用户只要活动的控制台会话没有改变,就可以多次处理。我在这里处于死胡同:(
【参考方案1】:
感谢您发布其余代码。我看到您正在启动提升的进程。我将此添加到我的测试服务中,它仍然可以正常工作。
但我认为问题可能出在GetLinkedTokeIfRequiered()
中的这一行:
Marshal.Release(LINKED_TOKEN_INFO);
显然应该是:
Marshal.FreeHGlobal(LINKED_TOKEN_INFO);
修复它,它可能会起作用。事实上,我很惊讶它没有崩溃。
对我来说不容易,挖掘这个。 C# 互操作不是我的强项。
为了 OP 的利益,我的测试服务的完整源代码,用 C++ 编写,可以工作:
#include <windows.h>
#include <wtsapi32.h>
#include <userenv.h>
#include <tchar.h>
#include <stdio.h>
#pragma comment (lib, "user32.lib")
#pragma comment (lib, "wtsapi32.lib")
#pragma comment (lib, "userenv.lib")
#pragma comment (lib, "advapi32.lib")
DWORD report_error (const char *operation)
DWORD err = GetLastError ();
return err;
// Launch notepad as currently logged-on user
DWORD LaunchProcess (DWORD SessionId, const char **failed_operation)
HANDLE hToken;
BOOL ok = WTSQueryUserToken (SessionId, &hToken);
if (!ok)
return report_error (*failed_operation = "WTSQueryUserToken");
void *environment = NULL;
ok = CreateEnvironmentBlock (&environment, hToken, TRUE);
if (!ok)
CloseHandle (hToken);
return report_error (*failed_operation = "CreateEnvironmentBlock");
TOKEN_LINKED_TOKEN lto;
DWORD nbytes;
ok = GetTokenInformation (hToken, TokenLinkedToken, <o, sizeof (lto), &nbytes);
if (ok)
CloseHandle (hToken);
hToken = lto.LinkedToken;
STARTUPINFO si = sizeof (si) ;
PROCESS_INFORMATION pi = ;
si.lpDesktop = "winsta0\\default";
// Do NOT want to inherit handles here, surely
DWORD dwCreationFlags = NORMAL_PRIORITY_CLASS | /* CREATE_NEW_CONSOLE | */ CREATE_UNICODE_ENVIRONMENT;
ok = CreateProcessAsUser (hToken, "c:\\windows\\system32\\notepad.exe", NULL, NULL, NULL, FALSE,
dwCreationFlags, environment, NULL, &si, &pi);
DestroyEnvironmentBlock (environment);
CloseHandle (hToken);
if (!ok)
return report_error (*failed_operation = "CreateProcessAsUser");
CloseHandle (pi.hThread);
CloseHandle (pi.hProcess);
return 0;
// Determine the session ID of the currently logged-on user
DWORD GetCurrentSessionId ()
WTS_SESSION_INFO *pSessionInfo;
DWORD n_sessions = 0;
BOOL ok = WTSEnumerateSessions (WTS_CURRENT_SERVER, 0, 1, &pSessionInfo, &n_sessions);
if (!ok)
return 0;
DWORD SessionId = 0;
for (DWORD i = 0; i < n_sessions; ++i)
if (pSessionInfo [i].State == WTSActive)
SessionId = pSessionInfo [i].SessionId;
break;
WTSFreeMemory (pSessionInfo);
return SessionId;
#define SERVICE_NAME __T ("demo_service")
bool quit;
// CtrlHandler callback
DWORD WINAPI CtrlHandler (DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext)
if (dwControl == SERVICE_CONTROL_STOP)
quit = true;
return NO_ERROR;
// SvcMain callback
VOID WINAPI SvcMain (DWORD dwArgc, LPTSTR *lpszArgv)
// Register for callbacks
SERVICE_STATUS_HANDLE sh = RegisterServiceCtrlHandlerEx (SERVICE_NAME, CtrlHandler, NULL);
// Tell the SCM that we are up and running
SERVICE_STATUS ss = ;
ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
ss.dwCurrentState = SERVICE_RUNNING;
ss.dwControlsAccepted = SERVICE_ACCEPT_STOP;
SetServiceStatus (sh, &ss);
TCHAR buf [256];
const TCHAR *title = __T ("(c) 2018 Contoso Corporation");
while (!quit)
DWORD response = IDOK;
DWORD SessionId = GetCurrentSessionId ();
if (SessionId == 0)
Sleep (2000);
continue;
// Pop-up a message on the screen of the currently logged-on user (session 1)
_stprintf (buf, __T ("Ready to launch..., SessionId = %d"), SessionId);
WTSSendMessage (WTS_CURRENT_SERVER_HANDLE, SessionId, (TCHAR *) title, _tcslen (title),
buf, _tcslen (buf), MB_OKCANCEL, 0, &response, TRUE);
if (response == IDCANCEL)
break;
const char *failed_operation = "";
DWORD dwResult = LaunchProcess (SessionId, &failed_operation);
// Report results
_stprintf (buf, __T ("LaunchProcess returned %lx from %s"), dwResult, failed_operation);
WTSSendMessage (WTS_CURRENT_SERVER_HANDLE, SessionId, (char *) title, _tcslen (title),
buf, _tcslen (buf), MB_OK, 0, &response, TRUE);
FILE *logfile = fopen ("g:\\temp\\service.log", "at");
if (logfile)
fprintf (logfile, "%s\n", buf);
fclose (logfile);
// Tell the SCM we are going away and exit
ss.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus (sh, &ss);
// main
int main (void)
SERVICE_TABLE_ENTRY DispatchTable [] =
SERVICE_NAME, SvcMain ,
NULL, NULL
;
// This call returns when the service has stopped.
// The process should simply terminate when the call returns.
StartServiceCtrlDispatcher (DispatchTable);
return 0;
【讨论】:
您对 FreeHGlobal 的看法是正确的,但即便如此,问题仍然存在。正如我所说,如果用户注销并重新登录并且仅在第一次启动时出现几次,问题就会出现 100%。 好的,那我没办法了,抱歉。我发布了我的代码,也许它会有所帮助。【参考方案2】:错误是STATUS_DLL_INIT_FAILED
,这意味着缺少动态加载的DLL
。也许您指定了错误的工作目录并且对LoadLibrary("lib_with_no_path.dll")
的某些调用失败了?
如果您查看Event Viewer
,您应该能够看到缺少哪个 DLL。
【讨论】:
以上是关于CreateProcessAsUser 进程以 -1073741502 退出的主要内容,如果未能解决你的问题,请参考以下文章
创建新进程,就三个函数CreateProcessAsUser CreateProcessWithLogonW CreateProcessWithTokenW(附网友的流程)
使用 CREATE_SUSPENDED 标志和 JobObject 从 CreateProcessAsUser 为 C# 进程设置 ProcessStartInfo
从 Vista 上的服务中使用 CreateProcessAsUser 的桌面问题
CreateProcessAsUser,C#写的windows服务弹框提示消息或者启动复杂的UI界面的子进程