How to create Windows service and launch desktop app

A good artical introduce how to create and install Windows service via C#. The artical link is attached for future refer.

http://www.cnblogs.com/wuhuacong/archive/2009/02/11/1381428.html

Call a program via Windows service is not easy as we used before. We cannot use Process to launch an program with UI. Following method got from MSDN forum is very useful:

using System;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace WindowsServiceLaunchingExe
{
    class NativeMethods
    {
        [StructLayout(LayoutKind.Sequential)]
        public struct PROCESS_INFORMATION {
            public IntPtr hProcess;
            public IntPtr hThread;
            public System.UInt32 dwProcessId;
            public System.UInt32 dwThreadId;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct SECURITY_ATTRIBUTES {
            public System.UInt32 nLength;
            public IntPtr lpSecurityDescriptor;
            public bool bInheritHandle;
        }
        
        [StructLayout(LayoutKind.Sequential)] 
        public struct STARTUPINFO {
            public System.UInt32 cb;
            public string lpReserved;
            public string lpDesktop;
            public string lpTitle;
            public System.UInt32 dwX;
            public System.UInt32 dwY;
            public System.UInt32 dwXSize;
            public System.UInt32 dwYSize;
            public System.UInt32 dwXCountChars;
            public System.UInt32 dwYCountChars;
            public System.UInt32 dwFillAttribute;
            public System.UInt32 dwFlags;
            public short wShowWindow;
            public short cbReserved2;
            public IntPtr lpReserved2;
            public IntPtr hStdInput;
            public IntPtr hStdOutput;
            public IntPtr hStdError;
        }
        
        [StructLayout(LayoutKind.Sequential)]
        public struct PROFILEINFO {
            public int dwSize;
            public int dwFlags;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string lpUserName;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string lpProfilePath;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string lpDefaultPath;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string lpServerName;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string lpPolicyPath;
            public IntPtr hProfile;
        }
        
        internal enum SECURITY_IMPERSONATION_LEVEL {
            SecurityAnonymous = 0,
            SecurityIdentification = 1,
            SecurityImpersonation = 2,
            SecurityDelegation = 3
        }

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

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

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

        [DllImport("advapi32.dll", SetLastError = true)]
        private static extern bool OpenProcessToken(IntPtr ProcessHandle, int DesiredAccess, ref IntPtr TokenHandle);

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

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

        private const short SW_SHOW = 1;
        private const short SW_SHOWMAXIMIZED = 7;
        private const int TOKEN_QUERY = 8;
        private const int TOKEN_DUPLICATE = 2;
        private const int TOKEN_ASSIGN_PRIMARY = 1;
        private const int GENERIC_ALL_ACCESS = 268435456;
        private const int STARTF_USESHOWWINDOW = 1;
        private const int STARTF_FORCEONFEEDBACK = 64;
        private const int CREATE_UNICODE_ENVIRONMENT = 0x00000400;
        private const string gs_EXPLORER = "explorer";

        public static void LaunchProcess(string Ps_CmdLine)
        {
            IntPtr li_Token = default(IntPtr);
            IntPtr li_EnvBlock = default(IntPtr);
            Process[] lObj_Processes = Process.GetProcessesByName(gs_EXPLORER);

            // Get explorer.exe id

            // If process not found
            if (lObj_Processes.Length == 0)
            {
                // Exit routine
                return;
            }

            // Get primary token for the user currently logged in
            li_Token = GetPrimaryToken(lObj_Processes[0].Id);

            // If token is nothing
            if (li_Token.Equals(IntPtr.Zero))
            {
                // Exit routine
                return;
            }

            // Get environment block
            li_EnvBlock = GetEnvironmentBlock(li_Token);

            // Launch the process using the environment block and primary token
            LaunchProcessAsUser(Ps_CmdLine, li_Token, li_EnvBlock);

            // If no valid enviroment block found
            if (li_EnvBlock.Equals(IntPtr.Zero))
            {
                // Exit routine
                return;
            }

            // Destroy environment block. Free environment variables created by the 
            // CreateEnvironmentBlock function.
            DestroyEnvironmentBlock(li_EnvBlock);
        }

        private static IntPtr GetPrimaryToken(int Pi_ProcessId) {

            IntPtr li_Token = IntPtr.Zero;
            IntPtr li_PrimaryToken = IntPtr.Zero;
            bool lb_ReturnValue = false;
            Process lObj_Process = Process.GetProcessById(Pi_ProcessId);
            SECURITY_ATTRIBUTES lObj_SecurityAttributes = default(SECURITY_ATTRIBUTES);

            // Get process by id
            // Open a handle to the access token associated with a process. The access token 
            // is a runtime object that represents a user account.
            lb_ReturnValue = OpenProcessToken(lObj_Process.Handle, TOKEN_DUPLICATE, ref li_Token);

            // If successfull in opening handle to token associated with process
            if (lb_ReturnValue) {

                // Create security attributes to pass to the DuplicateTokenEx function
                lObj_SecurityAttributes = new SECURITY_ATTRIBUTES();
                lObj_SecurityAttributes.nLength = Convert.ToUInt32(Marshal.SizeOf(lObj_SecurityAttributes));

                // Create a new access token that duplicates an existing token. This function 
                // can create either a primary token or an impersonation token.
                lb_ReturnValue = DuplicateTokenEx(li_Token, Convert.ToUInt32(TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_QUERY), ref lObj_SecurityAttributes, SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, TOKEN_TYPE.TokenPrimary, ref li_PrimaryToken);

                // If un-successful in duplication of the token
                if (!lb_ReturnValue) {
                    // Throw exception
                    throw new Exception(string.Format("DuplicateTokenEx Error: {0}", Marshal.GetLastWin32Error()));
                }
            }
            else {
                // If un-successful in opening handle for token then throw exception
                throw new Exception(string.Format("OpenProcessToken Error: {0}", Marshal.GetLastWin32Error()));
            }

            // Return primary token
            return li_PrimaryToken;
        }

        private static IntPtr GetEnvironmentBlock(IntPtr Pi_Token) {

            IntPtr li_EnvBlock = IntPtr.Zero;
            bool lb_ReturnValue = CreateEnvironmentBlock(ref li_EnvBlock, Pi_Token, false);

            // Retrieve the environment variables for the specified user. 
            // This block can then be passed to the CreateProcessAsUser function.

            // If not successful in creation of environment block then  
            if (!lb_ReturnValue) {
                // Throw exception
                throw new Exception(string.Format("CreateEnvironmentBlock Error: {0}", Marshal.GetLastWin32Error()));
            }

            // Return the retrieved environment block
            return li_EnvBlock;
        }

        private static void LaunchProcessAsUser(string Ps_CmdLine, IntPtr Pi_Token, IntPtr Pi_EnvBlock) {
            bool lb_Result = false;
            PROCESS_INFORMATION lObj_ProcessInformation = default(PROCESS_INFORMATION);
            SECURITY_ATTRIBUTES lObj_ProcessAttributes = default(SECURITY_ATTRIBUTES);
            SECURITY_ATTRIBUTES lObj_ThreadAttributes = default(SECURITY_ATTRIBUTES);
            STARTUPINFO lObj_StartupInfo = default(STARTUPINFO);

            // Information about the newly created process and its primary thread.
            lObj_ProcessInformation = new PROCESS_INFORMATION();

            // Create security attributes to pass to the CreateProcessAsUser function
            lObj_ProcessAttributes = new SECURITY_ATTRIBUTES();
            lObj_ProcessAttributes.nLength = Convert.ToUInt32(Marshal.SizeOf(lObj_ProcessAttributes));
            lObj_ThreadAttributes = new SECURITY_ATTRIBUTES();
            lObj_ThreadAttributes.nLength = Convert.ToUInt32(Marshal.SizeOf(lObj_ThreadAttributes));

            // To specify the window station, desktop, standard handles, and appearance of the 
            // main window for the new process.
            lObj_StartupInfo = new STARTUPINFO();
            lObj_StartupInfo.cb = Convert.ToUInt32(Marshal.SizeOf(lObj_StartupInfo));
            lObj_StartupInfo.lpDesktop = null;
            lObj_StartupInfo.dwFlags = Convert.ToUInt32(STARTF_USESHOWWINDOW | STARTF_FORCEONFEEDBACK);
            lObj_StartupInfo.wShowWindow = SW_SHOW;

            // Creates a new process and its primary thread. The new process runs in the 
            // security context of the user represented by the specified token.
            lb_Result = CreateProcessAsUser(Pi_Token, null, Ps_CmdLine, ref lObj_ProcessAttributes, ref lObj_ThreadAttributes, true, CREATE_UNICODE_ENVIRONMENT, Pi_EnvBlock, null, ref lObj_StartupInfo, ref lObj_ProcessInformation);

            // If create process return false then
            if (!lb_Result) {
                // Throw exception
                throw new Exception(string.Format("CreateProcessAsUser Error: {0}", Marshal.GetLastWin32Error()));
            }
        }
    }
}

To use the code, create a new windows service project, and add code like the following one:

using System;
using System.Diagnostics;
using System.ServiceProcess;
using System.Timers;

namespace WindowsServiceLaunchingExe
{
    public partial class ExeLauncherSvc : ServiceBase
    {
        public ExeLauncherSvc() {
            InitializeComponent();
        }

        Timer timer;

        protected override void OnStart(string[] args) {
            timer = new Timer(5000);
            timer.Elapsed += (sender, e) => {
                try {
                    timer.Stop();
                    EventLog.WriteEntry("WindowsServiceLaunchingExe", "Launching process...");
                    NativeMethods.LaunchProcess(@"C:\Windows\notepad.exe");
                } catch (Exception ex) {
                    EventLog.WriteEntry("WindowsServiceLaunchingExe", ex.Message);
                }
            };

            timer.Start();
        }


        protected override void OnStop() {
        }
    }
}

More details and code, please refer http://social.msdn.microsoft.com/Forums/en-US/990e7c4a-afa7-47aa-b9a6-d5b6beb19f41/call-a-program-via-windows-service.

 

posted @ 2013-07-23 11:12  Blackeye  阅读(698)  评论(0编辑  收藏  举报