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); } } }