2014-01-17 413 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

相關問題