2014-01-17 5 views
1

Я пытаюсь запустить службу с помощью CreateProcessAsUser, но по какой-то причине при отладке создаются несколько (30+) экземпляров EXE. Процессы начинают появляться в этой строке кода:CreateProcessAsUser Несколько экземпляров приложений?

ret = CreateProcessAsUser(DupedToken, Path, null, ref sa, ref sa, false, 0, (IntPtr)0, "c:\\", ref si, out pi);

я использовал код из этого примера - http://support.microsoft.com/default.aspx?scid=kb;EN-US;889251.

[StructLayout(LayoutKind.Sequential)] 
    public struct STARTUPINFO 
    { 
     public int cb; 
     public String lpReserved; 
     public String lpDesktop; 
     public String lpTitle; 
     public uint dwX; 
     public uint dwY; 
     public uint dwXSize; 
     public uint dwYSize; 
     public uint dwXCountChars; 
     public uint dwYCountChars; 
     public uint dwFillAttribute; 
     public uint dwFlags; 
     public short wShowWindow; 
     public short cbReserved2; 
     public IntPtr lpReserved2; 
     public IntPtr hStdInput; 
     public IntPtr hStdOutput; 
     public IntPtr hStdError; 
    } 

    [StructLayout(LayoutKind.Sequential)] 
    public struct PROCESS_INFORMATION 
    { 
     public IntPtr hProcess; 
     public IntPtr hThread; 
     public uint dwProcessId; 
     public uint dwThreadId; 
    } 

    [StructLayout(LayoutKind.Sequential)] 
    public struct SECURITY_ATTRIBUTES 
    { 
     public int Length; 
     public IntPtr lpSecurityDescriptor; 
     public bool bInheritHandle; 
    } 

    [DllImport("kernel32.dll", EntryPoint = "CloseHandle", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] 
    public extern static bool CloseHandle(IntPtr handle); 

    [DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] 
    public extern static bool CreateProcessAsUser(IntPtr hToken, String lpApplicationName, String lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes, 
     ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandle, int dwCreationFlags, IntPtr lpEnvironment, 
     String lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation); 

    [DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")] 
    public extern static bool DuplicateTokenEx(IntPtr ExistingTokenHandle, uint dwDesiredAccess, 
     ref SECURITY_ATTRIBUTES lpThreadAttributes, int TokenType, 
     int ImpersonationLevel, ref IntPtr DuplicateTokenHandle); 





     string curFile2 = AppDomain.CurrentDomain.BaseDirectory + "OnStart.txt"; 

    public void createProcessAsUser() 
    { 
     IntPtr Token = new IntPtr(0); 
     IntPtr DupedToken = new IntPtr(0); 
     bool  ret; 
     //Label2.Text+=WindowsIdentity.GetCurrent().Name.ToString(); 


     SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES(); 
     sa.bInheritHandle  = false; 
     sa.Length    = Marshal.SizeOf(sa); 
     sa.lpSecurityDescriptor = (IntPtr)0; 

     Token = WindowsIdentity.GetCurrent().Token; 

     const uint GENERIC_ALL = 0x10000000; 

     const int SecurityImpersonation = 2; 
     const int TokenType = 1; 

     ret = DuplicateTokenEx(Token, GENERIC_ALL, ref sa, SecurityImpersonation, TokenType, ref DupedToken); 

     if (ret == false) 
      File.AppendAllText(curFile2, "DuplicateTokenEx failed with " + Marshal.GetLastWin32Error()); 

     else 
      File.AppendAllText(curFile2, "DuplicateTokenEx SUCCESS"); 

     STARTUPINFO si   = new STARTUPINFO(); 
     si.cb     = Marshal.SizeOf(si); 
     si.lpDesktop   = ""; 

     string Path; 
     Path = @"C:\myEXEpath"; 

     PROCESS_INFORMATION pi = new PROCESS_INFORMATION(); 
     ret = CreateProcessAsUser(DupedToken, Path, null, ref sa, ref sa, false, 0, (IntPtr)0, "c:\\", ref si, out pi); 

     if (ret == false) 
      File.AppendAllText(curFile2, "CreateProcessAsUser failed with " + Marshal.GetLastWin32Error()); 
     else 
     { 
      File.AppendAllText(curFile2, "CreateProcessAsUser SUCCESS. The child PID is" + pi.dwProcessId); 

      CloseHandle(pi.hProcess); 
      CloseHandle(pi.hThread); 
     } 

     ret = CloseHandle(DupedToken); 
     if (ret == false) 
      File.AppendAllText(curFile2, Marshal.GetLastWin32Error().ToString()); 
     else 
      File.AppendAllText(curFile2, "CloseHandle SUCCESS"); 
    } 

enter image description here

+0

Хм, нет, это маловероятно. Я бы предположил, что вы некоторое время тестировали свой код и просто забыл убить процесс, который вы начали. Служба запускается с помощью ServiceController btw, учетная запись пользователя, которую она использует, управляется конфигурацией. –

+0

Хорошо, я пытаюсь запустить .exe в пользовательской сессии ... Я иду по правильному пути? – Blake

ответ

1

Шаги, описанные выше, будет генерировать один процесс за выполнение метода createProcessAsUser(). Теперь этот метод не содержит никакого кода для завершения или уничтожения процесса, поэтому повторный вызов этого метода будет генерировать более одного процесса. Когда ваш код будет отображаться, метод будет генерировать только один процесс.

Я думаю, что реальный ответ - как вы называете этот метод. Как указано в комментарии

Я пытаюсь запустить exe-файл в сеансе пользователя

Я могу только предположить, что вы можете называть этот процесс с Session начала, Application_BeginRequest или другим способом который может выполняться несколько раз в зависимости от того, как ваше приложение разработано (код вызова для этого метода был бы замечательным как редактирование).

Как я уже говорил, exe выполняется каждый раз, когда метод вызывается и не завершается. Если вам нужен только один экземпляр приложения, вам придется изучить дерево процессов, чтобы определить, запущен ли процесс. Теперь, если у вас должен быть один процесс для каждого пользователя, вам нужно будет сделать выше, но также сохранить ссылку на идентификатор процесса, который был создан при первом запуске приложения.

Обзора приведенный ниже код для изменения (упрощенный)

public void createProcessAsUser() 
{ 
    //one process per session 
    object sessionPID = Session["_servicePID"]; 
    if (sessionPID != null && sessionPID is int && Process.GetProcessById((int)sessionPID) != null) 
     return; //<-- Return process already running for session 
    else 
     Session.Remove("_servicePID"); 

    //one process per application 
    object applicationPID = Application["_applicationPID"]; 
    if (applicationPID != null && applicationPID is int && Process.GetProcessById((int)applicationPID) != null) 
     return; //<-- Process running for application 
    else 
     Application.Remove("_applicationPID"); 

    //omitted starting code 

    if (ret == false) 
     // omitted log failed 
    else 
    { 
     // omitted log started 

     //for one process per session 
     Session["_servicePID"] = Convert.ToInt32(pi.dwProcessId); 

     //for one process per application 
     Application["_applicationPID"] = Convert.ToInt32(pi.dwProcessId); 

     //close handles 
    } 

    // omitted the rest of the method 
} 

Этих простой сохраняет ссылку на идентификатор процесса, который был создан для применения в либо Session состояния для одного процесса для каждого пользователя или Application состояния для одного процесса на экземпляр приложения.

Теперь, если это ожидаемый результат, вы также можете посмотреть на завершение процесса либо при завершении работы приложения (изящно), либо в завершении сеанса. Это было бы очень похоже на нашу первую проверку, но это можно сделать, как показано ниже. * Обратите внимание, что это не учитывает завершение рабочего процесса без вызова сеансовых \ приложений, которые должны обрабатываться, а также, возможно, в начале приложения.

//session end 
void Session_End(object sender, EventArgs e) 
{ 
    object sessionPID = Session["_servicePID"]; 
    if (sessionPID != null && sessionPID is int) 
    { 
     Process runningProcess = Process.GetProcessById((int)sessionPID); 
     if (runningProcess != null) 
      runningProcess.Kill(); 
    } 
} 

//application end 
void Application_End(object sender, EventArgs e) 
{ 
    object applicationPID = Application["_applicationPID"]; 
    if (applicationPID != null && applicationPID is int && Process.GetProcessById((int)applicationPID) != null) 
    { 
     Process runningProcess = Process.GetProcessById((int)applicationPID); 
     if (runningProcess != null) 
      runningProcess.Kill(); 
    } 
} 

Снова вернемся к исходному вопросу, как остановить несколько экземпляров. Ответ просто прекращает возможность порождать несколько экземпляров, изучая, как вы запускаете экземпляры (например, код вызова для метода createProcessAsUser()) и соответствующим образом настраивайте свой метод, чтобы избежать множественных вызовов.

Пожалуйста, опубликуйте изменение, если этот inst поможет с подробностями о том, как вызывается метод createProcessAsUser().

Update 1:

Session \ Application не существует в контексте. Это произойдет, если метод createProcessUser() находится в другом классе, чем ASPX-страница (как в учебнике).

Из-за этого вам нужно будет изменить для существования в качестве HttpContext этого можно просто сделать по телефону

HttpContext.Currrent

Я приспособил выше метод включает проверку к HttpContext

public void createProcessAsUser() 
{ 
    //find the http context 
    var ctx = HttpContext.Current; 
    if (ctx == null) 
     throw new Exception("No Http Context"); 

    //use the following code for 1 process per user session 
    object sessionPID = ctx.Session["_servicePID"]; 
    if (sessionPID != null && sessionPID is int && Process.GetProcessById((int)sessionPID) != null) 
     return; //<-- Return process already running for session 
    else 
     ctx.Session.Remove("_servicePID"); 

    //use the following code for 1 process per application instance 
    object applicationPID = ctx.Application["_applicationPID"]; 
    if (applicationPID != null && applicationPID is int && Process.GetProcessById((int)sessionPID) != null) 
     return; //<-- Process running for application 
    else 
     ctx.Application.Remove("_applicationPID"); 

    // omitted code 

    if (ret == false) 
    { 
     //omitted logging 
    } 
    else 
    { 
     //omitted logging 

     CloseHandle(pi.hProcess); 
     CloseHandle(pi.hThread); 


     //for one process per session 
     ctx.Session["_servicePID"] = Convert.ToInt32(pi.dwProcessId); 

     //for one process per application 
     ctx.Application["_applicationPID"] = Convert.ToInt32(pi.dwProcessId); 
    } 

    //omitted the rest 
} 

Вы не будете вносить изменения в первые несколько строк, где он получает текущий HttpContext (вы должны добавить using System.Web), позвонив по телефону var ctx = HttpContext.Current

Далее мы просто проверяем, что переменная ctx не равна нулю. Если это null, я бросаю исключение, однако вы можете справиться с этим в любом случае.

Оттуда вместо прямого вызова Session и Application я изменил ссылки на ctx.Session... и ctx.Application...

Update 2:

Это является приложением Windows, вызывая метод выше. Теперь это изменяет игру в мяч, так как вышеприведенный код действительно предназначен для запуска процесса как олицетворенного имени Windows. Теперь олицетворение типично выполняется в WebApplications, а не в WinForms (может быть сделано, хотя).

Если вы не являетесь олицетворением другого пользователя, кроме пользователя, который запускает приложение. Значение пользователя, вошедшего в систему, - это пользователь, который запускает приложение. Если это так, то ваш код станет ALOT проще.

Ниже приведен пример того, как это можно достичь.

/// <summary> 
/// static process ID value 
/// </summary> 
static int? processID = null; 

public void startProcess() 
{ 
    //check if the processID has a value and if the process ID is active 
    if (processID.HasValue && Process.GetProcessById(processID.Value) != null) 
     return; 

    //start a new process 
    var process = new Process(); 
    var processStartInfo = new ProcessStartInfo(@"C:\myProg.exe"); 
    processStartInfo.CreateNoWindow = true; 
    processStartInfo.UseShellExecute = false; 
    process.StartInfo = processStartInfo; 
    process.Start(); 
    //set the process id 
    processID = process.Id; 
} 

Опять же, как это Win Forms приложение, которое вы можете использовать Process объект для запуска процесса, это окно приложения будет работать, как пользователь, запускающий Windows Forms приложение. В этом примере мы также сохраняем статическую ссылку на идентификатор processID и проверяем, запущен ли файл processID (если найден).

+0

Использование вашего сеанса и кода приложения Я получаю сообщение об ошибке «session/application» не существует в текущем контексте – Blake

+0

@Blake, я предполагал, что вы следовали руководству, где этот метод является частью страницы ASPX (cs)? Объекты session \ application являются частью класса HttpContext. Если вы не находитесь на странице ASPX, вам нужно будет захватить текущий HttpContext в своем классе. Я добавил для этого изменение. – Nico

+0

Я создаю службу Windows, а не веб-приложение. На какие изменения вы ссылаетесь? – Blake

Смежные вопросы