2016-01-29 6 views
0

Вот такая ситуация: у меня есть служба Windows C# .NET (запускается как локальная система), которая запускает исполняемый файл C# .NET (без окон WinForms) под текущей учетной записью пользователя, используя CreateProcessAsUser.exe ​​запущен с CreateProcessAsUser случайным образом сбой при запуске

Иногда это работает, иногда ... это не так. Я вижу, что exe появляется в диспетчере задач всего на секунду и исчезает. Основной метод exe не попадает, поэтому я думаю, что проблема исходит от CreateProcessAsUser.

Средство просмотра событий показывает ошибку приложения с кодом 0xc06d007e, который не помогает. Существует также отчет об ошибках Windows, в котором говорится, что нужно посмотреть файлы дампа, но они отсутствуют в указанном пути.

Я понятия не имею, как отладить это. Если бы я мог увидеть реальную ошибку.

Это абсолютно случайный.

EDIT:

Вот код, как просили.

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; 
} 
+0

.NET-программисты рано или поздно всегда обнаруживают, что запись обработчика событий для события AppDomain.CurrentDomain.UnhandledException никогда не является обязательным. Без отладчика и Trace Stack, вы слепы, как летучая мышь, чтобы обнаружить основную проблему. И никогда не просите помощи на сайте, например, SO. –

+0

Без улучшения. Я пытался прослушивать события UnhandledExceptions как для службы Windows, так и для exe, но безрезультатно. Думаю, мне нужно разместить его в главном экзе (я сделал), но, как я уже сказал, он никогда не срабатывает. Кажется, что exe будет убит ОС сразу после его запуска. – user1174017

+0

Не могли бы вы показать свой код? Сделали ли вы все «волшебство», которое описано здесь [http://blogs.msdn.com/b/winsdk/archive/2009/07/14/launching-an-interactive-process-from-windows-service-in -windows-перспектива-и-later.aspx)? Может быть, это может быть полезно. [Вот мой код] (http://pastebin.com/xhajdxrk), как я запускаю некоторый EXE под SessionId для некоторого пользователя. Он написан на C++, но его можно вызвать из C# через PInvoke. Вы можете перечислить весь сеанс [WTSEnumerateSessions] (https://msdn.microsoft.com/ru-ru/library/windows/desktop/aa383833%28v=vs.85%29.aspx). –

ответ

0

Просто нужно изменить:

si.lpDesktop = string.Empty; 

Для этого:

si.lpDesktop = null; 

... так что новый процесс наследует рабочий стол родительского процесса. Теперь он работает безупречно.