using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace NaGet.InteropServices
{
///
/// Win32 API のCreateProcess
を直に叩くためのクラス。
///
///
/// .NETのProcess.Startは、CreateProcess(NULL, cmdLine, ...)
/// のように第一引数がNULL
に相当する呼び出しが、
/// *厳密な意味*でできない。厳密な意味で、
/// これと同じ呼び出しを実現する必要があるときに使われる。
///
public class CreateProcessCaller : IDisposable
{
#region Win32API
/*
[StructLayout(LayoutKind.Sequential)]
internal struct SECURITY_ATTRIBUTES
{
public int nLength;
public IntPtr lpSecurityDescriptor;
public int bInheritHandle;
}
*/
[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
struct STARTUPINFO
{
public Int32 cb;
string lpReserved;
string lpDesktop;
string lpTitle;
Int32 dwX;
Int32 dwY;
Int32 dwXSize;
Int32 dwYSize;
Int32 dwXCountChars;
Int32 dwYCountChars;
Int32 dwFillAttribute;
Int32 dwFlags;
Int16 wShowWindow;
Int16 cbReserved2;
IntPtr lpReserved2;
IntPtr hStdInput;
IntPtr hStdOutput;
IntPtr hStdError;
}
[DllImport("kernel32.dll", CharSet= CharSet.Auto, SetLastError=true)]
static extern bool CreateProcess(string lpApplicationName,
string lpCommandLine,
/* ref SECURITY_ATTRIBUTES lpProcessAttributes, */
IntPtr lpProcessAttributes,
/* ref SECURITY_ATTRIBUTES lpThreadAttributes, */
IntPtr lpThreadAttributes,
bool bInheritHandles,
uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory,
[In] ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
[DllImport("kernel32", SetLastError=true, ExactSpelling=true)]
static extern UInt32 WaitForSingleObject(IntPtr handle, UInt32 milliseconds);
[DllImport("kernel32.dll", SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetExitCodeProcess(IntPtr hProcess, out int lpExitCode);
#endregion
STARTUPINFO si;
PROCESS_INFORMATION pi;
///
/// プロセスを生成する
///
/// プロセス起動情報。
/// なお、procInfo.UseShellExecute
は必ずfalseでなければならない
public CreateProcessCaller(ProcessStartInfo procInfo)
{
if (procInfo.UseShellExecute) {
throw new ArgumentException("UseShellExecute must be false");
}
si.cb = Marshal.SizeOf(si);
string lpFileName = (string.IsNullOrEmpty(procInfo.FileName))? null : procInfo.FileName;
uint dwCreationFlags = 0x0020; // NORMAL_PRIORITY_CLASS
if (procInfo.CreateNoWindow) dwCreationFlags |= 0x08000000; // CREATE_NO_WINDOW
string lpCurrentDirectory = (System.IO.Directory.Exists(procInfo.WorkingDirectory))? procInfo.WorkingDirectory : null;
bool retValue = CreateProcess(lpFileName, procInfo.Arguments,
IntPtr.Zero, IntPtr.Zero,
false, dwCreationFlags,
IntPtr.Zero, lpCurrentDirectory, ref si, out pi);
if (! retValue) {
throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
}
CloseHandle(pi.hThread);
}
///
/// 関連付けられたプロセスが終了するまで、最大指定したミリ秒間待機。
///
/// 最大待機時間(ミリ秒単位)
/// 終了コード
public uint WaitForExit(uint timeout)
{
return WaitForSingleObject(pi.hProcess, timeout);
}
///
/// 関連付けられたプロセスが終了するまで無期限に待機。
///
/// 終了コード
public uint WaitForExit()
{
// return WaitForExit(INFINITE)
return WaitForExit(0xFFFFFFFF);
}
///
/// 終了コード
///
public int ExitCode {
get {
int lpExitCode;
if (! GetExitCodeProcess(pi.hProcess, out lpExitCode) ) {
throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
}
return lpExitCode;
}
}
///
/// プロセスのハンドルを開放する
///
public void Dispose()
{
CloseHandle(pi.hProcess);
}
}
}