StephaneLenclud@394: #include "UserAccountControl.h" StephaneLenclud@394: StephaneLenclud@394: #include StephaneLenclud@394: #pragma comment (lib, "kernel32.lib") StephaneLenclud@394: #pragma comment (lib, "advapi32.lib") StephaneLenclud@394: StephaneLenclud@394: #include StephaneLenclud@394: #include StephaneLenclud@394: #include StephaneLenclud@394: StephaneLenclud@394: using namespace msclr::interop; StephaneLenclud@394: StephaneLenclud@394: using namespace System::ComponentModel; StephaneLenclud@394: using namespace Microsoft::Win32; StephaneLenclud@394: StephaneLenclud@394: namespace UacHelpers { StephaneLenclud@394: StephaneLenclud@394: Process^ UserAccountControl::CreateProcessAsAdmin(System::String^ exePath, System::String^ arguments) StephaneLenclud@394: { StephaneLenclud@394: ProcessStartInfo^ psi = gcnew ProcessStartInfo(exePath, arguments); StephaneLenclud@394: psi->UseShellExecute = true; StephaneLenclud@394: psi->Verb = "runas"; StephaneLenclud@394: return Process::Start(psi); StephaneLenclud@394: } StephaneLenclud@394: StephaneLenclud@394: Process^ UserAccountControl::CreateProcessAsStandardUser(System::String^ exePath, System::String^ arguments) StephaneLenclud@394: { StephaneLenclud@394: marshal_context context; StephaneLenclud@394: StephaneLenclud@394: //If the current process is not elevated, then there's no reason to go through the hassle -- StephaneLenclud@394: //just use the standard System.Diagnostics.Process facilities. StephaneLenclud@394: if (!IsCurrentProcessElevated) StephaneLenclud@394: { StephaneLenclud@394: return Process::Start(exePath, arguments); StephaneLenclud@394: } StephaneLenclud@394: StephaneLenclud@394: //The following implementation is roughly based on Aaron Margosis' post: StephaneLenclud@394: //http://blogs.msdn.com/aaron_margosis/archive/2009/06/06/faq-how-do-i-start-a-program-as-the-desktop-user-from-an-elevated-app.aspx StephaneLenclud@394: StephaneLenclud@394: //Enable SeIncreaseQuotaPrivilege in this process. (This requires administrative privileges.) StephaneLenclud@394: HANDLE hProcessToken = NULL; StephaneLenclud@394: if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hProcessToken)) StephaneLenclud@394: { StephaneLenclud@394: throw gcnew Win32Exception(GetLastError()); StephaneLenclud@394: } StephaneLenclud@394: else StephaneLenclud@394: { StephaneLenclud@394: TOKEN_PRIVILEGES tkp; StephaneLenclud@394: tkp.PrivilegeCount = 1; StephaneLenclud@394: LookupPrivilegeValueW(NULL, SE_INCREASE_QUOTA_NAME, &tkp.Privileges[0].Luid); StephaneLenclud@394: tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; StephaneLenclud@394: AdjustTokenPrivileges(hProcessToken, FALSE, &tkp, 0, NULL, NULL); StephaneLenclud@394: DWORD dwLastErr = GetLastError(); StephaneLenclud@394: CloseHandle(hProcessToken); StephaneLenclud@394: if (ERROR_SUCCESS != dwLastErr) StephaneLenclud@394: { StephaneLenclud@394: throw gcnew Win32Exception(dwLastErr); StephaneLenclud@394: } StephaneLenclud@394: } StephaneLenclud@394: StephaneLenclud@394: //Get window handle representing the desktop shell. This might not work if there is no shell window, or when StephaneLenclud@394: //using a custom shell. Also note that we're assuming that the shell is not running elevated. StephaneLenclud@394: HWND hShellWnd = GetShellWindow(); StephaneLenclud@394: if (hShellWnd == NULL) StephaneLenclud@394: { StephaneLenclud@394: throw gcnew System::InvalidOperationException("Unable to locate shell window; you might be using a custom shell"); StephaneLenclud@394: } StephaneLenclud@394: StephaneLenclud@394: //Get the ID of the desktop shell process. StephaneLenclud@394: DWORD dwShellPID; StephaneLenclud@394: GetWindowThreadProcessId(hShellWnd, &dwShellPID); StephaneLenclud@394: if (dwShellPID == 0) StephaneLenclud@394: { StephaneLenclud@394: throw gcnew Win32Exception(GetLastError()); StephaneLenclud@394: } StephaneLenclud@394: StephaneLenclud@394: //Open the desktop shell process in order to get the process token. StephaneLenclud@394: HANDLE hShellProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwShellPID); StephaneLenclud@394: if (hShellProcess == NULL) StephaneLenclud@394: { StephaneLenclud@394: throw gcnew Win32Exception(GetLastError()); StephaneLenclud@394: } StephaneLenclud@394: StephaneLenclud@394: HANDLE hShellProcessToken = NULL; StephaneLenclud@394: HANDLE hPrimaryToken = NULL; StephaneLenclud@394: try StephaneLenclud@394: { StephaneLenclud@394: //Get the process token of the desktop shell. StephaneLenclud@394: if (!OpenProcessToken(hShellProcess, TOKEN_DUPLICATE, &hShellProcessToken)) StephaneLenclud@394: { StephaneLenclud@394: throw gcnew Win32Exception(GetLastError()); StephaneLenclud@394: } StephaneLenclud@394: StephaneLenclud@394: //Duplicate the shell's process token to get a primary token. StephaneLenclud@394: const DWORD dwTokenRights = TOKEN_QUERY | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID; StephaneLenclud@394: if (!DuplicateTokenEx(hShellProcessToken, dwTokenRights, NULL, SecurityImpersonation, TokenPrimary, &hPrimaryToken)) StephaneLenclud@394: { StephaneLenclud@394: throw gcnew Win32Exception(GetLastError()); StephaneLenclud@394: } StephaneLenclud@394: StephaneLenclud@394: //Start the target process with the new token. StephaneLenclud@394: STARTUPINFO si = {0}; si.cb = sizeof(si); StephaneLenclud@394: PROCESS_INFORMATION pi = {0}; StephaneLenclud@394: if (!CreateProcessWithTokenW(hPrimaryToken, 0, StephaneLenclud@394: context.marshal_as(exePath), context.marshal_as(exePath + " " + arguments), StephaneLenclud@394: 0, NULL, NULL, &si, &pi)) StephaneLenclud@394: { StephaneLenclud@394: throw gcnew Win32Exception(GetLastError()); StephaneLenclud@394: } StephaneLenclud@394: CloseHandle(pi.hProcess); StephaneLenclud@394: CloseHandle(pi.hThread); StephaneLenclud@394: StephaneLenclud@394: return Process::GetProcessById(pi.dwProcessId); StephaneLenclud@394: } StephaneLenclud@394: finally StephaneLenclud@394: { StephaneLenclud@394: if (hShellProcessToken != NULL) StephaneLenclud@394: CloseHandle(hShellProcessToken); StephaneLenclud@394: StephaneLenclud@394: if (hPrimaryToken != NULL) StephaneLenclud@394: CloseHandle(hPrimaryToken); StephaneLenclud@394: StephaneLenclud@394: if (hShellProcess != NULL) StephaneLenclud@394: CloseHandle(hShellProcess); StephaneLenclud@394: } StephaneLenclud@394: } StephaneLenclud@394: StephaneLenclud@394: bool UserAccountControl::IsUserAdmin::get() StephaneLenclud@394: { StephaneLenclud@394: if (UserAccountControl::IsUacEnabled) StephaneLenclud@394: return GetProcessTokenElevationType() != TokenElevationTypeDefault; //split token StephaneLenclud@394: StephaneLenclud@394: //If UAC is off, we can't rely on the token; check for Admin group. StephaneLenclud@394: return WindowsPrincipal(WindowsIdentity::GetCurrent()).IsInRole("Administrators"); StephaneLenclud@394: } StephaneLenclud@394: StephaneLenclud@394: bool UserAccountControl::IsUacEnabled::get() StephaneLenclud@394: { StephaneLenclud@394: //Check the HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System\EnableLUA registry value. StephaneLenclud@394: RegistryKey^ key = Registry::LocalMachine->OpenSubKey(UacRegistryKey, false); StephaneLenclud@394: return key->GetValue(UacRegistryValue)->Equals(1); StephaneLenclud@394: } StephaneLenclud@394: StephaneLenclud@394: void UserAccountControl::DisableUac() StephaneLenclud@394: { StephaneLenclud@394: SetUacRegistryValue(false); StephaneLenclud@394: } StephaneLenclud@394: StephaneLenclud@394: void UserAccountControl::DisableUacAndRestartWindows() StephaneLenclud@394: { StephaneLenclud@394: DisableUac(); StephaneLenclud@394: RestartWindows(); StephaneLenclud@394: } StephaneLenclud@394: StephaneLenclud@394: void UserAccountControl::EnableUac() StephaneLenclud@394: { StephaneLenclud@394: SetUacRegistryValue(true); StephaneLenclud@394: } StephaneLenclud@394: StephaneLenclud@394: void UserAccountControl::EnableUacAndRestartWindows() StephaneLenclud@394: { StephaneLenclud@394: EnableUac(); StephaneLenclud@394: RestartWindows(); StephaneLenclud@394: } StephaneLenclud@394: StephaneLenclud@394: bool UserAccountControl::IsCurrentProcessElevated::get() StephaneLenclud@394: { StephaneLenclud@394: return GetProcessTokenElevationType() == TokenElevationTypeFull; //elevated StephaneLenclud@394: } StephaneLenclud@394: StephaneLenclud@394: bool UserAccountControl::IsCurrentProcessVirtualized::get() StephaneLenclud@394: { StephaneLenclud@394: HANDLE hToken; StephaneLenclud@394: try StephaneLenclud@394: { StephaneLenclud@394: if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) StephaneLenclud@394: throw gcnew Win32Exception(GetLastError()); StephaneLenclud@394: StephaneLenclud@394: DWORD virtualizationEnabled; StephaneLenclud@394: DWORD dwSize; StephaneLenclud@394: if (!GetTokenInformation(hToken, TokenVirtualizationEnabled, &virtualizationEnabled, sizeof(virtualizationEnabled), &dwSize)) StephaneLenclud@394: throw gcnew Win32Exception(GetLastError()); StephaneLenclud@394: StephaneLenclud@394: return virtualizationEnabled != 0; StephaneLenclud@394: } StephaneLenclud@394: finally StephaneLenclud@394: { StephaneLenclud@394: CloseHandle(hToken); StephaneLenclud@394: } StephaneLenclud@394: } StephaneLenclud@394: StephaneLenclud@394: int UserAccountControl::GetProcessTokenElevationType() StephaneLenclud@394: { StephaneLenclud@394: HANDLE hToken; StephaneLenclud@394: try StephaneLenclud@394: { StephaneLenclud@394: if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) StephaneLenclud@394: throw gcnew Win32Exception(GetLastError()); StephaneLenclud@394: StephaneLenclud@394: TOKEN_ELEVATION_TYPE elevationType; StephaneLenclud@394: DWORD dwSize; StephaneLenclud@394: if (!GetTokenInformation(hToken, TokenElevationType, &elevationType, sizeof(elevationType), &dwSize)) StephaneLenclud@394: throw gcnew Win32Exception(GetLastError()); StephaneLenclud@394: StephaneLenclud@394: return elevationType; StephaneLenclud@394: } StephaneLenclud@394: finally StephaneLenclud@394: { StephaneLenclud@394: CloseHandle(hToken); StephaneLenclud@394: } StephaneLenclud@394: } StephaneLenclud@394: StephaneLenclud@394: void UserAccountControl::SetUacRegistryValue(bool enabled) StephaneLenclud@394: { StephaneLenclud@394: RegistryKey^ key = Registry::LocalMachine->OpenSubKey(UacRegistryKey, true); StephaneLenclud@394: key->SetValue(UacRegistryValue, enabled ? 1 : 0); StephaneLenclud@394: } StephaneLenclud@394: StephaneLenclud@394: void UserAccountControl::RestartWindows() StephaneLenclud@394: { StephaneLenclud@394: InitiateSystemShutdownEx(NULL, NULL, 0/*Timeout*/, StephaneLenclud@394: TRUE/*ForceAppsClosed*/, TRUE/*RebootAfterShutdown*/, StephaneLenclud@394: SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_RECONFIG | SHTDN_REASON_FLAG_PLANNED); StephaneLenclud@394: //This shutdown flag corresponds to: "Operating System: Reconfiguration (Planned)". StephaneLenclud@394: } StephaneLenclud@394: }