OSDN Git Service

AppliStation-all,アセンブリバージョンを1.4.6に
[applistation/AppliStation.git] / na-get-lib / NaGet.InteropServices / CreateProcessCaller.cs
index 696388f..af62a3a 100644 (file)
@@ -1,4 +1,4 @@
-using System;\r
+using System;\r
 using System.Diagnostics;\r
 using System.Runtime.InteropServices;\r
 \r
@@ -82,6 +82,59 @@ namespace NaGet.InteropServices
                [return: MarshalAs(UnmanagedType.Bool)]\r
                static extern bool GetExitCodeProcess(IntPtr hProcess, out int lpExitCode);\r
                \r
+               #region 優先度関連\r
+               \r
+               [DllImport("kernel32.dll")]\r
+               static extern uint GetPriorityClass(IntPtr hProcess);\r
+               \r
+               [DllImport("kernel32.dll")]\r
+               static extern bool SetPriorityClass(IntPtr hProcess, uint dwPriorityClass);\r
+               \r
+               #endregion\r
+               \r
+               #region 権限降格関連\r
+               \r
+               [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError=true)]\r
+               [return: MarshalAs(UnmanagedType.Bool)]\r
+               static extern bool CreateProcessWithTokenW(\r
+            IntPtr hToken,\r
+            uint dwLogonFlags,\r
+            string lpApplicationName, string lpCommandLine,\r
+            uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory,\r
+            [In] ref STARTUPINFO lpStartupInfo,\r
+            out PROCESS_INFORMATION lpProcessInfo);\r
+\r
+               // For Windows Mobile, replace user32.dll with coredll.dll\r
+               [DllImport("user32.dll", SetLastError = true)]\r
+               static extern IntPtr FindWindow(string lpClassName, string lpWindowName);\r
+               \r
+               [DllImport("user32.dll", SetLastError=true)]\r
+               static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);\r
+                               \r
+               [DllImport("advapi32.dll", SetLastError=true)]\r
+               [return: MarshalAs(UnmanagedType.Bool)]\r
+               static extern bool OpenProcessToken(IntPtr ProcessHandle, uint DesiredAccess, out IntPtr TokenHandle);\r
+               \r
+               [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]\r
+               extern static bool DuplicateTokenEx(\r
+                       IntPtr hExistingToken,\r
+                       uint dwDesiredAccess,\r
+                       /* ref SECURITY_ATTRIBUTES lpTokenAttributes, */\r
+                       IntPtr lpTokenAttributes,\r
+                       /* SECURITY_IMPERSONATION_LEVEL ImpersonationLevel, */\r
+                       uint ImpersonationLevel,\r
+                       /* TOKEN_TYPE TokenType, */\r
+                       uint TokenType,\r
+                       out IntPtr phNewToken );\r
+               \r
+               [DllImport("kernel32.dll")]\r
+               static extern IntPtr GetEnvironmentStrings();\r
+\r
+               [DllImport("kernel32.dll")]\r
+               static extern bool FreeEnvironmentStrings(IntPtr lpszEnvironmentBlock);\r
+               \r
+               #endregion\r
+               \r
                #endregion\r
                \r
                STARTUPINFO si;\r
@@ -93,6 +146,11 @@ namespace NaGet.InteropServices
                /// <param name="procInfo">プロセス起動情報。\r
                /// なお、<code>procInfo.UseShellExecute</code>は必ずfalseでなければならない</param>\r
                public CreateProcessCaller(ProcessStartInfo procInfo)\r
+                       : this(procInfo, false)\r
+               {\r
+               }\r
+               \r
+               public CreateProcessCaller(ProcessStartInfo procInfo, bool runAsNormalUser)\r
                {\r
                        if (procInfo.UseShellExecute) {\r
                                throw new ArgumentException("UseShellExecute must be false");\r
@@ -105,14 +163,88 @@ namespace NaGet.InteropServices
                        if (procInfo.CreateNoWindow) dwCreationFlags |= 0x08000000; // CREATE_NO_WINDOW\r
                        string lpCurrentDirectory = (System.IO.Directory.Exists(procInfo.WorkingDirectory))? procInfo.WorkingDirectory : null;\r
                        \r
-                       bool retValue = CreateProcess(lpFileName, procInfo.Arguments,\r
+                       bool retValue;\r
+                       if (runAsNormalUser && NaGet.Utils.IsAdministrators()) {\r
+                               retValue = _CreateProcessAsNormalUser(lpFileName, procInfo.Arguments,\r
                                                      IntPtr.Zero, IntPtr.Zero,\r
                                                      false, dwCreationFlags,\r
                                                      IntPtr.Zero, lpCurrentDirectory, ref si, out pi);\r
+                       } else {\r
+                               retValue = CreateProcess(lpFileName, procInfo.Arguments,\r
+                                                     IntPtr.Zero, IntPtr.Zero,\r
+                                                     false, dwCreationFlags,\r
+                                                     IntPtr.Zero, lpCurrentDirectory, ref si, out pi);\r
+                       }\r
+                       \r
                        if (! retValue) {\r
                                throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());\r
                        }\r
-                       CloseHandle(pi.hThread);\r
+                       if (pi.hThread != IntPtr.Zero) {\r
+                               CloseHandle(pi.hThread);\r
+                       }\r
+               }\r
+               \r
+               private static bool _CreateProcessAsNormalUser(string lpApplicationName,\r
+                       string lpCommandLine,\r
+                       /* ref SECURITY_ATTRIBUTES lpProcessAttributes, */\r
+                       IntPtr lpProcessAttributes,\r
+                       /* ref SECURITY_ATTRIBUTES lpThreadAttributes, */\r
+                       IntPtr lpThreadAttributes,\r
+                       bool bInheritHandles,\r
+                       uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory,\r
+                       [In] ref STARTUPINFO lpStartupInfo,\r
+                       out PROCESS_INFORMATION lpProcessInformation)\r
+               {\r
+                       /*\r
+                        * cf. nsWindowsRestart.cpp#LaunchAsNormalUser\r
+                        * 動作チェックせず\r
+                        */\r
+                       \r
+                       \r
+                       lpProcessInformation = new PROCESS_INFORMATION(); /* fake */\r
+                       try {\r
+                               uint result;\r
+                               \r
+                               IntPtr hwndShell = FindWindow("Progman", null);\r
+                               uint dwProcessId;\r
+                               result = GetWindowThreadProcessId(hwndShell, out dwProcessId);\r
+                               if (result == 0) {\r
+                                       throw new System.ComponentModel.Win32Exception();\r
+                               }\r
+                               \r
+                               Process procShell = Process.GetProcessById((int) dwProcessId);\r
+                               if (procShell == null) {\r
+                                       return false;\r
+                               }\r
+                               \r
+                               IntPtr hTokenHandle, hNewToken;\r
+                               // bool ok = OpenProcessToken(hProcessShell, MAXIMUM_ALLOWED, out hTokenHandle);\r
+                               bool ok = OpenProcessToken(procShell.Handle, 0x02000000, out hTokenHandle);\r
+                               if (!ok) return false;\r
+                               // ok = DuplicateTokenEx(hTokenShell, MAXIMUM_ALLOWED, null, SecurityDelegation, TokenPrimary, out hNewToken);\r
+                               ok = DuplicateTokenEx(hTokenHandle, 0x02000000, IntPtr.Zero, 3, 1, out hNewToken);\r
+                               CloseHandle(hTokenHandle);\r
+                               if (!ok) return false;\r
+\r
+                               IntPtr myenv = GetEnvironmentStrings();\r
+                               ok = CreateProcessWithTokenW(hNewToken,\r
+                                                            0, // profile is already loaded\r
+                                                            lpApplicationName,\r
+                                                            lpCommandLine,\r
+                                                            dwCreationFlags,\r
+                                                            myenv,\r
+                                                            lpCurrentDirectory,\r
+                                                            ref lpStartupInfo,\r
+                                                            out lpProcessInformation);\r
+                               if (myenv != IntPtr.Zero) {\r
+                                       FreeEnvironmentStrings(myenv);\r
+                               }\r
+                               CloseHandle(hNewToken);\r
+                               \r
+                               return ok;\r
+                       } catch {\r
+                               return false;\r
+                       }\r
                }\r
                \r
                /// <summary>\r
@@ -149,12 +281,29 @@ namespace NaGet.InteropServices
                }\r
                \r
                /// <summary>\r
+               /// プロセスの優先度を取得または設定します。\r
+               /// </summary>\r
+               public ProcessPriorityClass PriorityClass {\r
+                       get {\r
+                               return (ProcessPriorityClass) GetPriorityClass(pi.hProcess);\r
+                       }\r
+                       set {\r
+                               SetPriorityClass(pi.hProcess, (uint) value);\r
+                       }\r
+               }\r
+               \r
+               /// <summary>\r
                /// プロセスのハンドルを開放する\r
                /// </summary>\r
                public void Dispose()\r
                {\r
-                       CloseHandle(pi.hProcess);\r
+                       if (pi.hProcess != IntPtr.Zero) {\r
+                               CloseHandle(pi.hProcess);\r
+                       }\r
+                       GC.SuppressFinalize(this);\r
                }\r
                \r
+               \r
+               \r
        }\r
 }\r