1

CreateProcessAsUser を使用してサービスを起動しようとしていますが、何らかの理由で、デバッグ時に EXE の複数 (30 以上) のインスタンスが作成されています。プロセスは、次のコード行で生成され始めます。

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");
    }

ここに画像の説明を入力

4

1 に答える 1

1

上記で概説した手順により、メソッドの実行ごとに 1 つのプロセスが生成されますcreateProcessAsUser()。現在、このメソッドにはプロセスを終了または強制終了するコードが含まれていないため、このメソッドを繰り返し呼び出すと、複数のプロセスが生成されます。コードが表示されると、メソッドは 1 つのプロセスのみを生成します。

本当の答えは、このメソッドをどのように呼び出しているかだと思います。あなたがコメントで述べたように

ユーザー セッションで .exe を起動しようとしています。

Sessionアプリケーションの設計方法に応じて、Application_BeginRequest、または複数回実行される可能性のある別のメソッド (このメソッドの呼び出しコードは編集として最適です)からこのプロセスを呼び出している可能性があるとしか思えません。

前に述べたようにexe、メソッドが呼び出されるたびに実行され、終了されません。アプリケーションのインスタンスを 1 つだけ実行したい場合は、プロセス ツリーを調べて、プロセスが既に実行されているかどうかを確認する必要があります。ユーザーごとに 1 つのプロセスを実行する必要がある場合は、上記の手順を実行する必要がありますが、アプリケーションの初回起動時に作成されたプロセス 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
}

この単純な例では、アプリケーション用に作成されたプロセス ID への参照を、Sessionユーザーごとに 1 つのプロセスのApplication状態、またはアプリケーション インスタンスごとに 1 つのプロセスの状態に保存します。

これが意図した結果である場合は、アプリケーションのシャットダウン (正常に) またはセッションの終了時にプロセスを終了することも検討してください。これは最初のチェックと非常に似ていますが、以下のように実行できます。*これは、セッション\アプリケーション終了イベントを呼び出さずにシャットダウンするワーカー プロセスを考慮していないことに注意してください。

//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.Webvar ctx = HttpContext.Current

次に、ctx変数が null でないことを確認します。null の場合は例外をスローしますが、任意の方法でこれを処理できます。

そこから直接呼び出す代わりに、参照を および に変更しSessionましたApplicationctx.Session...ctx.Application...

更新 2:

これは、上記のメソッドを呼び出す Windows アプリケーションです。上記のコードは、偽装された Windows ID としてプロセスを開始することを実際に意図しているため、これで球技が変わります。現在、偽装は通常、WinForms ではなく WebApplications で行われます (ただし、行うことはできます)。

アプリケーションを実行しているユーザーとは別のユーザーになりすましている場合。つまり、ログインしているユーザーは、アプリケーションを実行しているユーザーです。もしそうなら、あなたのコードは非常に簡単になります。

以下は、これを実現する方法の例です。

/// <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 フォーム アプリケーションでProcessあるため、この Windows アプリケーションは、Windows フォーム アプリケーションを実行しているユーザーとして実行されます。この例では、プロセス ID への静的参照も保持し、processID(見つかった場合) が既に実行されているかどうかを確認します。

于 2014-01-28T03:11:46.210 に答える