2014-01-17 6 views
1

CreateProcessAsUser를 사용하여 서비스를 시작하려고하지만 디버깅 중에 EXE의 여러 인스턴스 (30 개 이상)가 만들어지고 있습니다. 프로세스는 코드 행에 산란을 시작 :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 \ 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() 메서드에 대한 호출 코드)을 검사하여 여러 인스턴스를 생성하는 기능을 중지하고 여러 호출을 피하기 위해 그에 따라 메서드를 조정하는 것입니다.

createProcessAsUser() 메서드가 호출 된 방법에 대한 자세한 내용은이 인스 트림의 도움이 될 경우 편집을 게시하십시오.

업데이트 1 :

세션 \ 응용 프로그램 컨텍스트에 존재하지 않습니다. 이 방법은 createProcessUser() 메서드가 ASPX 페이지와 다른 클래스에있는 경우 발생합니다 (튜토리얼에있는 것처럼). 때문에 당신이 HttpContext이의 실존에 대한 변경해야합니다이의

은 내가 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.Currrent

를 호출하여 간단하게 수행 할 수 있습니다

HttpContext (using System.Web을 추가해야 함)을 가져 오는 처음 몇 줄의 내용은 변경된 내용이 아니며 var ctx = HttpContext.Current

다음은 ctx 변수가 null이 아닌지 확인합니다. null 인 경우 예외가 발생하지만 어쨌든이 처리 할 수 ​​있습니다.

이 위의 메소드를 호출하는 Windows 응용 프로그램입니다 :

가 대신 직접 SessionApplication 전화에서 나는 ctx.Session...ctx.Application...

업데이트 2에 대한 참조를 변경했습니다. 이제 위의 코드는 프로세스를 가장 된 창 ID로 시작하기위한 것이므로 볼 게임을 변경합니다. 이제 가장은 WinForms가 아닌 WebApplications에서 전형적으로 수행됩니다 (그래도 수행 할 수 있음).

응용 프로그램을 실행하는 사용자와 다른 사용자로 가장하지 않는 경우. 로그인 한 사용자가 응용 프로그램을 실행중인 사용자임을 의미합니다. 이것이 그렇다면 귀하의 코드는 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; 
} 

윈도우를 실행하는 사용자 Forms 응용 프로그램으로이 승리를 당신이 과정을 시작합니다 Process 객체를 사용할 수 Forms 응용 프로그램입니다 다시,이 Windows 응용 프로그램이 실행됩니다. 이 예에서는 processID에 대한 정적 참조를 보유하고 processID (있는 경우)이 이미 실행 중인지 확인합니다.

+0

세션 및 응용 프로그램 코드 사용 '세션/응용 프로그램'이 현재 상황에 존재하지 않습니다. – Blake

+0

@Blake, 방법이 ASPX 페이지 (cs)의 일부인 지침을 따르고 있다고 가정하고 있었습니까? session \ application 객체는'HttpContext' 클래스의 일부입니다. ASPX 페이지에 없다면 클래스의 현재 HttpContext를 캡처해야합니다. 내가 할 편집을 추가했습니다. – Nico

+0

웹 응용 프로그램이 아닌 Windows 서비스를 만들고 있습니다. 어떤 편집을 언급하고 있습니까? – Blake

관련 문제