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.
You do not need a window handle to use CreateProcessWithLogonW, I'm not sure where your information came from.
The application failed to initialize error has many causes, but it is almost always related to security or exhausted user resources. It is extremely difficult to diagnose this without a lot more information about what you are running and the context that you're running in. But things to look into are: does the user provided have the correct permissions to access the executable file's directory, does the user have permission to access the window station and desktop in which it is being launched, does it have correct permissions on any dll files it needs to load at initialization, etc.
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.
I had similar issues, when I tried to start the PhantomJS-binary with the "runas"-verb within a windows service. I have now solved the Problem using the following procedure:
You can use the Impersonator-Class for impersonation. It is also important to set the following Properties in the ProcessStartInfo, so the application does not try to access the Windows UI:
You say that "The Windows Service is started using "administrator" credentials"
Do you mean the actual 'Administrator' account, or a user in the 'Administrators' group? Starting the service as Administrator solved this for me.
The reason that the call fails after the first time is very probably because it uses a "default" security descriptor (whatever that is).
from msdn:
I guess CreateProcessWithLogonW is creating this default security descriptor (in any case, I'm not specifying one).
Time to start Interopping...
Just a guess - are you using LoadUserProfile=true with the start info? CreateProcessWithLogonW does not load user registry hive by default, unless you tell it to.