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