views:

3732

answers:

6

I have a Windows Service developed in C# and .NET 3.5 to perform various administrative tasks throughout our 3 domains. I've got an admin account in each domain that has the necessary rights/permissions for what this service is doing.

For all the AD interactions, I can bind to AD using the correct username/password for the domain and all is good. However, I have a need now to launch an external process (robocopy) to perform some work and I cannot find any code examples anywhere for doing this in .NET 3.5.

What I need to do is launch robocopy using alternate credentials in a hidden window, capture the standard output so I have a log of what was actually done, and capture the exit code so I can tell if it was successful.

I found some old .NET 1.1 code from K. Scott Allen's blog (see the last few comments for a good code listing) that extends the System.Diagnostics.Process object and calls the Win32 API function CreateProcessAsUserW. However, the code doesn't work due to some API changes in .NET 2.0. Using the code snippet from the last comment on the referenced blog where it is using reflection to call the private SetProcessHandle function of the Process object causes Access Denied errors when I try to interact with the process (to kill it or get exit code).

Does anyone have a good example of how to accomplish this?

EDIT: The built-in .NET Process and ProcessStartInfo API does allow you to specify alternate credentials but when you do, it wants to create a visible window (even if you specify CreateNoWindow or WindowStyle Hidden) which fails when running as a Windows Service. So those are not an option for this application. Impersonation doesn't work either since when the external process is launched, it uses the credentials of the parent process, not the impersonated credentials.

SOLUTION: As Reed pointed out below, P/Invoke using one of the Win32 API functions should allow you to make this happen. I ended up going a slightly different route using an old freeware NT4 utility called RunProcess that allows you to specify a username/password on the command line. It worked on Vista and 2k3 and also worked correctly when launched by a windows service running as Local System.

+2  A: 

I think you want to look at:

ProcessStartInfo

That provides a .NET only way to start a process under a different user with different credentials. No more need for the Win32 API.

--- EDIT ---

Other alternatives might be to use the P/Invoke as specified here (look at the note at the bottom first) or more likely, using P/Invoke with CreateProcessWithLogonW.

Reed Copsey
Trouble with ProcessStartInfo is that when you specify credentials, it wants to create a visible window which fails when running as a service.
David Archer
OK, this issue might void my answer more or less... :)
Mudu
David: I added 2 other possibilities.
Reed Copsey
@Reed: Falling back on the Win32 API using P/Invoke would work so I'll accept this as the answer. I ended up going a slightly different route as noted in the question.
David Archer
David: Nice find on the RunProcess utility.
Reed Copsey
A: 

This should work well with the Process class and ProcessStartInfo.

Different credentials:

Redirect standard output:

Exit code:

I hope this helps.

Matthias

Mudu
A: 

StartInfo has a property called CreateNoWindow that may make Reed's suggestion work for you.

Chris Holmes
From the MSDN docs for CreateNoWindow: If the UserName and Password properties are not a null reference (Nothing in Visual Basic), the CreateNoWindow property value is ignored and a new window is created.http://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo.createnowindow.aspx
David Archer
You are providing a username and password though, are you not? You said you were supplying alternate user credentials. Maybe I missed something.
Chris Holmes
Yes, I am supplying alternate credentials. The docs say if you supply credentials it ignores the CreateNoWindow property.
David Archer
+1  A: 

Just an unverified thought, but isn't using dynamic Impersonate valid for the entire code block in .net 2+? So Impersonate the required user and then do whatever you need within the impersonated context/scope, like starting a process the normal way? Think we did something like that to integrate mstsc.exe launching using already supplied credentials.

Oskar Duveborn
Unfortunately, when you launch an external process, it uses the credentials that the parent process started with, not the impersonated credentials. (Hope that makes sense) :)
David Archer
Ah well, as I recall making it work within the impersonationcontext with mstsc was possible... but, it might have been more complicated than I recall - have to recheck the implementation, after the weekend beer though ;)
Oskar Duveborn
A: 

This has been a PITA since early XP with CreateProcess. Sounds like all MSFT did was document the bug. Why should CreateNoWindows be ignored if you supply a userid and password???

A: 

Hi, If you want to see those exit codes actually used, have a look at www.robocopyplus.com. it acts like a wrapper around robocopy to add swicthes for email notification and report filtering. This may already do some of the stuff you are trying to achieve.