简体   繁体   English

如何通过 c# 中的服务应用程序运行 GUI 应用程序

[英]How run an GUI application via a Service application in c#

I wanted to run an GUI application via a service application in C#, so i have tried with System.Diagnostics.Process.Start() method as below:我想通过 C# 中的服务应用程序运行 GUI 应用程序,所以我尝试使用 System.Diagnostics.Process.Start() 方法如下:

        if (KillTask("notepad") == false)
        {
            //ProcessStartInfo _ProcessStartInfo = new ProcessStartInfo(@"C:\WINDOWS\system32\notepad.exe");
            //_ProcessStartInfo.UseShellExecute = false;
            //_ProcessStartInfo.RedirectStandardError = true;
            //_ProcessStartInfo.RedirectStandardInput = true;
            //_ProcessStartInfo.RedirectStandardOutput = true;
            //_ProcessStartInfo.CreateNoWindow = true;

            //_ProcessStartInfo.ErrorDialog = false;
            //_ProcessStartInfo.WindowStyle = ProcessWindowStyle.Maximized;
            //System.Diagnostics.Process.Start(_ProcessStartInfo);


            System.Diagnostics.Process.Start("notepad.exe");



        }

The problem is that Notepad goes run but with no UI and you can see it in the task manager but no GUI Instance of Notepad was shown.问题是记事本运行但没有 UI,您可以在任务管理器中看到它,但没有显示记事本的 GUI 实例。

I've also tried with the ProcessStartInfo() class as you can see as remarked code, but the problem still exists.我也尝试过使用 ProcessStartInfo() class 正如您看到的注释代码一样,但问题仍然存在。

Have you tried setting the username and password property of ProcessStartInfo to a user that is currently logged on?您是否尝试将 ProcessStartInfo 的用户名和密码属性设置为当前登录的用户? The problem is that the system user does not have a gui available.问题是系统用户没有可用的 gui。

Hello what happens is that the process runs in an environment without a session,您好,发生的情况是该进程在没有 session 的环境中运行,

It is necessary to have a user session, start the process and send the GUI to the graphical desktop of this started session.必须有一个用户 session,启动进程并将 GUI 发送到这个启动的图形桌面 session。

This is verified since when you start your Process you get an ID and it can be checked in TaskManager/Details/这是经过验证的,因为当您启动流程时,您会获得一个 ID,并且可以在 TaskManager/Details/ 中检查它

Sort by PID and you will see that service started but in SYSTEM.按 PID 排序,您将看到该服务已启动但在 SYSTEM 中。

It is necessary to create a class to obtain the logged in User and send the GUI to that desktop有必要创建一个 class 来获取登录用户并将 GUI 发送到该桌面

class ProcessExtensions.cs class ProcessExtensions.cs

public static class ProcessExtensions
{
    #region Win32 Constants

    private const int CREATE_UNICODE_ENVIRONMENT = 0x00000400;
    private const int CREATE_NO_WINDOW = 0x08000000;

    private const int CREATE_NEW_CONSOLE = 0x00000010;

    private const uint INVALID_SESSION_ID = 0xFFFFFFFF;
    private static readonly IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;

    #endregion

    #region DllImports

    [DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
    private static extern bool CreateProcessAsUser(
        IntPtr hToken,
        String lpApplicationName,
        String lpCommandLine,
        IntPtr lpProcessAttributes,
        IntPtr lpThreadAttributes,
        bool bInheritHandle,
        uint dwCreationFlags,
        IntPtr lpEnvironment,
        String lpCurrentDirectory,
        ref STARTUPINFO lpStartupInfo,
        out PROCESS_INFORMATION lpProcessInformation);

    [DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")]
    private static extern bool DuplicateTokenEx(
        IntPtr ExistingTokenHandle,
        uint dwDesiredAccess,
        IntPtr lpThreadAttributes,
        int TokenType,
        int ImpersonationLevel,
        ref IntPtr DuplicateTokenHandle);

    [DllImport("userenv.dll", SetLastError = true)]
    private static extern bool CreateEnvironmentBlock(ref IntPtr lpEnvironment, IntPtr hToken, bool bInherit);

    [DllImport("userenv.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool DestroyEnvironmentBlock(IntPtr lpEnvironment);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool CloseHandle(IntPtr hSnapshot);

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

    [DllImport("Wtsapi32.dll")]
    private static extern uint WTSQueryUserToken(uint SessionId, ref IntPtr phToken);

    [DllImport("wtsapi32.dll", SetLastError = true)]
    private static extern int WTSEnumerateSessions(
        IntPtr hServer,
        int Reserved,
        int Version,
        ref IntPtr ppSessionInfo,
        ref int pCount);

    #endregion

    #region Win32 Structs

    private enum SW
    {
        SW_HIDE = 0,
        SW_SHOWNORMAL = 1,
        SW_NORMAL = 1,
        SW_SHOWMINIMIZED = 2,
        SW_SHOWMAXIMIZED = 3,
        SW_MAXIMIZE = 3,
        SW_SHOWNOACTIVATE = 4,
        SW_SHOW = 5,
        SW_MINIMIZE = 6,
        SW_SHOWMINNOACTIVE = 7,
        SW_SHOWNA = 8,
        SW_RESTORE = 9,
        SW_SHOWDEFAULT = 10,
        SW_MAX = 10
    }

    private enum WTS_CONNECTSTATE_CLASS
    {
        WTSActive,
        WTSConnected,
        WTSConnectQuery,
        WTSShadow,
        WTSDisconnected,
        WTSIdle,
        WTSListen,
        WTSReset,
        WTSDown,
        WTSInit
    }

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

    private enum SECURITY_IMPERSONATION_LEVEL
    {
        SecurityAnonymous = 0,
        SecurityIdentification = 1,
        SecurityImpersonation = 2,
        SecurityDelegation = 3,
    }

    [StructLayout(LayoutKind.Sequential)]
    private 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;
    }

    private enum TOKEN_TYPE
    {
        TokenPrimary = 1,
        TokenImpersonation = 2
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct WTS_SESSION_INFO
    {
        public readonly UInt32 SessionID;

        [MarshalAs(UnmanagedType.LPStr)]
        public readonly String pWinStationName;

        public readonly WTS_CONNECTSTATE_CLASS State;
    }

    #endregion

    // Gets the user token from the currently active session
    private static bool GetSessionUserToken(ref IntPtr phUserToken)
    {
        var bResult = false;
        var hImpersonationToken = IntPtr.Zero;
        var activeSessionId = INVALID_SESSION_ID;
        var pSessionInfo = IntPtr.Zero;
        var sessionCount = 0;

        // Get a handle to the user access token for the current active session.
        if (WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, ref pSessionInfo, ref sessionCount) != 0)
        {
            var arrayElementSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));
            var current = pSessionInfo;

            for (var i = 0; i < sessionCount; i++)
            {
                var si = (WTS_SESSION_INFO)Marshal.PtrToStructure((IntPtr)current, typeof(WTS_SESSION_INFO));
                current += arrayElementSize;

                if (si.State == WTS_CONNECTSTATE_CLASS.WTSActive)
                {
                    activeSessionId = si.SessionID;
                }
            }
        }

        // If enumerating did not work, fall back to the old method
        if (activeSessionId == INVALID_SESSION_ID)
        {
            activeSessionId = WTSGetActiveConsoleSessionId();
        }

        if (WTSQueryUserToken(activeSessionId, ref hImpersonationToken) != 0)
        {
            // Convert the impersonation token to a primary token
            bResult = DuplicateTokenEx(hImpersonationToken, 0, IntPtr.Zero,
                (int)SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, (int)TOKEN_TYPE.TokenPrimary,
                ref phUserToken);

            CloseHandle(hImpersonationToken);
        }

        return bResult;
    }

    public static bool StartProcessAsCurrentUser(string appPath, string cmdLine = null, string workDir = null, bool visible = true)
    {
        var hUserToken = IntPtr.Zero;
        var startInfo = new STARTUPINFO();
        var procInfo = new PROCESS_INFORMATION();
        var pEnv = IntPtr.Zero;
        int iResultOfCreateProcessAsUser;

        startInfo.cb = Marshal.SizeOf(typeof(STARTUPINFO));

        try
        {
            if (!GetSessionUserToken(ref hUserToken))
            {
                throw new Exception("StartProcessAsCurrentUser: GetSessionUserToken failed.");
            }

            uint dwCreationFlags = CREATE_UNICODE_ENVIRONMENT | (uint)(visible ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW);
            startInfo.wShowWindow = (short)(visible ? SW.SW_SHOW : SW.SW_HIDE);
            startInfo.lpDesktop = "winsta0\\default";

            if (!CreateEnvironmentBlock(ref pEnv, hUserToken, false))
            {
                throw new Exception("StartProcessAsCurrentUser: CreateEnvironmentBlock failed.");
            }

            if (!CreateProcessAsUser(hUserToken,
                appPath, // Application Name
                cmdLine, // Command Line
                IntPtr.Zero,
                IntPtr.Zero,
                false,
                dwCreationFlags,
                pEnv,
                workDir, // Working directory
                ref startInfo,
                out procInfo))
            {
                iResultOfCreateProcessAsUser = Marshal.GetLastWin32Error();
                throw new Exception("StartProcessAsCurrentUser: CreateProcessAsUser failed.  Error Code -" + iResultOfCreateProcessAsUser);
            }

            iResultOfCreateProcessAsUser = Marshal.GetLastWin32Error();
        }
        finally
        {
            CloseHandle(hUserToken);
            if (pEnv != IntPtr.Zero)
            {
                DestroyEnvironmentBlock(pEnv);
            }
            CloseHandle(procInfo.hThread);
            CloseHandle(procInfo.hProcess);
        }

        return true;
    }

}

Then you just call your process as follows然后你只需按如下方式调用你的过程

 public static void run()
    {
        ProcessExtensions.StartProcessAsCurrentUser(@"calc.exe");
    }

Next Recompile you Service // Install // Start //接下来重新编译你的服务 // 安装 // 开始 //

在此处输入图像描述

More Info:更多信息:

How to start a process from windows service into currently logged in user's session 如何从 windows 服务启动一个进程到当前登录用户的 session

Credits: @murrayju学分:@murrayju

https://github.com/murrayju/CreateProcessAsUser https://github.com/murrayju/CreateProcessAsUser

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM