Reputation: 67
Here is the situation : I have a C# .NET Windows service (run as localsystem) that starts a satellite C# .NET executable (windowless WinForms) under the current logged in user account, using CreateProcessAsUser.
Sometimes it works, sometimes... it doesn't. I can see the exe showing up in the tasks manager for just a second, and disappearing. The main method of the exe does not get hit, so I think the problem comes from CreateProcessAsUser.
The event viewer shows an Application Error with code 0xc06d007e, which is of no help. There is also a Windows Error Reporting that says to look at dump files, but those are missing from the path being given.
I have no idea how to debug that. If at least I could see a real error.
It's completely random.
EDIT:
Here is some code as asked.
public static uint? LaunchAsCurrentUser(string cmdLine)
{
IntPtr token = GetCurrentUserToken();
if (token == IntPtr.Zero)
return null;
IntPtr envBlock = GetEnvironmentBlock(token);
uint? processId = LaunchProcessAsUser(cmdLine, token, envBlock);
if (envBlock != IntPtr.Zero)
DestroyEnvironmentBlock(envBlock);
CloseHandle(token);
return processId;
}
private static uint LaunchProcessAsUser(string cmdLine, IntPtr token, IntPtr envBlock)
{
PROCESS_INFORMATION processInformation = new PROCESS_INFORMATION();
SECURITY_ATTRIBUTES saProcess = new SECURITY_ATTRIBUTES();
SECURITY_ATTRIBUTES saThread = new SECURITY_ATTRIBUTES();
saProcess.nLength = (uint)Marshal.SizeOf(saProcess);
saThread.nLength = (uint)Marshal.SizeOf(saThread);
STARTUPINFO si = new STARTUPINFO();
si.cb = (uint)Marshal.SizeOf(si);
//if this member is NULL, the new process inherits the desktop
//and window station of its parent process. If this member is
//an empty string, the process does not inherit the desktop and
//window station of its parent process; instead, the system
//determines if a new desktop and window station need to be created.
//If the impersonated user already has a desktop, the system uses the
//existing desktop.
si.lpDesktop = string.Empty; //Modify as needed
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_FORCEONFEEDBACK;
si.wShowWindow = SW_SHOW;
//Set other si properties as required.
bool result = CreateProcessAsUser(
token,
null,
cmdLine,
ref saProcess,
ref saThread,
false,
CREATE_UNICODE_ENVIRONMENT,
envBlock,
null,
ref si,
out processInformation);
if (!result)
{
int error = Marshal.GetLastWin32Error();
string message = String.Format("CreateProcessAsUser Error: {0}", error);
throw new ApplicationException(message);
}
return processInformation.dwProcessId;
}
private static IntPtr GetEnvironmentBlock(IntPtr token)
{
IntPtr envBlock = IntPtr.Zero;
bool retVal = CreateEnvironmentBlock(ref envBlock, token, false);
if (retVal == false)
{
// Environment Block, things like common paths to My Documents etc.
// Will not be created if "false"
// It should not adversley affect CreateProcessAsUser.
string message = String.Format("CreateEnvironmentBlock Error: {0}", Marshal.GetLastWin32Error());
throw new ApplicationException(message);
}
return envBlock;
}
private static IntPtr GetCurrentUserToken()
{
IntPtr currentToken = IntPtr.Zero;
IntPtr primaryToken = IntPtr.Zero;
IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;
int dwSessionId = 0;
IntPtr hUserToken = IntPtr.Zero;
IntPtr hTokenDup = IntPtr.Zero;
IntPtr pSessionInfo = IntPtr.Zero;
int dwCount = 0;
WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, ref pSessionInfo, ref dwCount);
Int32 dataSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));
Int64 current = (long)pSessionInfo;
for (int i = 0; i < dwCount; i++)
{
WTS_SESSION_INFO si =
(WTS_SESSION_INFO)Marshal.PtrToStructure((IntPtr)current, typeof(WTS_SESSION_INFO));
if (WTS_CONNECTSTATE_CLASS.WTSActive == si.State)
{
dwSessionId = si.SessionID;
break;
}
current += dataSize;
}
WTSFreeMemory(pSessionInfo);
bool bRet = WTSQueryUserToken(dwSessionId, out currentToken);
if (bRet == false)
return IntPtr.Zero;
bRet = DuplicateTokenEx(currentToken, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS, IntPtr.Zero, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenPrimary, out primaryToken);
if (bRet == false)
return IntPtr.Zero;
return primaryToken;
}
Upvotes: 1
Views: 759
Reputation: 67
Just needed to change this:
si.lpDesktop = string.Empty;
To this:
si.lpDesktop = null;
...so the new process inherits its parent process desktop. Now it works flawlessly.
Upvotes: 1