I'd like to periodically run an arbitrary .NET exe under a specified user account from a Windows Service.
So far I've got my windows service running with logic to decide what the target process is, and when to run it.
The target process is started in the following manner:
- The Windows Service is started using "administrator" credentials.
- When the time comes, an intermediate .NET process is executed with arguments detailing which process should be started (filename, username, domain, password).
- This process creates a new System.Diagnostics.Process, associates a ProcessStartInfo object filled with the arguments passed to it, and then calls Start() on the process object.
The first time this happens, the target process executes fine and then closes normally. Every subsequent time however, as soon as the target process is started it throws the error "Application failed to initalize properly (0xc0000142)". Restarting the Windows Service will allow the process to run successfully once again (for the first execution).
Naturally, the goal is to have target process execute successfully every time.
Regarding step 2 above: To run a process as a different user .NET calls the win32 function CreateProcessWithLogonW. This function requires a window handle to log the specified user in. Since the Windows Service isn't running in Interactive Mode it has no window handle. This intermediate process solves the issue, as it has a window handle which can be passed to the target process.
Please, no suggestions of using psexec or the windows task planner. I've accepted my lot in life, and that includes solving the problem in the manner stated above.
Best Answer
I seem to have a working implementation (Works On My Machine(TM)) for the following scenarios:
Batch File, .NET Console Assembly, .NET Windows Forms application.
Here's how:
I have a windows service running as the Administrator user. I add the following policies to the Administrator user:
These policies can be added by opening Control Panel/ Administrative Tools / Local Security Policy / User Rights Assignment. Once they are set, the policies don't take effect until next login. You can use another user instead of the Administrator, which might make things a bit safer :)
Now, my windows service has the required permissions to start jobs as other users. When a job needs to be started the service executes a seperate assembly ("Starter" .NET console assembly) which initiates the process for me.
The following code, located in the windows service, executes my "Starter" console assembly:
The console assembly then starts the target process via interop calls:
The basic procedure is:
This is development code fresh from my machine and no way near ready for use in production environments. The code here is still buggy - For starters: I'm not sure whether the handles are closed at the right point, and there's a few interop functions defined above that aren't required. The separate starter process also really annoys me. Ideally I'd like all this Job stuff wrapped up in an assembly for use from our API as well as this service. If someone has any suggestions here, they'd be appreciated.