2 using System.Diagnostics;
\r
3 using System.Runtime.InteropServices;
\r
5 namespace NaGet.InteropServices
\r
8 /// Win32 API の<code>CreateProcess</code>を直に叩くためのクラス。
\r
11 /// .NETのProcess.Startは、<code>CreateProcess(NULL, cmdLine, ...)</code>
\r
12 /// のように第一引数が<code>NULL</code>に相当する呼び出しが、
\r
13 /// *厳密な意味*でできない。厳密な意味で、
\r
14 /// これと同じ呼び出しを実現する必要があるときに使われる。
\r
16 public class CreateProcessCaller : IDisposable
\r
21 [StructLayout(LayoutKind.Sequential)]
\r
22 internal struct SECURITY_ATTRIBUTES
\r
25 public IntPtr lpSecurityDescriptor;
\r
26 public int bInheritHandle;
\r
30 [StructLayout(LayoutKind.Sequential)]
\r
31 internal struct PROCESS_INFORMATION
\r
33 public IntPtr hProcess;
\r
34 public IntPtr hThread;
\r
35 public int dwProcessId;
\r
36 public int dwThreadId;
\r
39 [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
\r
50 Int32 dwXCountChars;
\r
51 Int32 dwYCountChars;
\r
52 Int32 dwFillAttribute;
\r
62 [DllImport("kernel32.dll", CharSet= CharSet.Auto, SetLastError=true)]
\r
63 static extern bool CreateProcess(string lpApplicationName,
\r
64 string lpCommandLine,
\r
65 /* ref SECURITY_ATTRIBUTES lpProcessAttributes, */
\r
66 IntPtr lpProcessAttributes,
\r
67 /* ref SECURITY_ATTRIBUTES lpThreadAttributes, */
\r
68 IntPtr lpThreadAttributes,
\r
69 bool bInheritHandles,
\r
70 uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory,
\r
71 [In] ref STARTUPINFO lpStartupInfo,
\r
72 out PROCESS_INFORMATION lpProcessInformation);
\r
74 [DllImport("kernel32", SetLastError=true, ExactSpelling=true)]
\r
75 static extern UInt32 WaitForSingleObject(IntPtr handle, UInt32 milliseconds);
\r
77 [DllImport("kernel32.dll", SetLastError=true)]
\r
78 [return: MarshalAs(UnmanagedType.Bool)]
\r
79 static extern bool CloseHandle(IntPtr hObject);
\r
81 [DllImport("kernel32.dll", SetLastError = true)]
\r
82 [return: MarshalAs(UnmanagedType.Bool)]
\r
83 static extern bool GetExitCodeProcess(IntPtr hProcess, out int lpExitCode);
\r
87 [DllImport("kernel32.dll")]
\r
88 static extern uint GetPriorityClass(IntPtr hProcess);
\r
90 [DllImport("kernel32.dll")]
\r
91 static extern bool SetPriorityClass(IntPtr hProcess, uint dwPriorityClass);
\r
97 [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError=true)]
\r
98 [return: MarshalAs(UnmanagedType.Bool)]
\r
99 static extern bool CreateProcessWithTokenW(
\r
102 string lpApplicationName, string lpCommandLine,
\r
103 uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory,
\r
104 [In] ref STARTUPINFO lpStartupInfo,
\r
105 out PROCESS_INFORMATION lpProcessInfo);
\r
107 // For Windows Mobile, replace user32.dll with coredll.dll
\r
108 [DllImport("user32.dll", SetLastError = true)]
\r
109 static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
\r
111 [DllImport("user32.dll", SetLastError=true)]
\r
112 static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
\r
114 [DllImport("advapi32.dll", SetLastError=true)]
\r
115 [return: MarshalAs(UnmanagedType.Bool)]
\r
116 static extern bool OpenProcessToken(IntPtr ProcessHandle, uint DesiredAccess, out IntPtr TokenHandle);
\r
118 [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
\r
119 extern static bool DuplicateTokenEx(
\r
120 IntPtr hExistingToken,
\r
121 uint dwDesiredAccess,
\r
122 /* ref SECURITY_ATTRIBUTES lpTokenAttributes, */
\r
123 IntPtr lpTokenAttributes,
\r
124 /* SECURITY_IMPERSONATION_LEVEL ImpersonationLevel, */
\r
125 uint ImpersonationLevel,
\r
126 /* TOKEN_TYPE TokenType, */
\r
128 out IntPtr phNewToken );
\r
130 [DllImport("kernel32.dll")]
\r
131 static extern IntPtr GetEnvironmentStrings();
\r
133 [DllImport("kernel32.dll")]
\r
134 static extern bool FreeEnvironmentStrings(IntPtr lpszEnvironmentBlock);
\r
141 PROCESS_INFORMATION pi;
\r
146 /// <param name="procInfo">プロセス起動情報。
\r
147 /// なお、<code>procInfo.UseShellExecute</code>は必ずfalseでなければならない</param>
\r
148 public CreateProcessCaller(ProcessStartInfo procInfo)
\r
149 : this(procInfo, false)
\r
153 public CreateProcessCaller(ProcessStartInfo procInfo, bool runAsNormalUser)
\r
155 if (procInfo.UseShellExecute) {
\r
156 throw new ArgumentException("UseShellExecute must be false");
\r
158 si.cb = Marshal.SizeOf(si);
\r
160 string lpFileName = (string.IsNullOrEmpty(procInfo.FileName))? null : procInfo.FileName;
\r
162 uint dwCreationFlags = 0x0020; // NORMAL_PRIORITY_CLASS
\r
163 if (procInfo.CreateNoWindow) dwCreationFlags |= 0x08000000; // CREATE_NO_WINDOW
\r
164 string lpCurrentDirectory = (System.IO.Directory.Exists(procInfo.WorkingDirectory))? procInfo.WorkingDirectory : null;
\r
167 if (runAsNormalUser && NaGet.Utils.IsAdministrators()) {
\r
168 retValue = _CreateProcessAsNormalUser(lpFileName, procInfo.Arguments,
\r
169 IntPtr.Zero, IntPtr.Zero,
\r
170 false, dwCreationFlags,
\r
171 IntPtr.Zero, lpCurrentDirectory, ref si, out pi);
\r
173 retValue = CreateProcess(lpFileName, procInfo.Arguments,
\r
174 IntPtr.Zero, IntPtr.Zero,
\r
175 false, dwCreationFlags,
\r
176 IntPtr.Zero, lpCurrentDirectory, ref si, out pi);
\r
180 throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
\r
182 if (pi.hThread != IntPtr.Zero) {
\r
183 CloseHandle(pi.hThread);
\r
187 private bool _CreateProcessAsNormalUser(string lpApplicationName,
\r
188 string lpCommandLine,
\r
189 /* ref SECURITY_ATTRIBUTES lpProcessAttributes, */
\r
190 IntPtr lpProcessAttributes,
\r
191 /* ref SECURITY_ATTRIBUTES lpThreadAttributes, */
\r
192 IntPtr lpThreadAttributes,
\r
193 bool bInheritHandles,
\r
194 uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory,
\r
195 [In] ref STARTUPINFO lpStartupInfo,
\r
196 out PROCESS_INFORMATION lpProcessInformation)
\r
199 * cf. nsWindowsRestart.cpp#LaunchAsNormalUser
\r
204 lpProcessInformation = new PROCESS_INFORMATION(); /* fake */
\r
206 IntPtr hwndShell = FindWindow("Progman", null);
\r
208 GetWindowThreadProcessId(hwndShell, out dwProcessId);
\r
209 Process procShell = Process.GetProcessById((int) dwProcessId);
\r
210 if (procShell == null) {
\r
214 IntPtr hTokenHandle, hNewToken;
\r
215 // bool ok = OpenProcessToken(hProcessShell, MAXIMUM_ALLOWED, out hTokenHandle);
\r
216 bool ok = OpenProcessToken(procShell.Handle, 0x02000000, out hTokenHandle);
\r
217 if (!ok) return false;
\r
218 // ok = DuplicateTokenEx(hTokenShell, MAXIMUM_ALLOWED, null, SecurityDelegation, TokenPrimary, out hNewToken);
\r
219 ok = DuplicateTokenEx(hTokenHandle, 0x02000000, IntPtr.Zero, 3, 1, out hNewToken);
\r
220 CloseHandle(hTokenHandle);
\r
221 if (!ok) return false;
\r
223 IntPtr myenv = GetEnvironmentStrings();
\r
224 ok = CreateProcessWithTokenW(hNewToken,
\r
225 0, // profile is already loaded
\r
230 lpCurrentDirectory,
\r
232 out lpProcessInformation);
\r
233 if (myenv != IntPtr.Zero) {
\r
234 FreeEnvironmentStrings(myenv);
\r
236 CloseHandle(hNewToken);
\r
245 /// 関連付けられたプロセスが終了するまで、最大指定したミリ秒間待機。
\r
247 /// <param name="timeout">最大待機時間(ミリ秒単位)</param>
\r
248 /// <returns>終了コード</returns>
\r
249 public uint WaitForExit(uint timeout)
\r
251 return WaitForSingleObject(pi.hProcess, timeout);
\r
255 /// 関連付けられたプロセスが終了するまで無期限に待機。
\r
257 /// <returns>終了コード</returns>
\r
258 public uint WaitForExit()
\r
260 // return WaitForExit(INFINITE)
\r
261 return WaitForExit(0xFFFFFFFF);
\r
267 public int ExitCode {
\r
270 if (! GetExitCodeProcess(pi.hProcess, out lpExitCode) ) {
\r
271 throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
\r
278 /// プロセスの優先度を取得または設定します。
\r
280 public ProcessPriorityClass PriorityClass {
\r
282 return (ProcessPriorityClass) GetPriorityClass(pi.hProcess);
\r
285 SetPriorityClass(pi.hProcess, (uint) value);
\r
292 public void Dispose()
\r
294 if (pi.hProcess != IntPtr.Zero) {
\r
295 CloseHandle(pi.hProcess);
\r