I've been grappling with some pretty crappy code lately, as I try to rescue a project from another coder's gross mismanagement. Features are poorly implemented or not implemented at all, there is code duplication everywhere, the whole thing makes me want to scream bloody murder and take an axe to it.
One big problem was integrating with a third-party COM object that just plain didn't work under ASP.NET. The component creator had no advice on the matter, so I took things into my own hands and wrote a console application that wrapped the object's core functions. Then came the pain: how do I invoke a process from C# that runs as a given user (Network Service won't cut it) and doesn't show a window?
Here's the result of a ton of Google searches and the pulling out of much hair. It uses the Windows API call "CreateProcessWithLogonW" (can anybody explain why windows API coders use such arcane nomenclature?), and sets flags to keep the process hidden. (The real gem was finding the @"winsta0\default" line -- without this your process cannot execute from ASP.NET. Why? Hard to say from merely Google. Perhaps if I bought a book.) Feel free to incorporate this into your own code, most of it came from
this handy site.
UPDATE: I spoke too soon. This ONLY works if the user I log in with is in the Administrators group -- not good! I'm going to take a completely different tack. If anybody knows what I'm missing to make this work with a non-admin, chime in in the comments.
Even if it's five years from the posting date, I'd love to know what I did wrong!Show CodeHide
using System;
using System.Runtime.InteropServices;
namespace ProcessLauncher
{
public class ProcLauncher
{
private const UInt32 Infinite = 0xffffffff;
private const Int32 Startf_UseStdHandles = 4; // 00000100 (binary)
private const Int32 Startf_UseShowWindow = 1; // 00000001 (binary)
private const Int32 StdOutputHandle = -11;
private const Int32 StdErrorHandle = -12;
#region WinAPI Calls
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
private struct StartupInfo
{
public int cb;
public String reserved;
public String desktop;
public String title;
public int x;
public int y;
public int xSize;
public int ySize;
public int xCountChars;
public int yCountChars;
public int fillAttribute;
public int flags;
public UInt16 showWindow;
public UInt16 reserved2;
public byte reserved3;
public IntPtr stdInput;
public IntPtr stdOutput;
public IntPtr stdError;
}
private struct ProcessInformation
{
public IntPtr process;
public IntPtr thread;
public int processId;
public int threadId;
}
[DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
private static extern bool CreateProcessWithLogonW(
String userName,
String domain,
String password,
UInt32 logonFlags,
String applicationName,
String commandLine,
UInt32 creationFlags,
UInt32 environment,
String currentDirectory,
ref StartupInfo startupInfo,
out ProcessInformation processInformation);
[DllImport("kernel32.dll", SetLastError=true)]
private static extern bool GetExitCodeProcess(IntPtr process, ref UInt32 exitCode);
[DllImport("Kernel32.dll", SetLastError=true)]
private static extern UInt32 WaitForSingleObject(IntPtr handle, UInt32 milliseconds);
[DllImport("Kernel32.dll", SetLastError=true)]
private static extern IntPtr GetStdHandle(IntPtr handle);
[DllImport("Kernel32.dll", SetLastError=true)]
private static extern bool CloseHandle(IntPtr handle);
#endregion
public static UInt32 RunProcess(string username, string domain, string password, string appPath, string[] arguments)
{
for (int i = 0; i < arguments.Length; i++)
{
arguments[i] = "\"" + arguments[i].Trim().Replace("\"", "\"\"") + "\"";
}
StartupInfo startupInfo = new StartupInfo();
startupInfo.reserved = null;
startupInfo.desktop = @"winsta0\default";
startupInfo.flags = (Startf_UseStdHandles | Startf_UseShowWindow);
startupInfo.stdOutput = (IntPtr)StdOutputHandle;
startupInfo.stdError = (IntPtr)StdErrorHandle;
startupInfo.showWindow = 0;
UInt32 exitCode = 123456;
ProcessInformation processInfo = new ProcessInformation();
string currentDirectory = System.IO.Path.GetDirectoryName(appPath);
try
{
CreateProcessWithLogonW(
username,
domain,
password,
(UInt32) 1,
appPath,
"\"" + appPath + "\" " + string.Join(" ", arguments),
(UInt32) 0,
(UInt32) 0,
currentDirectory,
ref startupInfo,
out processInfo);
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine(e.ToString());
}
WaitForSingleObject(processInfo.process, Infinite);
GetExitCodeProcess(processInfo.process, ref exitCode);
CloseHandle(processInfo.process);
CloseHandle(processInfo.thread);
return exitCode;
}
}
}
0 comments:
Post a Comment