启动 Windows 服务并启动 cmd

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4147821/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 15:35:52  来源:igfitidea点击:

Start a windows service and launch cmd

c#windowswindows-servicescmd

提问by Proyb2

Do I need to enable Interactive desktp for it to work and what is the correct code to start an EXE or cmd window? I'm still unable to start the service even when I had enable it to interact with desktop.

我是否需要启用交互式桌面才能工作?启动 EXE 或 cmd 窗口的正确代码是什么?即使我已启用它与桌面交互,我仍然无法启动该服务。

I would be using an chat engine so it is easier to manage as a windows service.

我将使用聊天引擎,因此作为 Windows 服务更容易管理。

What wrong with my code?

我的代码有什么问题?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceProcess;
using System.Diagnostics;
using System.ComponentModel;
using System.Threading;

namespace MyNewService
{
    class Program : ServiceBase
    {
        static void Main(string[] args)
        {
        }

        public Program()
        {
            this.ServiceName = "Chatter";
        }

        protected override void OnStart(string[] args)
        {
            base.OnStart(args);

            //TODO: place your start code here
            ThreadStart starter = new ThreadStart(bw_DoWork);
            Thread t = new Thread(starter);
            t.Start();

        }

        private void bw_DoWork()
        {
            Process p = new Process();
            p.StartInfo = new ProcessStartInfo(@"C:\Windows\system32\cmd.exe");
            p.Start();
            p.WaitForExit();
            base.Stop();
        }

        protected override void OnStop()
        {
            base.OnStop();

            //TODO: clean up any variables and stop any threads
        }
    }
}

回答by Aliostad

I have gone through all the pain of doing this.

我已经经历了做这件事的所有痛苦。

Under windows 7/Vista/2008 it is not possible to load any interactive process from a service - without calling a number of Win API. = BLACK MAGIC

在 windows 7/Vista/2008 下,不可能从服务加载任何交互进程 - 不调用多个 Win API。= 黑魔法

Have a look hereand here.

看看这里这里

The code below does the trick, use it with your own risk:

下面的代码可以解决问题,请自行承担风险:

public static class ProcessAsCurrentUser
{

    /// <summary>
    /// Connection state of a session.
    /// </summary>
    public enum ConnectionState
    {
        /// <summary>
        /// A user is logged on to the session.
        /// </summary>
        Active,
        /// <summary>
        /// A client is connected to the session.
        /// </summary>
        Connected,
        /// <summary>
        /// The session is in the process of connecting to a client.
        /// </summary>
        ConnectQuery,
        /// <summary>
        /// This session is shadowing another session.
        /// </summary>
        Shadowing,
        /// <summary>
        /// The session is active, but the client has disconnected from it.
        /// </summary>
        Disconnected,
        /// <summary>
        /// The session is waiting for a client to connect.
        /// </summary>
        Idle,
        /// <summary>
        /// The session is listening for connections.
        /// </summary>
        Listening,
        /// <summary>
        /// The session is being reset.
        /// </summary>
        Reset,
        /// <summary>
        /// The session is down due to an error.
        /// </summary>
        Down,
        /// <summary>
        /// The session is initializing.
        /// </summary>
        Initializing
    }


    [StructLayout(LayoutKind.Sequential)]
    class SECURITY_ATTRIBUTES
    {
        public int nLength;
        public IntPtr lpSecurityDescriptor;
        public int bInheritHandle;
    }


    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    struct STARTUPINFO
    {
        public Int32 cb;
        public string lpReserved;
        public string lpDesktop;
        public string lpTitle;
        public Int32 dwX;
        public Int32 dwY;
        public Int32 dwXSize;
        public Int32 dwYSize;
        public Int32 dwXCountChars;
        public Int32 dwYCountChars;
        public Int32 dwFillAttribute;
        public Int32 dwFlags;
        public Int16 wShowWindow;
        public Int16 cbReserved2;
        public IntPtr lpReserved2;
        public IntPtr hStdInput;
        public IntPtr hStdOutput;
        public IntPtr hStdError;
    }

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

    enum LOGON_TYPE
    {
        LOGON32_LOGON_INTERACTIVE = 2,
        LOGON32_LOGON_NETWORK,
        LOGON32_LOGON_BATCH,
        LOGON32_LOGON_SERVICE,
        LOGON32_LOGON_UNLOCK = 7,
        LOGON32_LOGON_NETWORK_CLEARTEXT,
        LOGON32_LOGON_NEW_CREDENTIALS
    }

    enum LOGON_PROVIDER
    {
        LOGON32_PROVIDER_DEFAULT,
        LOGON32_PROVIDER_WINNT35,
        LOGON32_PROVIDER_WINNT40,
        LOGON32_PROVIDER_WINNT50
    }

    [Flags]
    enum CreateProcessFlags : uint
    {
        CREATE_BREAKAWAY_FROM_JOB = 0x01000000,
        CREATE_DEFAULT_ERROR_MODE = 0x04000000,
        CREATE_NEW_CONSOLE = 0x00000010,
        CREATE_NEW_PROCESS_GROUP = 0x00000200,
        CREATE_NO_WINDOW = 0x08000000,
        CREATE_PROTECTED_PROCESS = 0x00040000,
        CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000,
        CREATE_SEPARATE_WOW_VDM = 0x00000800,
        CREATE_SHARED_WOW_VDM = 0x00001000,
        CREATE_SUSPENDED = 0x00000004,
        CREATE_UNICODE_ENVIRONMENT = 0x00000400,
        DEBUG_ONLY_THIS_PROCESS = 0x00000002,
        DEBUG_PROCESS = 0x00000001,
        DETACHED_PROCESS = 0x00000008,
        EXTENDED_STARTUPINFO_PRESENT = 0x00080000,
        INHERIT_PARENT_AFFINITY = 0x00010000
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct WTS_SESSION_INFO
    {
        public int SessionID;
        [MarshalAs(UnmanagedType.LPTStr)]
        public string WinStationName;
        public ConnectionState State;
    }

    [DllImport("wtsapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern Int32 WTSEnumerateSessions(IntPtr hServer, int reserved, int version,
                                                    ref IntPtr sessionInfo, ref int count);


    [DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUserW", SetLastError = true, CharSet = CharSet.Auto)]
    static extern bool CreateProcessAsUser(
        IntPtr hToken,
        string lpApplicationName,
        string lpCommandLine,
        IntPtr lpProcessAttributes,
        IntPtr lpThreadAttributes,
        bool bInheritHandles,
        UInt32 dwCreationFlags,
        IntPtr lpEnvironment,
        string lpCurrentDirectory,
        ref STARTUPINFO lpStartupInfo,
        out PROCESS_INFORMATION lpProcessInformation);

    [DllImport("wtsapi32.dll")]
    public static extern void WTSFreeMemory(IntPtr memory);

    [DllImport("kernel32.dll")]
    private static extern UInt32 WTSGetActiveConsoleSessionId();

    [DllImport("wtsapi32.dll", SetLastError = true)]
    static extern int WTSQueryUserToken(UInt32 sessionId, out IntPtr Token);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public extern static bool DuplicateTokenEx(
        IntPtr hExistingToken,
        uint dwDesiredAccess,
        IntPtr lpTokenAttributes,
        int ImpersonationLevel,
        int TokenType,
        out IntPtr phNewToken);

    private const int TokenImpersonation = 2;
    private const int SecurityIdentification = 1;
    private const int MAXIMUM_ALLOWED = 0x2000000;
    private const int TOKEN_DUPLICATE = 0x2;
    private const int TOKEN_QUERY = 0x00000008;

    /// <summary>
    /// Launches a process for the current logged on user if there are any.
    /// If none, return false as well as in case of 
    /// 
    /// ##### !!! BEWARE !!! ####  ------------------------------------------
    /// This code will only work when running in a windows service (where it is really needed)
    /// so in case you need to test it, it needs to run in the service. Reason
    /// is a security privileg which only services have (SE_??? something, cant remember)!
    /// </summary>
    /// <param name="processExe"></param>
    /// <returns></returns>
    public static bool CreateProcessAsCurrentUser(string processExe)
    {

        IntPtr duplicate = new IntPtr();
        STARTUPINFO info = new STARTUPINFO();
        PROCESS_INFORMATION procInfo = new PROCESS_INFORMATION();

        Debug.WriteLine(string.Format("CreateProcessAsCurrentUser. processExe: " + processExe));

        IntPtr p = GetCurrentUserToken();

        bool result = DuplicateTokenEx(p, MAXIMUM_ALLOWED | TOKEN_QUERY | TOKEN_DUPLICATE, IntPtr.Zero, SecurityIdentification, SecurityIdentification, out duplicate);
        Debug.WriteLine(string.Format("DuplicateTokenEx result: {0}", result));
        Debug.WriteLine(string.Format("duplicate: {0}", duplicate));


        if (result)
        {
            result = CreateProcessAsUser(duplicate, processExe, null,
                IntPtr.Zero, IntPtr.Zero, false, (UInt32)CreateProcessFlags.CREATE_NEW_CONSOLE, IntPtr.Zero, null,
                ref info, out procInfo);
            Debug.WriteLine(string.Format("CreateProcessAsUser result: {0}", result));

        }


        if (p.ToInt32() != 0)
        {
            Marshal.Release(p);
            Debug.WriteLine(string.Format("Released handle p: {0}", p));
        }


        if (duplicate.ToInt32() != 0)
        {
            Marshal.Release(duplicate);
            Debug.WriteLine(string.Format("Released handle duplicate: {0}", duplicate));
        }



        return result;
    }

    public static int GetCurrentSessionId()
    {
        uint sessionId = WTSGetActiveConsoleSessionId();
        Debug.WriteLine(string.Format("sessionId: {0}", sessionId));

        if (sessionId == 0xFFFFFFFF)
            return -1;
        else
            return (int)sessionId;
    }

    public static bool IsUserLoggedOn()
    {
        List<WTS_SESSION_INFO> wtsSessionInfos = ListSessions();
        Debug.WriteLine(string.Format("Number of sessions: {0}", wtsSessionInfos.Count));
        return wtsSessionInfos.Where(x => x.State == ConnectionState.Active).Count() > 0;
    }

    private static IntPtr GetCurrentUserToken()
    {
        List<WTS_SESSION_INFO> wtsSessionInfos = ListSessions();
        int sessionId = wtsSessionInfos.Where(x => x.State == ConnectionState.Active).FirstOrDefault().SessionID;
        //int sessionId = GetCurrentSessionId();

        Debug.WriteLine(string.Format("sessionId: {0}", sessionId));
        if (sessionId == int.MaxValue)
        {
            return IntPtr.Zero;
        }
        else
        {
            IntPtr p = new IntPtr();
            int result = WTSQueryUserToken((UInt32)sessionId, out p);
            Debug.WriteLine(string.Format("WTSQueryUserToken result: {0}", result));
            Debug.WriteLine(string.Format("WTSQueryUserToken p: {0}", p));

            return p;
        }
    }

    public static List<WTS_SESSION_INFO> ListSessions()
    {
        IntPtr server = IntPtr.Zero;
        List<WTS_SESSION_INFO> ret = new List<WTS_SESSION_INFO>();

        try
        {
            IntPtr ppSessionInfo = IntPtr.Zero;

            Int32 count = 0;
            Int32 retval = WTSEnumerateSessions(IntPtr.Zero, 0, 1, ref ppSessionInfo, ref count);
            Int32 dataSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));

            Int64 current = (int)ppSessionInfo;

            if (retval != 0)
            {
                for (int i = 0; i < count; i++)
                {
                    WTS_SESSION_INFO si = (WTS_SESSION_INFO)Marshal.PtrToStructure((System.IntPtr)current, typeof(WTS_SESSION_INFO));
                    current += dataSize;

                    ret.Add(si);
                }

                WTSFreeMemory(ppSessionInfo);
            }
        }
        catch (Exception exception)
        {
            Debug.WriteLine(exception.ToString());
        }

        return ret;
    }

}

回答by Brad Cunningham

When running as a service you won't be able to launch anything that needs to interact with the desktop OR will spawn it's own windows.

当作为服务运行时,您将无法启动任何需要与桌面交互的东西,或者会生成它自己的窗口。

As Aliostad said, you need to call Win API calls to CreateProcessAsUser and emulate the user in order with this to work. This involves emulating the logged in user and using their credentials to "lift" your process into process isolation level 1 (which gives you access to the windowing system and things like the GPU).

正如 Aliostad 所说,您需要调用 Win API 调用 CreateProcessAsUser 并模拟用户以使其工作。这涉及模拟登录用户并使用他们的凭据将您的进程“提升”到进程隔离级别 1(它使您可以访问窗口系统和 GPU 之类的东西)。

I am doing this in an app I wrote and it does work but I agree with Aliostad there is a bit of Black magic going on and it generally sucks

我在我写的一个应用程序中这样做并且它确实有效,但我同意 Aliostad 有一些黑魔法正在进行,它通常很糟糕

Having said all that, you can spawn worker processes from within a service as along as they don't require things that are in process isolation level 1 (Windowing, GPU etc..)

说了这么多,您可以从服务中生成工作进程,因为它们不需要处于进程隔离级别 1(窗口、GPU 等)中的东西。

cmd.exe by default will try to create a window, this is why your example is failing. You could set the following ProcessStartInfo properties to get it work.

cmd.exe 默认会尝试创建一个窗口,这就是您的示例失败的原因。您可以设置以下 ProcessStartInfo 属性以使其工作。

CreateNoWindow WindowStyle

CreateNoWindow WindowStyle

回答by Erich Brunner

I wrote an application watchdog service which simply restarts an application (in my case a Console Window App).

我写了一个应用程序看门狗服务,它只是重新启动一个应用程序(在我的例子中是一个控制台窗口应用程序)。

  1. I found a very good Hands-On Lab Tutorial (in C++) which I tried at it worked for Session 0 Isolation at: http://msdn.microsoft.com/en-us/Windows7TrainingCourse_Win7Session0Isolation

  2. I converted that C++ Sample into C#. After a few tests it worked. As long as I stay logged on and don't log out and log on again that code works perfect. I have to do a little to catch the Session Logout/Login. But for simple log-in and Work Condition in Windows the watchdog works as expected.

  3. Here is that required PInvoke code:

    [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;
    }
    
    
    public enum TOKEN_TYPE
    {
        TokenPrimary = 1,
        TokenImpersonation
    }
    
    public enum SECURITY_IMPERSONATION_LEVEL
    {
        SecurityAnonymous,
        SecurityIdentification,
        SecurityImpersonation,
        SecurityDelegation
    }
    
    [StructLayout(LayoutKind.Sequential)]
    public struct SECURITY_ATTRIBUTES
    {
        public int nLength;
        public IntPtr lpSecurityDescriptor;
        public int bInheritHandle;
    }
    
    
    [DllImport("kernel32.dll", EntryPoint = "CloseHandle", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
    public extern static bool CloseHandle(IntPtr handle);
    
    [DllImport("kernel32.dll")]
    public static extern uint WTSGetActiveConsoleSessionId();
    
    [DllImport("wtsapi32.dll", SetLastError = true)]
    public static extern bool WTSQueryUserToken(UInt32 sessionId, out IntPtr Token);
    
    [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", CharSet = CharSet.Auto, SetLastError = true)]
    public extern static bool DuplicateTokenEx(
        IntPtr hExistingToken,
        uint dwDesiredAccess,
        ref SECURITY_ATTRIBUTES lpTokenAttributes,
        SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
        TOKEN_TYPE TokenType,
        out IntPtr phNewToken);
    
  4. Here is the encapsuled method :

        private void CreateUserProcess()
    {
    
        bool ret;
        SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
    
        uint dwSessionID = WTSGetActiveConsoleSessionId();
    
    
        this.EventLog.WriteEntry("WTSGetActiveConsoleSessionId: " + dwSessionID, EventLogEntryType.FailureAudit);
    
    
        IntPtr Token = new IntPtr();
        ret = WTSQueryUserToken((UInt32)dwSessionID, out Token);
    
        if (ret == false)
        {
            this.EventLog.WriteEntry("WTSQueryUserToken failed with " + Marshal.GetLastWin32Error(), EventLogEntryType.FailureAudit);
    
        }
    
        const uint MAXIMUM_ALLOWED  = 0x02000000;
        IntPtr DupedToken = IntPtr.Zero;
    
        ret = DuplicateTokenEx(Token,
            MAXIMUM_ALLOWED,
            ref sa,
            SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
            TOKEN_TYPE.TokenPrimary,
            out DupedToken);
    
        if (ret == false)
        {
            this.EventLog.WriteEntry("DuplicateTokenEx failed with " + Marshal.GetLastWin32Error(), EventLogEntryType.FailureAudit);
    
        }
        else
        {
            this.EventLog.WriteEntry("DuplicateTokenEx SUCCESS", EventLogEntryType.SuccessAudit); 
        }
    
        STARTUPINFO si = new STARTUPINFO();
        si.cb = Marshal.SizeOf(si);
        //si.lpDesktop = "";
    
        string commandLinePath;
    
        // commandLinePath example: "c:\myapp.exe c:\myconfig.xml" . cmdLineArgs can be ommited
        commandLinePath = AppPath + " " + CmdLineArgs;
    
        PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
        //CreateProcessAsUser(hDuplicatedToken, NULL, lpszClientPath, NULL, NULL, FALSE,
        //                    0,
        //                    NULL, NULL, &si, &pi)
        ret = CreateProcessAsUser(DupedToken, null, commandLinePath, ref sa, ref sa, false, 0, (IntPtr)0, null, ref si, out pi);
    
        if (ret == false)
        {
            this.EventLog.WriteEntry("CreateProcessAsUser failed with " + Marshal.GetLastWin32Error(), EventLogEntryType.FailureAudit);
    
    
        }
        else
        {
            this.EventLog.WriteEntry("CreateProcessAsUser SUCCESS.  The child PID is" + pi.dwProcessId, EventLogEntryType.SuccessAudit);
            CloseHandle(pi.hProcess);
            CloseHandle(pi.hThread);
        }
    
        ret = CloseHandle(DupedToken);
        if (ret == false)
        {
            this.EventLog.WriteEntry("CloseHandle LastError: " + Marshal.GetLastWin32Error(), EventLogEntryType.Error);
        }
        else
        {
        this.EventLog.WriteEntry("CloseHandle SUCCESS", EventLogEntryType.Information);
    
        }
    }
    
  1. 我找到了一个非常好的动手实验教程(在 C++ 中),我尝试在它用于会话 0 隔离:http: //msdn.microsoft.com/en-us/Windows7TrainingCourse_Win7Session0Isolation

  2. 我将该 C++ 示例转换为 C#。经过几次测试,它起作用了。只要我保持登录状态并且不注销并再次登录,该代码就可以完美运行。我必须做一点才能捕获会话注销/登录。但是对于 Windows 中的简单登录和工作条件,看门狗按预期工作。

  3. 这是所需的 PInvoke 代码:

    [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;
    }
    
    
    public enum TOKEN_TYPE
    {
        TokenPrimary = 1,
        TokenImpersonation
    }
    
    public enum SECURITY_IMPERSONATION_LEVEL
    {
        SecurityAnonymous,
        SecurityIdentification,
        SecurityImpersonation,
        SecurityDelegation
    }
    
    [StructLayout(LayoutKind.Sequential)]
    public struct SECURITY_ATTRIBUTES
    {
        public int nLength;
        public IntPtr lpSecurityDescriptor;
        public int bInheritHandle;
    }
    
    
    [DllImport("kernel32.dll", EntryPoint = "CloseHandle", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
    public extern static bool CloseHandle(IntPtr handle);
    
    [DllImport("kernel32.dll")]
    public static extern uint WTSGetActiveConsoleSessionId();
    
    [DllImport("wtsapi32.dll", SetLastError = true)]
    public static extern bool WTSQueryUserToken(UInt32 sessionId, out IntPtr Token);
    
    [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", CharSet = CharSet.Auto, SetLastError = true)]
    public extern static bool DuplicateTokenEx(
        IntPtr hExistingToken,
        uint dwDesiredAccess,
        ref SECURITY_ATTRIBUTES lpTokenAttributes,
        SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
        TOKEN_TYPE TokenType,
        out IntPtr phNewToken);
    
  4. 这是封装的方法:

        private void CreateUserProcess()
    {
    
        bool ret;
        SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
    
        uint dwSessionID = WTSGetActiveConsoleSessionId();
    
    
        this.EventLog.WriteEntry("WTSGetActiveConsoleSessionId: " + dwSessionID, EventLogEntryType.FailureAudit);
    
    
        IntPtr Token = new IntPtr();
        ret = WTSQueryUserToken((UInt32)dwSessionID, out Token);
    
        if (ret == false)
        {
            this.EventLog.WriteEntry("WTSQueryUserToken failed with " + Marshal.GetLastWin32Error(), EventLogEntryType.FailureAudit);
    
        }
    
        const uint MAXIMUM_ALLOWED  = 0x02000000;
        IntPtr DupedToken = IntPtr.Zero;
    
        ret = DuplicateTokenEx(Token,
            MAXIMUM_ALLOWED,
            ref sa,
            SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
            TOKEN_TYPE.TokenPrimary,
            out DupedToken);
    
        if (ret == false)
        {
            this.EventLog.WriteEntry("DuplicateTokenEx failed with " + Marshal.GetLastWin32Error(), EventLogEntryType.FailureAudit);
    
        }
        else
        {
            this.EventLog.WriteEntry("DuplicateTokenEx SUCCESS", EventLogEntryType.SuccessAudit); 
        }
    
        STARTUPINFO si = new STARTUPINFO();
        si.cb = Marshal.SizeOf(si);
        //si.lpDesktop = "";
    
        string commandLinePath;
    
        // commandLinePath example: "c:\myapp.exe c:\myconfig.xml" . cmdLineArgs can be ommited
        commandLinePath = AppPath + " " + CmdLineArgs;
    
        PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
        //CreateProcessAsUser(hDuplicatedToken, NULL, lpszClientPath, NULL, NULL, FALSE,
        //                    0,
        //                    NULL, NULL, &si, &pi)
        ret = CreateProcessAsUser(DupedToken, null, commandLinePath, ref sa, ref sa, false, 0, (IntPtr)0, null, ref si, out pi);
    
        if (ret == false)
        {
            this.EventLog.WriteEntry("CreateProcessAsUser failed with " + Marshal.GetLastWin32Error(), EventLogEntryType.FailureAudit);
    
    
        }
        else
        {
            this.EventLog.WriteEntry("CreateProcessAsUser SUCCESS.  The child PID is" + pi.dwProcessId, EventLogEntryType.SuccessAudit);
            CloseHandle(pi.hProcess);
            CloseHandle(pi.hThread);
        }
    
        ret = CloseHandle(DupedToken);
        if (ret == false)
        {
            this.EventLog.WriteEntry("CloseHandle LastError: " + Marshal.GetLastWin32Error(), EventLogEntryType.Error);
        }
        else
        {
        this.EventLog.WriteEntry("CloseHandle SUCCESS", EventLogEntryType.Information);
    
        }
    }
    

I hope it is useful !

我希望它有用!

回答by Alex Fotios

The function below will launch an executable as active user from a windows service.

下面的函数将作为活动用户从 Windows 服务启动可执行文件。

//Function to run a process as active user from windows service
void ImpersonateActiveUserAndRun(WCHAR* path, WCHAR* args)
{
    DWORD session_id = -1;
    DWORD session_count = 0;

    WTS_SESSION_INFOA *pSession = NULL;


    if (WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSession, &session_count))
    {
        //log success
    }
    else
    {
        //log error
        return;
    }

    for (int i = 0; i < session_count; i++)
    {
        session_id = pSession[i].SessionId;

        WTS_CONNECTSTATE_CLASS wts_connect_state = WTSDisconnected;
        WTS_CONNECTSTATE_CLASS* ptr_wts_connect_state = NULL;

        DWORD bytes_returned = 0;
        if (::WTSQuerySessionInformation(
            WTS_CURRENT_SERVER_HANDLE,
            session_id,
            WTSConnectState,
            reinterpret_cast<LPTSTR*>(&ptr_wts_connect_state),
            &bytes_returned))
        {
            wts_connect_state = *ptr_wts_connect_state;
            ::WTSFreeMemory(ptr_wts_connect_state);
            if (wts_connect_state != WTSActive) continue;
        }
        else
        {
            //log error
            continue;
        }

        HANDLE hImpersonationToken;

        if (!WTSQueryUserToken(session_id, &hImpersonationToken))
        {
            //log error
            continue;
        }


        //Get real token from impersonation token
        DWORD neededSize1 = 0;
        HANDLE *realToken = new HANDLE;
        if (GetTokenInformation(hImpersonationToken, (::TOKEN_INFORMATION_CLASS) TokenLinkedToken, realToken, sizeof(HANDLE), &neededSize1))
        {
            CloseHandle(hImpersonationToken);
            hImpersonationToken = *realToken;
        }
        else
        {
            //log error
            continue;
        }


        HANDLE hUserToken;

        if (!DuplicateTokenEx(hImpersonationToken,
            //0,
            //MAXIMUM_ALLOWED,
            TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS | MAXIMUM_ALLOWED,
            NULL,
            SecurityImpersonation,
            TokenPrimary,
            &hUserToken))
        {
            //log error
            continue;
        }

        // Get user name of this process
        //LPTSTR pUserName = NULL;
        WCHAR* pUserName;
        DWORD user_name_len = 0;

        if (WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, session_id, WTSUserName, &pUserName, &user_name_len))
        {
            //log username contained in pUserName WCHAR string
        }

        //Free memory                         
        if (pUserName) WTSFreeMemory(pUserName);

        ImpersonateLoggedOnUser(hUserToken);

        STARTUPINFOW StartupInfo;
        GetStartupInfoW(&StartupInfo);
        StartupInfo.cb = sizeof(STARTUPINFOW);
        //StartupInfo.lpDesktop = "winsta0\default";

        PROCESS_INFORMATION processInfo;

        SECURITY_ATTRIBUTES Security1;
        Security1.nLength = sizeof SECURITY_ATTRIBUTES;

        SECURITY_ATTRIBUTES Security2;
        Security2.nLength = sizeof SECURITY_ATTRIBUTES;

        void* lpEnvironment = NULL;

        // Get all necessary environment variables of logged in user
        // to pass them to the new process
        BOOL resultEnv = CreateEnvironmentBlock(&lpEnvironment, hUserToken, FALSE);
        if (!resultEnv)
        {
            //log error
            continue;
        }

        WCHAR PP[1024]; //path and parameters
        ZeroMemory(PP, 1024 * sizeof WCHAR);
        wcscpy(PP, path);
        wcscat(PP, L" ");
        wcscat(PP, args);

        // Start the process on behalf of the current user 
        BOOL result = CreateProcessAsUserW(hUserToken, 
            NULL,
            PP,
            //&Security1,
            //&Security2,
            NULL,
            NULL,
            FALSE, 
            NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE,
            //lpEnvironment,
            NULL,
            //"C:\ProgramData\some_dir",
            NULL,
            &StartupInfo,
            &processInfo);

        if (!result)
        {
            //log error
        }
        else
        {
            //log success
        }

        DestroyEnvironmentBlock(lpEnvironment);

        CloseHandle(hImpersonationToken);
        CloseHandle(hUserToken);
        CloseHandle(realToken);

        RevertToSelf();
    }

    WTSFreeMemory(pSession);
}