A couple of days ago a friend of mine called with a problem. She had a Windows service
written in C# that, under certain conditions, needed to launch some applications on behalf
of two specific users and she was getting one error after another. Oddly enough, I had just
answered a question on the same subject over at Stack Overflow and I had seen a few other
references to similar problems in other forums (usually with incorrect answers).
This isn't a common need but it also isn't that rare, so I thought I had better post this
step by step guide to troubleshooting the launching of a process under impersonated credentials.
This is based on using the Start method of the .Net Process class but it is also applicable to
the underlying API calls: CreateProcessWithLogonW and CreateProcessWithTokenW.
Access Denied - The first attempt and an access denied exception right
off the bat. This is the most common initial problem and is caused by the fact that the service
is running under the LOCAL SYSTEM account. Strangely, the SYSTEM account is the most powerful
account on the computer but one of the few things it cannot do is launch a process using
CreateProcessWithLogonW which is the API underlying the call to Process.Start. So change your
service account to Local Service, it's probably the more appropriate account anyway.
Access Denied Again - Aargh, I thought we solved this. Oops, double
check the permissions on the application that you are trying to launch. Remember that the system
tries to access the application file as the user account that the process will be running under,
not the service account.
Invalid Directory Error - What? All the paths are correct. All the
directories are spelled correctly, no invalid characters. This is an incredibly annoying error
and is not very consistent. Usually when we run a process we don't bother setting the WorkingDirectory
property and just accept the default from the parent process. When starting a process with new
credentials you can't do that, you must explicitly set a path for the WorkingDirectory or you'll
get a "The directory name is invalid." Win32Exception.
Failure: No Error? - Process.Start handles the creation of the Environment
block for the new process for you quite well. So this is a problem only if you are using the underlying
API. When calling one of the CreateProcess* APIs it is normal to leave the lpEnvironment parameter
as NULL and have the system use the default of copying the block from the parent process. But when
launching under new credentials you must create an environment block explicitly, either manually or
using CreateEnvironmentBlock. What makes this worse is, if you leave this out the CreateProcess* call
will fail but GetLastError will return ERROR_SUCCESS and if you make an error creating your environment
block there will be no error but the process may just not run at all.
Application Failed to Initialize Properly - No more exceptions, you've
solved all the problems and the process has been launched. Oops again, where is the process? Check
the event log (or you may have received an Application Error pop-up). There should be an entry for
Application Error that says that your process was the faulting application, either user32.dll or
kernel32.dll was the faulting module and the exception was: 0xC0000142. There may be some minor
variation in this but basically it is saying that your application could not initialize. The reason
for this is that on initialization, before any application code is run, all processes are attached
to a Window Station and all threads are attached to a Desktop but the user you are launching under
does not have permission to access the Window Station and Desktop in which your process is being
launched, ergo it can't initialize. The security descriptors for the Window Station and Desktop
must be adjusted to give AllAccess permission to the user the process is being launched under. This
is a devil to do directly in .Net, so you might find the security wrapper classes
here useful.
No More Errors - Really, no more errors, your process should be running
smoothly now. There may be some variations in what you need to do based on who the user is (for
instance an administrator will already have the correct permissions in some cases) or what kind of
session you are launching in. But following these steps should make your life smooth and easy (well
maybe not your whole life).
Source code (C#) supporting this article can be found
here.