UacHelpers.CppLibrary/UserAccountControl.cpp
author StephaneLenclud
Wed, 17 Apr 2013 17:09:21 +0200
branchMiniDisplay
changeset 441 2a02ad86a776
permissions -rw-r--r--
Adding FrontView menu with persistent options for time display and packed mode.
Adding support for editorconfig and rebracer.
     1 #include "UserAccountControl.h"
     2 
     3 #include <windows.h>
     4 #pragma comment (lib, "kernel32.lib")
     5 #pragma comment (lib, "advapi32.lib")
     6 
     7 #include <vcclr.h>
     8 #include <msclr\marshal.h>
     9 #include <msclr\marshal_windows.h>
    10 
    11 using namespace msclr::interop;
    12 
    13 using namespace System::ComponentModel;
    14 using namespace Microsoft::Win32;
    15 
    16 namespace UacHelpers {
    17 
    18 	Process^ UserAccountControl::CreateProcessAsAdmin(System::String^ exePath, System::String^ arguments)
    19     {
    20         ProcessStartInfo^ psi = gcnew ProcessStartInfo(exePath, arguments);
    21         psi->UseShellExecute = true;
    22         psi->Verb = "runas";
    23 		return Process::Start(psi);
    24     }
    25 
    26 	Process^ UserAccountControl::CreateProcessAsStandardUser(System::String^ exePath, System::String^ arguments)
    27 	{
    28 		marshal_context context;
    29 
    30 		//If the current process is not elevated, then there's no reason to go through the hassle --
    31 		//just use the standard System.Diagnostics.Process facilities.
    32 		if (!IsCurrentProcessElevated)
    33 		{
    34 			return Process::Start(exePath, arguments);
    35 		}
    36 
    37 		//The following implementation is roughly based on Aaron Margosis' post:
    38 		//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
    39 
    40 		//Enable SeIncreaseQuotaPrivilege in this process.  (This requires administrative privileges.)
    41 		HANDLE hProcessToken = NULL;
    42 		if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hProcessToken))
    43 		{
    44 			throw gcnew Win32Exception(GetLastError());
    45 		}
    46 		else
    47 		{
    48 			TOKEN_PRIVILEGES tkp;
    49 			tkp.PrivilegeCount = 1;
    50 			LookupPrivilegeValueW(NULL, SE_INCREASE_QUOTA_NAME, &tkp.Privileges[0].Luid);
    51 			tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    52 			AdjustTokenPrivileges(hProcessToken, FALSE, &tkp, 0, NULL, NULL);
    53 			DWORD dwLastErr = GetLastError();
    54 			CloseHandle(hProcessToken);
    55 			if (ERROR_SUCCESS != dwLastErr)
    56 			{
    57 				throw gcnew Win32Exception(dwLastErr);
    58 			}
    59 		}
    60 
    61 		//Get window handle representing the desktop shell.  This might not work if there is no shell window, or when
    62 		//using a custom shell.  Also note that we're assuming that the shell is not running elevated.
    63 		HWND hShellWnd = GetShellWindow();
    64 		if (hShellWnd == NULL)
    65 		{
    66 			throw gcnew System::InvalidOperationException("Unable to locate shell window; you might be using a custom shell");
    67 		}
    68 
    69 		//Get the ID of the desktop shell process.
    70 		DWORD dwShellPID;
    71 		GetWindowThreadProcessId(hShellWnd, &dwShellPID);
    72 		if (dwShellPID == 0)
    73 		{
    74 			throw gcnew Win32Exception(GetLastError());
    75 		}
    76 
    77 		//Open the desktop shell process in order to get the process token.
    78 		HANDLE hShellProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwShellPID);
    79 		if (hShellProcess == NULL)
    80 		{
    81 			throw gcnew Win32Exception(GetLastError());
    82 		}
    83 
    84 		HANDLE hShellProcessToken = NULL;
    85 		HANDLE hPrimaryToken = NULL;
    86 		try
    87 		{
    88 			//Get the process token of the desktop shell.
    89 			if (!OpenProcessToken(hShellProcess, TOKEN_DUPLICATE, &hShellProcessToken))
    90 			{
    91 				throw gcnew Win32Exception(GetLastError());
    92 			}
    93 
    94 			//Duplicate the shell's process token to get a primary token.
    95 			const DWORD dwTokenRights = TOKEN_QUERY | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID;
    96 			if (!DuplicateTokenEx(hShellProcessToken, dwTokenRights, NULL, SecurityImpersonation, TokenPrimary, &hPrimaryToken))
    97 			{
    98 				throw gcnew Win32Exception(GetLastError());
    99 			}
   100 
   101 			//Start the target process with the new token.
   102 			STARTUPINFO si = {0}; si.cb = sizeof(si);
   103 			PROCESS_INFORMATION pi = {0};
   104 			if (!CreateProcessWithTokenW(hPrimaryToken, 0,
   105 				context.marshal_as<LPCWSTR>(exePath), context.marshal_as<LPWSTR>(exePath + " " + arguments),
   106 				0, NULL, NULL, &si, &pi))
   107 			{
   108 				throw gcnew Win32Exception(GetLastError());
   109 			}
   110 			CloseHandle(pi.hProcess);
   111 			CloseHandle(pi.hThread);
   112 
   113 			return Process::GetProcessById(pi.dwProcessId);
   114 		}
   115 		finally
   116 		{
   117 			if (hShellProcessToken != NULL)
   118 				CloseHandle(hShellProcessToken);
   119 
   120 			if (hPrimaryToken != NULL)
   121 				CloseHandle(hPrimaryToken);
   122 
   123 			if (hShellProcess != NULL)
   124 				CloseHandle(hShellProcess);
   125 		}
   126 	}
   127 
   128 	bool UserAccountControl::IsUserAdmin::get()
   129 	{
   130 		if (UserAccountControl::IsUacEnabled)
   131 			return GetProcessTokenElevationType() != TokenElevationTypeDefault;	//split token
   132 
   133 		//If UAC is off, we can't rely on the token; check for Admin group.
   134 		return WindowsPrincipal(WindowsIdentity::GetCurrent()).IsInRole("Administrators");
   135 	}
   136 
   137 	bool UserAccountControl::IsUacEnabled::get()
   138 	{
   139 		//Check the HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System\EnableLUA registry value.
   140 		RegistryKey^ key = Registry::LocalMachine->OpenSubKey(UacRegistryKey, false);
   141 		return key->GetValue(UacRegistryValue)->Equals(1);
   142     }
   143 
   144 	void UserAccountControl::DisableUac()
   145 	{
   146 		SetUacRegistryValue(false);
   147 	}
   148 
   149 	void UserAccountControl::DisableUacAndRestartWindows()
   150 	{
   151 		DisableUac();
   152 		RestartWindows();
   153 	}
   154 
   155 	void UserAccountControl::EnableUac()
   156 	{
   157 		SetUacRegistryValue(true);
   158 	}
   159 
   160 	void UserAccountControl::EnableUacAndRestartWindows()
   161 	{
   162 		EnableUac();
   163 		RestartWindows();
   164 	}
   165 
   166 	bool UserAccountControl::IsCurrentProcessElevated::get()
   167 	{
   168 		return GetProcessTokenElevationType() == TokenElevationTypeFull;	//elevated
   169 	}
   170 
   171 	bool UserAccountControl::IsCurrentProcessVirtualized::get()
   172 	{
   173 		HANDLE hToken;
   174         try
   175         {
   176             if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
   177 				throw gcnew Win32Exception(GetLastError());
   178 
   179             DWORD virtualizationEnabled;
   180 			DWORD dwSize;
   181 			if (!GetTokenInformation(hToken, TokenVirtualizationEnabled, &virtualizationEnabled, sizeof(virtualizationEnabled), &dwSize))
   182                 throw gcnew Win32Exception(GetLastError());
   183 
   184 			return virtualizationEnabled != 0;
   185         }
   186         finally
   187         {
   188             CloseHandle(hToken);
   189         }
   190 	}
   191 
   192 	int UserAccountControl::GetProcessTokenElevationType()
   193 	{
   194 		HANDLE hToken;
   195         try
   196         {
   197             if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
   198 				throw gcnew Win32Exception(GetLastError());
   199 
   200             TOKEN_ELEVATION_TYPE elevationType;
   201 			DWORD dwSize;
   202             if (!GetTokenInformation(hToken, TokenElevationType, &elevationType, sizeof(elevationType), &dwSize))
   203                 throw gcnew Win32Exception(GetLastError());
   204 
   205 			return elevationType;
   206         }
   207         finally
   208         {
   209             CloseHandle(hToken);
   210         }
   211 	}
   212 
   213 	void UserAccountControl::SetUacRegistryValue(bool enabled)
   214 	{
   215 		RegistryKey^ key = Registry::LocalMachine->OpenSubKey(UacRegistryKey, true);
   216 		key->SetValue(UacRegistryValue, enabled ? 1 : 0);
   217 	}
   218 
   219 	void UserAccountControl::RestartWindows()
   220 	{
   221 		InitiateSystemShutdownEx(NULL, NULL, 0/*Timeout*/,
   222 								 TRUE/*ForceAppsClosed*/, TRUE/*RebootAfterShutdown*/,
   223 								 SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_RECONFIG | SHTDN_REASON_FLAG_PLANNED);
   224 		//This shutdown flag corresponds to: "Operating System: Reconfiguration (Planned)".
   225 	}
   226 }