UacHelpers.CppLibrary/UserAccountControl.cpp
changeset 394 60a1e2b6ed71
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/UacHelpers.CppLibrary/UserAccountControl.cpp	Sat Feb 09 17:18:09 2013 +0100
     1.3 @@ -0,0 +1,226 @@
     1.4 +#include "UserAccountControl.h"
     1.5 +
     1.6 +#include <windows.h>
     1.7 +#pragma comment (lib, "kernel32.lib")
     1.8 +#pragma comment (lib, "advapi32.lib")
     1.9 +
    1.10 +#include <vcclr.h>
    1.11 +#include <msclr\marshal.h>
    1.12 +#include <msclr\marshal_windows.h>
    1.13 +
    1.14 +using namespace msclr::interop;
    1.15 +
    1.16 +using namespace System::ComponentModel;
    1.17 +using namespace Microsoft::Win32;
    1.18 +
    1.19 +namespace UacHelpers {
    1.20 +
    1.21 +	Process^ UserAccountControl::CreateProcessAsAdmin(System::String^ exePath, System::String^ arguments)
    1.22 +    {
    1.23 +        ProcessStartInfo^ psi = gcnew ProcessStartInfo(exePath, arguments);
    1.24 +        psi->UseShellExecute = true;
    1.25 +        psi->Verb = "runas";
    1.26 +		return Process::Start(psi);
    1.27 +    }
    1.28 +
    1.29 +	Process^ UserAccountControl::CreateProcessAsStandardUser(System::String^ exePath, System::String^ arguments)
    1.30 +	{
    1.31 +		marshal_context context;
    1.32 +
    1.33 +		//If the current process is not elevated, then there's no reason to go through the hassle --
    1.34 +		//just use the standard System.Diagnostics.Process facilities.
    1.35 +		if (!IsCurrentProcessElevated)
    1.36 +		{
    1.37 +			return Process::Start(exePath, arguments);
    1.38 +		}
    1.39 +
    1.40 +		//The following implementation is roughly based on Aaron Margosis' post:
    1.41 +		//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
    1.42 +
    1.43 +		//Enable SeIncreaseQuotaPrivilege in this process.  (This requires administrative privileges.)
    1.44 +		HANDLE hProcessToken = NULL;
    1.45 +		if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hProcessToken))
    1.46 +		{
    1.47 +			throw gcnew Win32Exception(GetLastError());
    1.48 +		}
    1.49 +		else
    1.50 +		{
    1.51 +			TOKEN_PRIVILEGES tkp;
    1.52 +			tkp.PrivilegeCount = 1;
    1.53 +			LookupPrivilegeValueW(NULL, SE_INCREASE_QUOTA_NAME, &tkp.Privileges[0].Luid);
    1.54 +			tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    1.55 +			AdjustTokenPrivileges(hProcessToken, FALSE, &tkp, 0, NULL, NULL);
    1.56 +			DWORD dwLastErr = GetLastError();
    1.57 +			CloseHandle(hProcessToken);
    1.58 +			if (ERROR_SUCCESS != dwLastErr)
    1.59 +			{
    1.60 +				throw gcnew Win32Exception(dwLastErr);
    1.61 +			}
    1.62 +		}
    1.63 +
    1.64 +		//Get window handle representing the desktop shell.  This might not work if there is no shell window, or when
    1.65 +		//using a custom shell.  Also note that we're assuming that the shell is not running elevated.
    1.66 +		HWND hShellWnd = GetShellWindow();
    1.67 +		if (hShellWnd == NULL)
    1.68 +		{
    1.69 +			throw gcnew System::InvalidOperationException("Unable to locate shell window; you might be using a custom shell");
    1.70 +		}
    1.71 +
    1.72 +		//Get the ID of the desktop shell process.
    1.73 +		DWORD dwShellPID;
    1.74 +		GetWindowThreadProcessId(hShellWnd, &dwShellPID);
    1.75 +		if (dwShellPID == 0)
    1.76 +		{
    1.77 +			throw gcnew Win32Exception(GetLastError());
    1.78 +		}
    1.79 +
    1.80 +		//Open the desktop shell process in order to get the process token.
    1.81 +		HANDLE hShellProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwShellPID);
    1.82 +		if (hShellProcess == NULL)
    1.83 +		{
    1.84 +			throw gcnew Win32Exception(GetLastError());
    1.85 +		}
    1.86 +
    1.87 +		HANDLE hShellProcessToken = NULL;
    1.88 +		HANDLE hPrimaryToken = NULL;
    1.89 +		try
    1.90 +		{
    1.91 +			//Get the process token of the desktop shell.
    1.92 +			if (!OpenProcessToken(hShellProcess, TOKEN_DUPLICATE, &hShellProcessToken))
    1.93 +			{
    1.94 +				throw gcnew Win32Exception(GetLastError());
    1.95 +			}
    1.96 +
    1.97 +			//Duplicate the shell's process token to get a primary token.
    1.98 +			const DWORD dwTokenRights = TOKEN_QUERY | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID;
    1.99 +			if (!DuplicateTokenEx(hShellProcessToken, dwTokenRights, NULL, SecurityImpersonation, TokenPrimary, &hPrimaryToken))
   1.100 +			{
   1.101 +				throw gcnew Win32Exception(GetLastError());
   1.102 +			}
   1.103 +
   1.104 +			//Start the target process with the new token.
   1.105 +			STARTUPINFO si = {0}; si.cb = sizeof(si);
   1.106 +			PROCESS_INFORMATION pi = {0};
   1.107 +			if (!CreateProcessWithTokenW(hPrimaryToken, 0,
   1.108 +				context.marshal_as<LPCWSTR>(exePath), context.marshal_as<LPWSTR>(exePath + " " + arguments),
   1.109 +				0, NULL, NULL, &si, &pi))
   1.110 +			{
   1.111 +				throw gcnew Win32Exception(GetLastError());
   1.112 +			}
   1.113 +			CloseHandle(pi.hProcess);
   1.114 +			CloseHandle(pi.hThread);
   1.115 +
   1.116 +			return Process::GetProcessById(pi.dwProcessId);
   1.117 +		}
   1.118 +		finally
   1.119 +		{
   1.120 +			if (hShellProcessToken != NULL)
   1.121 +				CloseHandle(hShellProcessToken);
   1.122 +
   1.123 +			if (hPrimaryToken != NULL)
   1.124 +				CloseHandle(hPrimaryToken);
   1.125 +
   1.126 +			if (hShellProcess != NULL)
   1.127 +				CloseHandle(hShellProcess);
   1.128 +		}
   1.129 +	}
   1.130 +
   1.131 +	bool UserAccountControl::IsUserAdmin::get()
   1.132 +	{
   1.133 +		if (UserAccountControl::IsUacEnabled)
   1.134 +			return GetProcessTokenElevationType() != TokenElevationTypeDefault;	//split token
   1.135 +
   1.136 +		//If UAC is off, we can't rely on the token; check for Admin group.
   1.137 +		return WindowsPrincipal(WindowsIdentity::GetCurrent()).IsInRole("Administrators");
   1.138 +	}
   1.139 +
   1.140 +	bool UserAccountControl::IsUacEnabled::get()
   1.141 +	{
   1.142 +		//Check the HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System\EnableLUA registry value.
   1.143 +		RegistryKey^ key = Registry::LocalMachine->OpenSubKey(UacRegistryKey, false);
   1.144 +		return key->GetValue(UacRegistryValue)->Equals(1);
   1.145 +    }
   1.146 +
   1.147 +	void UserAccountControl::DisableUac()
   1.148 +	{
   1.149 +		SetUacRegistryValue(false);
   1.150 +	}
   1.151 +
   1.152 +	void UserAccountControl::DisableUacAndRestartWindows()
   1.153 +	{
   1.154 +		DisableUac();
   1.155 +		RestartWindows();
   1.156 +	}
   1.157 +
   1.158 +	void UserAccountControl::EnableUac()
   1.159 +	{
   1.160 +		SetUacRegistryValue(true);
   1.161 +	}
   1.162 +
   1.163 +	void UserAccountControl::EnableUacAndRestartWindows()
   1.164 +	{
   1.165 +		EnableUac();
   1.166 +		RestartWindows();
   1.167 +	}
   1.168 +
   1.169 +	bool UserAccountControl::IsCurrentProcessElevated::get()
   1.170 +	{
   1.171 +		return GetProcessTokenElevationType() == TokenElevationTypeFull;	//elevated
   1.172 +	}
   1.173 +
   1.174 +	bool UserAccountControl::IsCurrentProcessVirtualized::get()
   1.175 +	{
   1.176 +		HANDLE hToken;
   1.177 +        try
   1.178 +        {
   1.179 +            if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
   1.180 +				throw gcnew Win32Exception(GetLastError());
   1.181 +
   1.182 +            DWORD virtualizationEnabled;
   1.183 +			DWORD dwSize;
   1.184 +			if (!GetTokenInformation(hToken, TokenVirtualizationEnabled, &virtualizationEnabled, sizeof(virtualizationEnabled), &dwSize))
   1.185 +                throw gcnew Win32Exception(GetLastError());
   1.186 +
   1.187 +			return virtualizationEnabled != 0;
   1.188 +        }
   1.189 +        finally
   1.190 +        {
   1.191 +            CloseHandle(hToken);
   1.192 +        }
   1.193 +	}
   1.194 +
   1.195 +	int UserAccountControl::GetProcessTokenElevationType()
   1.196 +	{
   1.197 +		HANDLE hToken;
   1.198 +        try
   1.199 +        {
   1.200 +            if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
   1.201 +				throw gcnew Win32Exception(GetLastError());
   1.202 +
   1.203 +            TOKEN_ELEVATION_TYPE elevationType;
   1.204 +			DWORD dwSize;
   1.205 +            if (!GetTokenInformation(hToken, TokenElevationType, &elevationType, sizeof(elevationType), &dwSize))
   1.206 +                throw gcnew Win32Exception(GetLastError());
   1.207 +
   1.208 +			return elevationType;
   1.209 +        }
   1.210 +        finally
   1.211 +        {
   1.212 +            CloseHandle(hToken);
   1.213 +        }
   1.214 +	}
   1.215 +
   1.216 +	void UserAccountControl::SetUacRegistryValue(bool enabled)
   1.217 +	{
   1.218 +		RegistryKey^ key = Registry::LocalMachine->OpenSubKey(UacRegistryKey, true);
   1.219 +		key->SetValue(UacRegistryValue, enabled ? 1 : 0);
   1.220 +	}
   1.221 +
   1.222 +	void UserAccountControl::RestartWindows()
   1.223 +	{
   1.224 +		InitiateSystemShutdownEx(NULL, NULL, 0/*Timeout*/,
   1.225 +								 TRUE/*ForceAppsClosed*/, TRUE/*RebootAfterShutdown*/,
   1.226 +								 SHTDN_REASON_MAJOR_OPERATINGSYSTEM | SHTDN_REASON_MINOR_RECONFIG | SHTDN_REASON_FLAG_PLANNED);
   1.227 +		//This shutdown flag corresponds to: "Operating System: Reconfiguration (Planned)".
   1.228 +	}
   1.229 +}
   1.230 \ No newline at end of file