Geeks With Blogs

WinToolZone - Spelunking Microsoft Technologies
I work as a developer on the Common Language Runtime (CLR) team, specifically in the areas of exception handling and CLR hosting.

The information in this weblog is provided "AS IS" with no warranties, and confers no rights. This weblog does not represent the thoughts, intentions, plans or strategies of my employer. It is solely my opinion. Inappropriate comments will be deleted at the authors discretion. All code samples are provided "AS IS" without warranty of any kind, either express or implied, including but not limited to the implied warranties of merchantability and/or fitness for a particular purpose.

Inside and Out... An attempt to understand technology better...
I returned this past weekend after a family function. It had me very busy and thus, no blogging for sometime for me :)

That said, today I was working with one of my friends over email - she was stuck about getting to launch a process in a specific user context, using managed code. Since the .NET Framework doesn't support such functionality out of the box, we tried different ways to do this:

1) LogonUser followed by WindowsIdentity impersonation using the obtained token - the token wasn't getting down to the spawned process even after we attempted Impersonate method of WindowsIdentity.
2) WMI also tried - it works but needs to be slightly modified when running on a local machine compared to a remote machine (we cannot specify credentials for local machine in the options).

Finally, we thought about working on CreateProcessWithLogonW. It turned out to be rather simple and clean way of achieving our goal. For those who are interested, the following snippet uses CreateProcessWithLogonW via PInvoke and launches a process under the specified user context:

The following are the PInvoke declarations and data types required:

		enum LogonFlags 
		LOGON_WITH_PROFILE         = 0x00000001,
		LOGON_NETCREDENTIALS_ONLY  = 0x00000002        

		enum CreationFlags 
		CREATE_SUSPENDED           = 0x00000004,
		CREATE_NEW_CONSOLE         = 0x00000010,
		CREATE_NEW_PROCESS_GROUP   = 0x00000200,
		CREATE_SEPARATE_WOW_VDM    = 0x00000800,

		struct ProcessInfo 
		public IntPtr hProcess; 
		public IntPtr hThread; 
		public uint dwProcessId; 
		public uint dwThreadId; 

	[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
		struct StartupInfo 
		public int     cb; 
		public string  reserved1; 
		public string  desktop; 
		public string  title; 
		public uint    dwX; 
		public uint    dwY; 
		public uint    dwXSize; 
		public uint    dwYSize; 
		public uint    dwXCountChars; 
		public uint    dwYCountChars; 
		public uint    dwFillAttribute; 
		public uint    dwFlags; 
		public ushort  wShowWindow; 
		public short   reserved2; 
		public int     reserved3; 
		public IntPtr  hStdInput; 
		public IntPtr  hStdOutput; 
		public IntPtr  hStdError; 

	[DllImport("advapi32.dll", CharSet=CharSet.Unicode, ExactSpelling=true, SetLastError=true)]
	static extern bool CreateProcessWithLogonW(
		string principal,
		string authority,
		string password,
		LogonFlags logonFlags,
		string appName,
		string cmdLine,
		CreationFlags creationFlags,
		IntPtr environmentBlock,
		string currentDirectory,
		ref StartupInfo startupInfo,
		out ProcessInfo processInfo);

	static extern bool CloseHandle(IntPtr h);

The following is the code snippet that does the PInvoke and spawns the process under a specific user context:

                StartupInfo si = new StartupInfo();
		si.cb = Marshal.SizeOf(typeof(StartupInfo));
		si.title = "This is impersonated command prompt";

		ProcessInfo pi = new ProcessInfo();
		string app = Path.Combine(Environment.SystemDirectory, "cmd.exe");

			if (CreateProcessWithLogonW("username", "domain", "password",
			app, null,
			0, IntPtr.Zero, null,
			ref si, out pi)) 
		else Console.WriteLine("Error code: {0}", Marshal.GetLastWin32Error());
Posted on Wednesday, February 9, 2005 9:38 AM .NET Framework , .NET Framework V1.X , VS.NET 2002/2003 , VS 2005 , 32bit | Back to top

Comments on this post: Managed CreateProcessWithLogonW

# re: Managed CreateProcessWithLogonW
Requesting Gravatar...
>>>Since the .NET Framework doesn't support such functionality out of the box...

Whidbey does.
Left by Anonymous on Feb 09, 2005 11:50 PM

# re: Managed CreateProcessWithLogonW
Requesting Gravatar...
True - but for .NET Framework apps before Whidbey, this is the way to go.
Left by Gaurav Khanna on Feb 10, 2005 10:41 AM

# re: Managed CreateProcessWithLogonW
Requesting Gravatar...
I get "failed to initialize" when trying to start a process from a windows service. I am not using the system account. Should I specify anything special in the startup info?
Left by Henrik on Jul 13, 2005 9:47 PM

# re: Managed CreateProcessWithLogonW
Requesting Gravatar...
I get the same "failed to initialize" 0xc0000142 error. I can make it go away by impersonating an administrator -- but any non-admin user tends to crash this way. It seems to have something to do with the "windows stations" and "desktops" -- these are secured by the OS. I'm trying to spawn a no-GUI process, but it still needs to inherit and/or switch to one of these desktops. WMI doesn't seem to have this particular problem, but it has enough of its own that it hasn't worked either.
Left by gat on Sep 12, 2005 7:05 PM

# re: Managed CreateProcessWithLogonW
Requesting Gravatar...
When it fails
new Win32Exception(Marshal.GetLastWin32Error()) gives a much more meaningful information than simply
Left by al. h. on Dec 19, 2005 4:02 AM

# Managed code impersonation: CLR ThreadPool and more...
Requesting Gravatar...

Under managed code, once you impersonate your application's thread using LogonUser and WindowsIdentity...
Left by Inside and Out... on Jul 10, 2006 8:38 PM

Your comment:
 (will show your gravatar)

Copyright © Gaurav Khanna | Powered by: