【发布时间】: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 确实这正是我正在做的,正如我所说的服务能够启动重新启动用户只要活动的控制台会话没有改变,就可以多次处理。我在这里处于死胡同:(