OSDN Git Service

na-get-lib,チケット #36130 に関連して、更新パッケージから非対応パッケージを除外するようにした
[applistation/AppliStation.git] / na-get-lib / NaGet.InteropServices / CreateProcessCaller.cs
1 using System;\r
2 using System.Diagnostics;\r
3 using System.Runtime.InteropServices;\r
4 \r
5 namespace NaGet.InteropServices\r
6 {\r
7         /// <summary>\r
8         /// Win32 API の<code>CreateProcess</code>を直に叩くためのクラス。\r
9         /// </summary>\r
10         /// <remarks>\r
11         /// .NETのProcess.Startは、<code>CreateProcess(NULL, cmdLine, ...)</code>\r
12         /// のように第一引数が<code>NULL</code>に相当する呼び出しが、\r
13         /// *厳密な意味*でできない。厳密な意味で、\r
14         /// これと同じ呼び出しを実現する必要があるときに使われる。\r
15         /// </remarks>\r
16         public class CreateProcessCaller : IDisposable\r
17         {\r
18                 #region Win32API\r
19                 \r
20                 /*\r
21                 [StructLayout(LayoutKind.Sequential)]\r
22                 internal struct SECURITY_ATTRIBUTES\r
23                 {\r
24                     public int nLength;\r
25                     public IntPtr lpSecurityDescriptor;\r
26                     public int bInheritHandle;\r
27                 }\r
28                 */\r
29                 \r
30                 [StructLayout(LayoutKind.Sequential)]\r
31                 internal struct PROCESS_INFORMATION\r
32                 {\r
33                         public IntPtr hProcess;\r
34                         public IntPtr hThread;\r
35                         public int dwProcessId;\r
36                         public int dwThreadId;\r
37                 }\r
38                 \r
39                 [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]\r
40                 struct STARTUPINFO\r
41                 {\r
42                         public Int32 cb;\r
43                         string lpReserved;\r
44                         string lpDesktop;\r
45                         string lpTitle;\r
46                         Int32 dwX;\r
47                         Int32 dwY;\r
48                         Int32 dwXSize;\r
49                         Int32 dwYSize;\r
50                         Int32 dwXCountChars;\r
51                         Int32 dwYCountChars;\r
52                         Int32 dwFillAttribute;\r
53                         Int32 dwFlags;\r
54                         Int16 wShowWindow;\r
55                         Int16 cbReserved2;\r
56                         IntPtr lpReserved2;\r
57                         IntPtr hStdInput;\r
58                         IntPtr hStdOutput;\r
59                         IntPtr hStdError;\r
60                 }\r
61                 \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
73                 \r
74                 [DllImport("kernel32", SetLastError=true, ExactSpelling=true)]\r
75                 static extern UInt32 WaitForSingleObject(IntPtr handle, UInt32 milliseconds);\r
76                 \r
77                 [DllImport("kernel32.dll", SetLastError=true)]\r
78                 [return: MarshalAs(UnmanagedType.Bool)]\r
79                 static extern bool CloseHandle(IntPtr hObject);\r
80                 \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
84                 \r
85                 #region 優先度関連\r
86                 \r
87                 [DllImport("kernel32.dll")]\r
88                 static extern uint GetPriorityClass(IntPtr hProcess);\r
89                 \r
90                 [DllImport("kernel32.dll")]\r
91                 static extern bool SetPriorityClass(IntPtr hProcess, uint dwPriorityClass);\r
92                 \r
93                 #endregion\r
94                 \r
95                 #region 権限降格関連\r
96                 \r
97                 [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError=true)]\r
98                 [return: MarshalAs(UnmanagedType.Bool)]\r
99                 static extern bool CreateProcessWithTokenW(\r
100             IntPtr hToken,\r
101             uint dwLogonFlags,\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
106 \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
110                 \r
111                 [DllImport("user32.dll", SetLastError=true)]\r
112                 static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);\r
113                                 \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
117                 \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
127                         uint TokenType,\r
128                         out IntPtr phNewToken );\r
129                 \r
130                 [DllImport("kernel32.dll")]\r
131                 static extern IntPtr GetEnvironmentStrings();\r
132 \r
133                 [DllImport("kernel32.dll")]\r
134                 static extern bool FreeEnvironmentStrings(IntPtr lpszEnvironmentBlock);\r
135                 \r
136                 #endregion\r
137                 \r
138                 #endregion\r
139                 \r
140                 STARTUPINFO si;\r
141                 PROCESS_INFORMATION pi;\r
142                 \r
143                 /// <summary>\r
144                 /// プロセスを生成する\r
145                 /// </summary>\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
150                 {\r
151                 }\r
152                 \r
153                 public CreateProcessCaller(ProcessStartInfo procInfo, bool runAsNormalUser)\r
154                 {\r
155                         if (procInfo.UseShellExecute) {\r
156                                 throw new ArgumentException("UseShellExecute must be false");\r
157                         }\r
158                         si.cb = Marshal.SizeOf(si);\r
159                         \r
160                         string lpFileName = (string.IsNullOrEmpty(procInfo.FileName))? null : procInfo.FileName;\r
161                         \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
165                         \r
166                         bool retValue;\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
172                         } else {\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
177                         }\r
178                         \r
179                         if (! retValue) {\r
180                                 throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());\r
181                         }\r
182                         if (pi.hThread != IntPtr.Zero) {\r
183                                 CloseHandle(pi.hThread);\r
184                         }\r
185                 }\r
186                 \r
187                 private static 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
197                 {\r
198                         /*\r
199                          * cf. nsWindowsRestart.cpp#LaunchAsNormalUser\r
200                          * 動作チェックせず\r
201                          */\r
202                         \r
203                         \r
204                         lpProcessInformation = new PROCESS_INFORMATION(); /* fake */\r
205                         try {\r
206                                 uint result;\r
207                                 \r
208                                 IntPtr hwndShell = FindWindow("Progman", null);\r
209                                 uint dwProcessId;\r
210                                 result = GetWindowThreadProcessId(hwndShell, out dwProcessId);\r
211                                 if (result == 0) {\r
212                                         throw new System.ComponentModel.Win32Exception();\r
213                                 }\r
214                                 \r
215                                 Process procShell = Process.GetProcessById((int) dwProcessId);\r
216                                 if (procShell == null) {\r
217                                         return false;\r
218                                 }\r
219                                 \r
220                                 IntPtr hTokenHandle, hNewToken;\r
221                                 // bool ok = OpenProcessToken(hProcessShell, MAXIMUM_ALLOWED, out hTokenHandle);\r
222                                 bool ok = OpenProcessToken(procShell.Handle, 0x02000000, out hTokenHandle);\r
223                                 if (!ok) return false;\r
224                                 // ok = DuplicateTokenEx(hTokenShell, MAXIMUM_ALLOWED, null, SecurityDelegation, TokenPrimary, out hNewToken);\r
225                                 ok = DuplicateTokenEx(hTokenHandle, 0x02000000, IntPtr.Zero, 3, 1, out hNewToken);\r
226                                 CloseHandle(hTokenHandle);\r
227                                 if (!ok) return false;\r
228 \r
229                                 IntPtr myenv = GetEnvironmentStrings();\r
230                                 ok = CreateProcessWithTokenW(hNewToken,\r
231                                                              0, // profile is already loaded\r
232                                                              lpApplicationName,\r
233                                                              lpCommandLine,\r
234                                                              dwCreationFlags,\r
235                                                              myenv,\r
236                                                              lpCurrentDirectory,\r
237                                                              ref lpStartupInfo,\r
238                                                              out lpProcessInformation);\r
239                                 if (myenv != IntPtr.Zero) {\r
240                                         FreeEnvironmentStrings(myenv);\r
241                                 }\r
242                                 CloseHandle(hNewToken);\r
243                                 \r
244                                 return ok;\r
245                         } catch {\r
246                                 return false;\r
247                         }\r
248                 }\r
249                 \r
250                 /// <summary>\r
251                 /// 関連付けられたプロセスが終了するまで、最大指定したミリ秒間待機。 \r
252                 /// </summary>\r
253                 /// <param name="timeout">最大待機時間(ミリ秒単位)</param>\r
254                 /// <returns>終了コード</returns>\r
255                 public uint WaitForExit(uint timeout)\r
256                 {\r
257                         return WaitForSingleObject(pi.hProcess, timeout);\r
258                 }\r
259                 \r
260                 /// <summary>\r
261                 /// 関連付けられたプロセスが終了するまで無期限に待機。\r
262                 /// </summary>\r
263                 /// <returns>終了コード</returns>\r
264                 public uint WaitForExit()\r
265                 {\r
266                         // return WaitForExit(INFINITE)\r
267                         return WaitForExit(0xFFFFFFFF);\r
268                 }\r
269                 \r
270                 /// <summary>\r
271                 /// 終了コード\r
272                 /// </summary>\r
273                 public int ExitCode {\r
274                         get {\r
275                                 int lpExitCode;\r
276                                 if (! GetExitCodeProcess(pi.hProcess, out lpExitCode) ) {\r
277                                         throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());\r
278                                 }\r
279                                 return lpExitCode;\r
280                         }\r
281                 }\r
282                 \r
283                 /// <summary>\r
284                 /// プロセスの優先度を取得または設定します。\r
285                 /// </summary>\r
286                 public ProcessPriorityClass PriorityClass {\r
287                         get {\r
288                                 return (ProcessPriorityClass) GetPriorityClass(pi.hProcess);\r
289                         }\r
290                         set {\r
291                                 SetPriorityClass(pi.hProcess, (uint) value);\r
292                         }\r
293                 }\r
294                 \r
295                 /// <summary>\r
296                 /// プロセスのハンドルを開放する\r
297                 /// </summary>\r
298                 public void Dispose()\r
299                 {\r
300                         if (pi.hProcess != IntPtr.Zero) {\r
301                                 CloseHandle(pi.hProcess);\r
302                         }\r
303                         GC.SuppressFinalize(this);\r
304                 }\r
305                 \r
306                 \r
307                 \r
308         }\r
309 }\r