[c#] How can I run an EXE program from a Windows Service using C#?

How can I run an EXE program from a Windows Service using C#?

This is my code:

System.Diagnostics.Process.Start(@"E:\PROJECT XL\INI SQLLOADER\ConsoleApplication2\ConsoleApplication2\ConsoleApplication2\bin\Debug\ConsoleApplication2.exe");

When I run this service, the application is not starting.
What's wrong with my code?

This question is related to c# .net windows windows-services process.start

The answer is


You should check this MSDN article and download the .docx file and read it carefully , it was very helpful for me.

However this is a class which works fine for my case :

    [StructLayout(LayoutKind.Sequential)]
    internal struct PROCESS_INFORMATION
    {
        public IntPtr hProcess;
        public IntPtr hThread;
        public uint dwProcessId;
        public uint dwThreadId;
    }
    
    [StructLayout(LayoutKind.Sequential)]
    internal struct SECURITY_ATTRIBUTES
    {
        public uint nLength;
        public IntPtr lpSecurityDescriptor;
        public bool bInheritHandle;
    }


    [StructLayout(LayoutKind.Sequential)]
    public struct STARTUPINFO
    {
        public uint 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;

    }

    internal enum SECURITY_IMPERSONATION_LEVEL
    {
        SecurityAnonymous,
        SecurityIdentification,
        SecurityImpersonation,
        SecurityDelegation
    }

    internal enum TOKEN_TYPE
    {
        TokenPrimary = 1,
        TokenImpersonation
    }

    public static class ProcessAsUser
    {

        [DllImport("advapi32.dll", SetLastError = true)]
        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,
            out PROCESS_INFORMATION lpProcessInformation);


        [DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx", SetLastError = true)]
        private static extern bool DuplicateTokenEx(
            IntPtr hExistingToken,
            uint dwDesiredAccess,
            ref SECURITY_ATTRIBUTES lpThreadAttributes,
            Int32 ImpersonationLevel,
            Int32 dwTokenType,
            ref IntPtr phNewToken);


        [DllImport("advapi32.dll", SetLastError = true)]
        private static extern bool OpenProcessToken(
            IntPtr ProcessHandle,
            UInt32 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);

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

        private const short SW_SHOW = 5;
        private const uint TOKEN_QUERY = 0x0008;
        private const uint TOKEN_DUPLICATE = 0x0002;
        private const uint TOKEN_ASSIGN_PRIMARY = 0x0001;
        private const int GENERIC_ALL_ACCESS = 0x10000000;
        private const int STARTF_USESHOWWINDOW = 0x00000001;
        private const int STARTF_FORCEONFEEDBACK = 0x00000040;
        private const uint CREATE_UNICODE_ENVIRONMENT = 0x00000400;


        private static bool LaunchProcessAsUser(string cmdLine, IntPtr token, IntPtr envBlock)
        {
            bool result = false;


            PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
            SECURITY_ATTRIBUTES saProcess = new SECURITY_ATTRIBUTES();
            SECURITY_ATTRIBUTES saThread = new SECURITY_ATTRIBUTES();
            saProcess.nLength = (uint)Marshal.SizeOf(saProcess);
            saThread.nLength = (uint)Marshal.SizeOf(saThread);

            STARTUPINFO si = new STARTUPINFO();
            si.cb = (uint)Marshal.SizeOf(si);


            //if this member is NULL, the new process inherits the desktop
            //and window station of its parent process. If this member is
            //an empty string, the process does not inherit the desktop and
            //window station of its parent process; instead, the system
            //determines if a new desktop and window station need to be created.
            //If the impersonated user already has a desktop, the system uses the
            //existing desktop.

            si.lpDesktop = @"WinSta0\Default"; //Modify as needed
            si.dwFlags = STARTF_USESHOWWINDOW | STARTF_FORCEONFEEDBACK;
            si.wShowWindow = SW_SHOW;
            //Set other si properties as required.

            result = CreateProcessAsUser(
                token,
                null,
                cmdLine,
                ref saProcess,
                ref saThread,
                false,
                CREATE_UNICODE_ENVIRONMENT,
                envBlock,
                null,
                ref si,
                out pi);


            if (result == false)
            {
                int error = Marshal.GetLastWin32Error();
                string message = String.Format("CreateProcessAsUser Error: {0}", error);
                FilesUtilities.WriteLog(message,FilesUtilities.ErrorType.Info);

            }

            return result;
        }


        private static IntPtr GetPrimaryToken(int processId)
        {
            IntPtr token = IntPtr.Zero;
            IntPtr primaryToken = IntPtr.Zero;
            bool retVal = false;
            Process p = null;

            try
            {
                p = Process.GetProcessById(processId);
            }

            catch (ArgumentException)
            {

                string details = String.Format("ProcessID {0} Not Available", processId);
                FilesUtilities.WriteLog(details, FilesUtilities.ErrorType.Info);
                throw;
            }


            //Gets impersonation token
            retVal = OpenProcessToken(p.Handle, TOKEN_DUPLICATE, ref token);
            if (retVal == true)
            {

                SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
                sa.nLength = (uint)Marshal.SizeOf(sa);

                //Convert the impersonation token into Primary token
                retVal = DuplicateTokenEx(
                    token,
                    TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_QUERY,
                    ref sa,
                    (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
                    (int)TOKEN_TYPE.TokenPrimary,
                    ref primaryToken);

                //Close the Token that was previously opened.
                CloseHandle(token);
                if (retVal == false)
                {
                    string message = String.Format("DuplicateTokenEx Error: {0}", Marshal.GetLastWin32Error());
                    FilesUtilities.WriteLog(message, FilesUtilities.ErrorType.Info);

                }

            }

            else
            {

                string message = String.Format("OpenProcessToken Error: {0}", Marshal.GetLastWin32Error());
                FilesUtilities.WriteLog(message, FilesUtilities.ErrorType.Info);

            }

            //We'll Close this token after it is used.
            return primaryToken;

        }

        private static IntPtr GetEnvironmentBlock(IntPtr token)
        {

            IntPtr envBlock = IntPtr.Zero;
            bool retVal = CreateEnvironmentBlock(ref envBlock, token, false);
            if (retVal == false)
            {

                //Environment Block, things like common paths to My Documents etc.
                //Will not be created if "false"
                //It should not adversley affect CreateProcessAsUser.

                string message = String.Format("CreateEnvironmentBlock Error: {0}", Marshal.GetLastWin32Error());
                FilesUtilities.WriteLog(message, FilesUtilities.ErrorType.Info);

            }
            return envBlock;
        }

        public static bool Launch(string appCmdLine /*,int processId*/)
        {

            bool ret = false;

            //Either specify the processID explicitly
            //Or try to get it from a process owned by the user.
            //In this case assuming there is only one explorer.exe

            Process[] ps = Process.GetProcessesByName("explorer");
            int processId = -1;//=processId
            if (ps.Length > 0)
            {
                processId = ps[0].Id;
            }

            if (processId > 1)
            {
                IntPtr token = GetPrimaryToken(processId);

                if (token != IntPtr.Zero)
                {

                    IntPtr envBlock = GetEnvironmentBlock(token);
                    ret = LaunchProcessAsUser(appCmdLine, token, envBlock);
                    if (envBlock != IntPtr.Zero)
                        DestroyEnvironmentBlock(envBlock);

                    CloseHandle(token);
                }

            }
            return ret;
        }

    }

And to execute , simply call like this :

string szCmdline = "AbsolutePathToYourExe\\ExeNameWithoutExtension";
ProcessAsUser.Launch(szCmdline);

I think You are copying the .exe to different location. This might be the problem I guess. When you copy the exe, you are not copying its dependencies.

So, what you can do is, put all dependent dlls in GAC so that any .net exe can access it

Else, do not copy the exe to new location. Just create a environment variable and call the exe in your c#. Since the path is defined in environment variables, the exe is can be accessed by your c# program.

Update:

previously I had some kind of same issue in my c#.net 3.5 project in which I was trying to run a .exe file from c#.net code and that exe was nothing but the another project exe(where i added few supporting dlls for my functionality) and those dlls methods I was using in my exe application. At last I resolved this by creating that application as a separate project to the same solution and i added that project output to my deployment project. According to this scenario I answered, If its not what he wants then I am extremely sorry.


you can use from windows task scheduler for this purpose, there are many libraries like TaskScheduler that help you.

for example consider we want to scheduling a task that will executes once five seconds later:

using (var ts = new TaskService())
        {

            var t = ts.Execute("notepad.exe")
                .Once()
                .Starting(DateTime.Now.AddSeconds(5))
                .AsTask("myTask");

        }

notepad.exe will be executed five seconds later.

for details and more information please go to wiki

if you know which class and method in that assembly you need, you can invoke it yourself like this:

        Assembly assembly = Assembly.LoadFrom("yourApp.exe");
        Type[] types = assembly.GetTypes();
        foreach (Type t in types)
        {
            if (t.Name == "YourClass")
            {
                MethodInfo method = t.GetMethod("YourMethod",
                BindingFlags.Public | BindingFlags.Instance);
                if (method != null)
                {
                    ParameterInfo[] parameters = method.GetParameters();
                    object classInstance = Activator.CreateInstance(t, null);

                    var result = method.Invoke(classInstance, parameters.Length == 0 ? null : parameters);
                    
                    break;
                }
            }
                         
        }
    

Top answer with most upvotes isn't wrong but still the opposite of what I would post. I say it will totally work to start an exe file and you can do this in the context of any user. Logically you just can't have any user interface or ask for user input...

Here is my advice:

  1. Create a simple Console Application that does what your service should do right on start without user interaction. I really recommend not using the Windows Service project type especially because you (currently) can't using .NET Core.
  2. Add code to start your exe you want to call from service

Example to start e.g. plink.exe. You could even listen to the output:

var psi = new ProcessStartInfo()
{
    FileName = "./Client/plink.exe", //path to your *.exe
    Arguments = "-telnet -P 23 127.0.0.1 -l myUsername -raw", //arguments
    RedirectStandardError = true,
    RedirectStandardOutput = true,
    RedirectStandardInput = true,
    UseShellExecute = false,
    CreateNoWindow = true //no window, you can't show it anyway
};

var p = Process.Start(psi);
  1. Use NSSM (Non-Sucking Service Manager) to register that Console Application as service. NSSM can be controlled via command line and can show an UI to configure the service or you configure it via command line. You can run the service in the context of any user if you know the login data of that user.

I took LocalSystem account which is default and more than Local Service. It worked fine without having to enter login information of a specific user. I didn't even tick the checkbox "Allow service to interact with desktop" which you could if you need higher permissions.

Option to allow service to interact with desktop

Lastly I just want to say how funny it is that the top answer says quite the opposite of my answer and still both of us are right it's just how you interpret the question :-D. If you now say but you can't with the windows service project type - You CAN but I had this before and installation was sketchy and it was maybe kind of an unintentional hack until I found NSSM.


System.Diagnostics.Process.Start("Exe Name");


You can execute an .exe from a Windows service very well in Windows XP. I have done it myself in the past.

You need to make sure you had checked the option "Allow to interact with the Desktop" in the Windows service properties. If that is not done, it will not execute.

I need to check in Windows 7 or Vista as these versions requires additional security privileges so it may throw an error, but I am quite sure it can be achieved either directly or indirectly. For XP I am certain as I had done it myself.


First, we are going to create a Windows Service that runs under the System account. This service will be responsible for spawning an interactive process within the currently active User’s Session. This newly created process will display a UI and run with full admin rights. When the first User logs on to the computer, this service will be started and will be running in Session0; however the process that this service spawns will be running on the desktop of the currently logged on User. We will refer to this service as the LoaderService.

Next, the winlogon.exe process is responsible for managing User login and logout procedures. We know that every User who logs on to the computer will have a unique Session ID and a corresponding winlogon.exe process associated with their Session. Now, we mentioned above, the LoaderService runs under the System account. We also confirmed that each winlogon.exe process on the computer runs under the System account. Because the System account is the owner of both the LoaderService and the winlogon.exe processes, our LoaderService can copy the access token (and Session ID) of the winlogon.exe process and then call the Win32 API function CreateProcessAsUser to launch a process into the currently active Session of the logged on User. Since the Session ID located within the access token of the copied winlogon.exe process is greater than 0, we can launch an interactive process using that token.

Try this one. Subverting Vista UAC in Both 32 and 64 bit Architectures


This will never work, at least not under Windows Vista or later. The key problem is that you're trying to execute this from within a Windows Service, rather than a standard Windows application. The code you've shown will work perfectly in a Windows Forms, WPF, or Console application, but it won't work at all in a Windows Service.

Windows Services cannot start additional applications because they are not running in the context of any particular user. Unlike regular Windows applications, services are now run in an isolated session and are prohibited from interacting with a user or the desktop. This leaves no place for the application to be run.

More information is available in the answers to these related questions:

The best solution to your problem, as you've probably figured out by now, is to create a standard Windows application instead of a service. These are designed to be run by a particular user and are associated with that user's desktop. This way, you can run additional applications whenever you want, using the code that you've already shown.

Another possible solution, assuming that your Console application does not require an interface or output of any sort, is to instruct the process not to create a window. This will prevent Windows from blocking the creation of your process, because it will no longer request that a Console window be created. You can find the relevant code in this answer to a related question.


i have tried this article Code Project, it is working fine for me. I have used the code too. article is excellent in explanation with screenshot.

I am adding necessary explanation to this scenario

You have just booted up your computer and are about to log on. When you log on, the system assigns you a unique Session ID. In Windows Vista, the first User to log on to the computer is assigned a Session ID of 1 by the OS. The next User to log on will be assigned a Session ID of 2. And so on and so forth. You can view the Session ID assigned to each logged on User from the Users tab in Task Manager. enter image description here

But your windows service is brought under session ID of 0. This session is isolated from other sessions. This ultimately prevent the windows service to invoke the application running under user session's like 1 or 2.

In order to invoke the application from windows service you need to copy the control from winlogon.exe which acts as present logged user as shown in below screenshot. enter image description here

Important codes

// obtain the process id of the winlogon process that 
// is running within the currently active session
Process[] processes = Process.GetProcessesByName("winlogon");
foreach (Process p in processes)
{
    if ((uint)p.SessionId == dwSessionId)
    {
        winlogonPid = (uint)p.Id;
    }
}

// obtain a handle to the winlogon process
hProcess = OpenProcess(MAXIMUM_ALLOWED, false, winlogonPid);

// obtain a handle to the access token of the winlogon process
if (!OpenProcessToken(hProcess, TOKEN_DUPLICATE, ref hPToken))
{
    CloseHandle(hProcess);
    return false;
}

// Security attibute structure used in DuplicateTokenEx and   CreateProcessAsUser
// I would prefer to not have to use a security attribute variable and to just 
// simply pass null and inherit (by default) the security attributes
// of the existing token. However, in C# structures are value types and   therefore
// cannot be assigned the null value.
SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
sa.Length = Marshal.SizeOf(sa);

// copy the access token of the winlogon process; 
// the newly created token will be a primary token
if (!DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, ref sa, 
    (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, 
    (int)TOKEN_TYPE.TokenPrimary, ref hUserTokenDup))
    {
      CloseHandle(hProcess);
      CloseHandle(hPToken);
      return false;
    }

 STARTUPINFO si = new STARTUPINFO();
 si.cb = (int)Marshal.SizeOf(si);

// interactive window station parameter; basically this indicates 
// that the process created can display a GUI on the desktop
si.lpDesktop = @"winsta0\default";

// flags that specify the priority and creation method of the process
int dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;

// create a new process in the current User's logon session
 bool result = CreateProcessAsUser(hUserTokenDup,  // client's access token
                            null,             // file to execute
                            applicationName,  // command line
                            ref sa,           // pointer to process    SECURITY_ATTRIBUTES
                            ref sa,           // pointer to thread SECURITY_ATTRIBUTES
                            false,            // handles are not inheritable
                            dwCreationFlags,  // creation flags
                            IntPtr.Zero,      // pointer to new environment block 
                            null,             // name of current directory 
                            ref si,           // pointer to STARTUPINFO structure
                            out procInfo      // receives information about new process
                            );

Examples related to c#

How can I convert this one line of ActionScript to C#? Microsoft Advertising SDK doesn't deliverer ads How to use a global array in C#? How to correctly write async method? C# - insert values from file into two arrays Uploading into folder in FTP? Are these methods thread safe? dotnet ef not found in .NET Core 3 HTTP Error 500.30 - ANCM In-Process Start Failure Best way to "push" into C# array

Examples related to .net

You must add a reference to assembly 'netstandard, Version=2.0.0.0 How to use Bootstrap 4 in ASP.NET Core No authenticationScheme was specified, and there was no DefaultChallengeScheme found with default authentification and custom authorization .net Core 2.0 - Package was restored using .NetFramework 4.6.1 instead of target framework .netCore 2.0. The package may not be fully compatible Update .NET web service to use TLS 1.2 EF Core add-migration Build Failed What is the difference between .NET Core and .NET Standard Class Library project types? Visual Studio 2017 - Could not load file or assembly 'System.Runtime, Version=4.1.0.0' or one of its dependencies Nuget connection attempt failed "Unable to load the service index for source" Token based authentication in Web API without any user interface

Examples related to windows

"Permission Denied" trying to run Python on Windows 10 A fatal error occurred while creating a TLS client credential. The internal error state is 10013 How to install OpenJDK 11 on Windows? I can't install pyaudio on Windows? How to solve "error: Microsoft Visual C++ 14.0 is required."? git clone: Authentication failed for <URL> How to avoid the "Windows Defender SmartScreen prevented an unrecognized app from starting warning" XCOPY: Overwrite all without prompt in BATCH Laravel 5 show ErrorException file_put_contents failed to open stream: No such file or directory how to open Jupyter notebook in chrome on windows Tensorflow import error: No module named 'tensorflow'

Examples related to windows-services

Can't start Tomcat as Windows Service Error 1053 the service did not respond to the start or control request in a timely fashion How to solve "The specified service has been marked for deletion" error Service will not start: error 1067: the process terminated unexpectedly How to get all Windows service names starting with a common word? Windows service with timer Windows service on Local Computer started and then stopped error Windows service start failure: Cannot start service from the command line or debugger "Automatic" vs "Automatic (Delayed start)" How to install node.js as windows service?

Examples related to process.start

How can I run an EXE program from a Windows Service using C#? Process.start: how to get the output? How do I start a process from C#?