2014-01-17 234 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启动服务,它使用的用户帐户由config控制。 –

+0

好吧,我正试图在用户会话中启动.exe文件...我要走正确的道路吗? – Blake

回答

1

你以上概述将产生每方法createProcessAsUser()的执行的一个过程中的步骤。现在,此方法不包含任何代码来终止或终止进程,因此重新调用此方法将生成多个进程。当你的代码被显示时,这个方法将只生成一个进程。

我认为真正的答案是你如何调用这种方法。当您在评论指出

我试图启动.exe文件在用户会话

我只能假设你可以从Session开始的Application_BeginRequest或其它方法调用此过程根据应用程序的设计方式,这可能会被执行多次(这种方法的调用代码在编辑时会很棒)。

如前所述,exe正在执行,每次调用该方法时都不会终止。如果您只希望运行一个应用程序实例,则必须检查流程树以确定流程是否已在运行。现在,如果您应该为每个用户运行一个进程,那么您将需要执行上述操作,同时还要维护一个引用,该引用是第一次启动应用程序时创建的进程ID。

审查下面的代码,以使更改(简化的)

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状态中创建的进程ID的参考每个应用程序实例一个进程。

现在,如果这是预期的结果,您可能还想看看应用程序关闭(正常)或会话结束时终止进程。这与我们的第一次检查非常相似,但可以按照以下所述完成。 *注意,这不考虑工作进程在不调用会话\应用程序结束事件的情况下关闭,应该在应用程序启动时处理这些事件。

//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()的调用代码)来停止产生多个实例的能力,并相应地调整您的方法以避免多次调用。

请发布编辑,如果这有助于详细了解如何调用createProcessAsUser()方法。

更新1:

会议\应用程序不会在上下文中。如果方法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)的前几行中的更改

接下来我们只检查ctx变量是否为空。如果它是空的,我抛出一个异常,但是,无论如何,你可以处理这个。

从那里而不是直接调用SessionApplication我已经改变了引用ctx.Session...ctx.Application...

更新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; 
} 

同样,因为这是一个双赢的窗体应用程序可以使用Process对象来启动一个进程,这个Windows应用程序将运行在运行Windows的用户窗体应用程序。在这个例子中,我们还持有对processID的静态引用,并检查processID(如果找到)是否已在运行。

+0

使用你的会话和应用程序代码我得到一个错误'会话/应用程序'在当前上下文中不存在 – Blake

+0

@Blake,我假设你正在按照指南的方法是ASPX页面(cs)的一部分? session \ application对象是'HttpContext'类的一部分。如果你不在ASPX页面中,你将不得不在你的类中捕获当前的HttpContext。我已经添加了一个编辑来做到这一点。 – Nico

+0

我正在创建一个Windows服务,而不是一个Web应用程序。你指的是什么编辑? – Blake

相关问题