private static Process CreateProcessInJob(string ExeFileName, string CommandLine, SafeWaitHandle Job, bool BreakawayOk = false)
{
PROCESS_INFORMATION ProcessInfo;
STARTUPINFO StartupInfo;
bool Succeeded = false;
ProcessInfo.hProcess = IntPtr.Zero;
ProcessInfo.hThread = IntPtr.Zero;
StartupInfo.cb = 0;
StartupInfo.lpReserved = null;
StartupInfo.lpDesktop = null;
StartupInfo.lpTitle = null;
StartupInfo.dwX = 0;
StartupInfo.dwY = 0;
StartupInfo.dwXSize = 0;
StartupInfo.dwYSize = 0;
StartupInfo.dwXCountChars = 0;
StartupInfo.dwYCountChars = 0;
StartupInfo.dwFillAttribute = 0;
StartupInfo.dwFlags = (UInt32)STARTFLAGS.UseShowWindow;
StartupInfo.wShowWindow = (UInt16)SHOWCMD.SW_HIDE;
StartupInfo.cbReserved2 = 0;
StartupInfo.lpReserved2 = IntPtr.Zero;
StartupInfo.hStdInput = IntPtr.Zero;
StartupInfo.hStdOutput = IntPtr.Zero;
StartupInfo.hStdError = IntPtr.Zero;
StartupInfo.cb = Marshal.SizeOf(StartupInfo);
try
{
string RealCommandLine = String.Format("\"{0}\" {1}", ExeFileName, CommandLine);
bool BreakawayTried = false;
//
// Create the process.
//
for (; ; )
{
if (CreateProcessA(ExeFileName,
CommandLine,
IntPtr.Zero,
IntPtr.Zero,
0,
PROCESSCREATIONFLAGS.CreateSuspended | (BreakawayOk && !BreakawayTried ? PROCESSCREATIONFLAGS.CreateBreakawayFromJob : 0),
IntPtr.Zero,
null,
ref StartupInfo,
out ProcessInfo) == 0)
{
int LastError = Marshal.GetLastWin32Error();
ProcessInfo.hProcess = IntPtr.Zero;
ProcessInfo.hThread = IntPtr.Zero;
if ((LastError == ERROR_ACCESS_DENIED) && (BreakawayOk) && (!BreakawayTried))
{
Logger.Log("DatabaseConnector.CreateProcessInJob: Failed to create process with breakaway due to ERROR_ACCESS_DENIED, trying again without breakaway.");
BreakawayTried = true;
continue;
}
throw new ApplicationException(String.Format("CreateProcessA(ExeFileName = '{0}', CommandLine = '{1}') failed: {2}",
ExeFileName,
CommandLine,
LastError));
}
break;
}
//
// Join it to the job so that it will be cleaned up if the
// nwn2server process exits unexpectedly.
//
if (AssignProcessToJobObject(Job.DangerousGetHandle(), ProcessInfo.hProcess) == 0)
{
int LastError = Marshal.GetLastWin32Error();
bool ContinueAnyway = false;
if (LastError == ERROR_ACCESS_DENIED)
{
if (BreakawayOk == false)
{
Logger.Log("DatabaseConnector.CreateProcessInJob: Failed to assign process to job due to ERROR_ACCESS_DENIED, trying with breakaway.");
return CreateProcessInJob(ExeFileName, CommandLine, Job, true);
}
else
{
Logger.Log("DatabaseConnector.CreateProcessInJob: Failed to assign process to job due to ERROR_ACCESS_DENIED and breakaway failed, ignoring failure.");
ContinueAnyway = true;
}
}
if (!ContinueAnyway)
throw new ApplicationException("AssignProcessToJobObject failed: " + LastError);
}
//
// Attach a Process object to the process by ID. Since a
// handle to the process is currently open, the process ID is
// guaranteed to not be recycled in this interval. The .NET
// Process object will be used to wait on the process to exit
// in the monitor thread.
//
Process ProcessObject = Process.GetProcessById((int)ProcessInfo.dwProcessId);
//
// Finally, resume the thread and call it done.
//
if (ResumeThread(ProcessInfo.hThread) == UInt32.MaxValue)
throw new ApplicationException("ResumeThread failed: " + Marshal.GetLastWin32Error());
Succeeded = true;
return ProcessObject;
}
finally
{
if (ProcessInfo.hProcess != IntPtr.Zero)
{
if (!Succeeded)
TerminateProcess(ProcessInfo.hProcess, 0);
CloseHandle(ProcessInfo.hProcess);
ProcessInfo.hProcess = IntPtr.Zero;
}
if (ProcessInfo.hThread != IntPtr.Zero)
{
CloseHandle(ProcessInfo.hThread);
ProcessInfo.hThread = IntPtr.Zero;
}
}
}