OSDN Git Service

AppliStation-GUI,PackageListViewForm.csのリファクタリング
[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("advapi32.dll", CharSet = CharSet.Unicode, SetLastError=true)]\r
88                 [return: MarshalAs(UnmanagedType.Bool)]\r
89                 static extern bool CreateProcessWithTokenW(\r
90             IntPtr hToken,\r
91             uint dwLogonFlags,\r
92             string lpApplicationName, string lpCommandLine,\r
93             uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory,\r
94             [In] ref STARTUPINFO lpStartupInfo,\r
95             out PROCESS_INFORMATION lpProcessInfo);\r
96 \r
97                 // For Windows Mobile, replace user32.dll with coredll.dll\r
98                 [DllImport("user32.dll", SetLastError = true)]\r
99                 static extern IntPtr FindWindow(string lpClassName, string lpWindowName);\r
100                 \r
101                 [DllImport("user32.dll", SetLastError=true)]\r
102                 static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);\r
103                                 \r
104                 [DllImport("advapi32.dll", SetLastError=true)]\r
105                 [return: MarshalAs(UnmanagedType.Bool)]\r
106                 static extern bool OpenProcessToken(IntPtr ProcessHandle, uint DesiredAccess, out IntPtr TokenHandle);\r
107                 \r
108                 [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]\r
109                 extern static bool DuplicateTokenEx(\r
110                         IntPtr hExistingToken,\r
111                         uint dwDesiredAccess,\r
112                         /* ref SECURITY_ATTRIBUTES lpTokenAttributes, */\r
113                         IntPtr lpTokenAttributes,\r
114                         /* SECURITY_IMPERSONATION_LEVEL ImpersonationLevel, */\r
115                         uint ImpersonationLevel,\r
116                         /* TOKEN_TYPE TokenType, */\r
117                         uint TokenType,\r
118                         out IntPtr phNewToken );\r
119                 \r
120                 [DllImport("kernel32.dll")]\r
121                 static extern IntPtr GetEnvironmentStrings();\r
122 \r
123                 [DllImport("kernel32.dll")]\r
124                 static extern bool FreeEnvironmentStrings(IntPtr lpszEnvironmentBlock);\r
125                 \r
126                 #endregion\r
127                 \r
128                 #endregion\r
129                 \r
130                 STARTUPINFO si;\r
131                 PROCESS_INFORMATION pi;\r
132                 \r
133                 /// <summary>\r
134                 /// プロセスを生成する\r
135                 /// </summary>\r
136                 /// <param name="procInfo">プロセス起動情報。\r
137                 /// なお、<code>procInfo.UseShellExecute</code>は必ずfalseでなければならない</param>\r
138                 public CreateProcessCaller(ProcessStartInfo procInfo)\r
139                         : this(procInfo, false)\r
140                 {\r
141                 }\r
142                 \r
143                 public CreateProcessCaller(ProcessStartInfo procInfo, bool runAsNormalUser)\r
144                 {\r
145                         if (procInfo.UseShellExecute) {\r
146                                 throw new ArgumentException("UseShellExecute must be false");\r
147                         }\r
148                         si.cb = Marshal.SizeOf(si);\r
149                         \r
150                         string lpFileName = (string.IsNullOrEmpty(procInfo.FileName))? null : procInfo.FileName;\r
151                         \r
152                         uint dwCreationFlags = 0x0020; // NORMAL_PRIORITY_CLASS\r
153                         if (procInfo.CreateNoWindow) dwCreationFlags |= 0x08000000; // CREATE_NO_WINDOW\r
154                         string lpCurrentDirectory = (System.IO.Directory.Exists(procInfo.WorkingDirectory))? procInfo.WorkingDirectory : null;\r
155                         \r
156                         bool retValue;\r
157                         if (runAsNormalUser && NaGet.Utils.IsAdministrators()) {\r
158                                 retValue = _CreateProcessAsNormalUser(lpFileName, procInfo.Arguments,\r
159                                                       IntPtr.Zero, IntPtr.Zero,\r
160                                                       false, dwCreationFlags,\r
161                                                       IntPtr.Zero, lpCurrentDirectory, ref si, out pi);\r
162                         } else {\r
163                                 retValue = CreateProcess(lpFileName, procInfo.Arguments,\r
164                                                       IntPtr.Zero, IntPtr.Zero,\r
165                                                       false, dwCreationFlags,\r
166                                                       IntPtr.Zero, lpCurrentDirectory, ref si, out pi);\r
167                         }\r
168                         \r
169                         if (! retValue) {\r
170                                 throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());\r
171                         }\r
172                         if (pi.hThread != IntPtr.Zero) {\r
173                                 CloseHandle(pi.hThread);\r
174                         }\r
175                 }\r
176                 \r
177                 private bool _CreateProcessAsNormalUser(string lpApplicationName,\r
178                         string lpCommandLine,\r
179                         /* ref SECURITY_ATTRIBUTES lpProcessAttributes, */\r
180                         IntPtr lpProcessAttributes,\r
181                         /* ref SECURITY_ATTRIBUTES lpThreadAttributes, */\r
182                         IntPtr lpThreadAttributes,\r
183                         bool bInheritHandles,\r
184                         uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory,\r
185                         [In] ref STARTUPINFO lpStartupInfo,\r
186                         out PROCESS_INFORMATION lpProcessInformation)\r
187                 {\r
188                         /*\r
189                          * cf. nsWindowsRestart.cpp#LaunchAsNormalUser\r
190                          * 動作チェックせず\r
191                          */\r
192                         \r
193                         \r
194                         lpProcessInformation = new PROCESS_INFORMATION(); /* fake */\r
195                         try {\r
196                                 IntPtr hwndShell = FindWindow("Progman", null);\r
197                                 uint dwProcessId;\r
198                                 GetWindowThreadProcessId(hwndShell, out dwProcessId);\r
199                                 Process procShell = Process.GetProcessById((int) dwProcessId);\r
200                                 if (procShell == null) {\r
201                                         return false;\r
202                                 }\r
203                                 \r
204                                 IntPtr hTokenHandle, hNewToken;\r
205                                 // bool ok = OpenProcessToken(hProcessShell, MAXIMUM_ALLOWED, out hTokenHandle);\r
206                                 bool ok = OpenProcessToken(procShell.Handle, 0x02000000, out hTokenHandle);\r
207                                 if (!ok) return false;\r
208                                 // ok = DuplicateTokenEx(hTokenShell, MAXIMUM_ALLOWED, null, SecurityDelegation, TokenPrimary, out hNewToken);\r
209                                 ok = DuplicateTokenEx(hTokenHandle, 0x02000000, IntPtr.Zero, 3, 1, out hNewToken);\r
210                                 CloseHandle(hTokenHandle);\r
211                                 if (!ok) return false;\r
212 \r
213                                 IntPtr myenv = GetEnvironmentStrings();\r
214                                 ok = CreateProcessWithTokenW(hNewToken,\r
215                                                              0, // profile is already loaded\r
216                                                              lpApplicationName,\r
217                                                              lpCommandLine,\r
218                                                              dwCreationFlags,\r
219                                                              myenv,\r
220                                                              lpCurrentDirectory,\r
221                                                              ref lpStartupInfo,\r
222                                                              out lpProcessInformation);\r
223                                 if (myenv != IntPtr.Zero) {\r
224                                         FreeEnvironmentStrings(myenv);\r
225                                 }\r
226                                 CloseHandle(hNewToken);\r
227                                 \r
228                                 return ok;\r
229                         } catch {\r
230                                 return false;\r
231                         }\r
232                 }\r
233                 \r
234                 /// <summary>\r
235                 /// 関連付けられたプロセスが終了するまで、最大指定したミリ秒間待機。 \r
236                 /// </summary>\r
237                 /// <param name="timeout">最大待機時間(ミリ秒単位)</param>\r
238                 /// <returns>終了コード</returns>\r
239                 public uint WaitForExit(uint timeout)\r
240                 {\r
241                         return WaitForSingleObject(pi.hProcess, timeout);\r
242                 }\r
243                 \r
244                 /// <summary>\r
245                 /// 関連付けられたプロセスが終了するまで無期限に待機。\r
246                 /// </summary>\r
247                 /// <returns>終了コード</returns>\r
248                 public uint WaitForExit()\r
249                 {\r
250                         // return WaitForExit(INFINITE)\r
251                         return WaitForExit(0xFFFFFFFF);\r
252                 }\r
253                 \r
254                 /// <summary>\r
255                 /// 終了コード\r
256                 /// </summary>\r
257                 public int ExitCode {\r
258                         get {\r
259                                 int lpExitCode;\r
260                                 if (! GetExitCodeProcess(pi.hProcess, out lpExitCode) ) {\r
261                                         throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());\r
262                                 }\r
263                                 return lpExitCode;\r
264                         }\r
265                 }\r
266                 \r
267                 /// <summary>\r
268                 /// プロセスのハンドルを開放する\r
269                 /// </summary>\r
270                 public void Dispose()\r
271                 {\r
272                         if (pi.hProcess != IntPtr.Zero) {\r
273                                 CloseHandle(pi.hProcess);\r
274                         }\r
275                 }\r
276                 \r
277         }\r
278 }\r