-using System;\r
+using System;\r
using System.Diagnostics;\r
using System.Runtime.InteropServices;\r
\r
namespace NaGet.InteropServices\r
{\r
+ /// <summary>\r
+ /// Win32 API の<code>CreateProcess</code>を直に叩くためのクラス。\r
+ /// </summary>\r
+ /// <remarks>\r
+ /// .NETのProcess.Startは、<code>CreateProcess(NULL, cmdLine, ...)</code>\r
+ /// のように第一引数が<code>NULL</code>に相当する呼び出しが、\r
+ /// *厳密な意味*でできない。厳密な意味で、\r
+ /// これと同じ呼び出しを実現する必要があるときに使われる。\r
+ /// </remarks>\r
public class CreateProcessCaller : IDisposable\r
{\r
#region Win32API\r
[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
PROCESS_INFORMATION pi;\r
\r
+ /// <summary>\r
+ /// プロセスを生成する\r
+ /// </summary>\r
+ /// <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
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
+ /// 関連付けられたプロセスが終了するまで、最大指定したミリ秒間待機。 \r
+ /// </summary>\r
+ /// <param name="timeout">最大待機時間(ミリ秒単位)</param>\r
+ /// <returns>終了コード</returns>\r
public uint WaitForExit(uint timeout)\r
{\r
return WaitForSingleObject(pi.hProcess, timeout);\r
}\r
\r
+ /// <summary>\r
+ /// 関連付けられたプロセスが終了するまで無期限に待機。\r
+ /// </summary>\r
+ /// <returns>終了コード</returns>\r
public uint WaitForExit()\r
{\r
// return WaitForExit(INFINITE)\r
return WaitForExit(0xFFFFFFFF);\r
}\r
\r
+ /// <summary>\r
+ /// 終了コード\r
+ /// </summary>\r
public int ExitCode {\r
get {\r
int lpExitCode;\r
}\r
}\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