UacHelpers.CppLibrary/UserAccountControl.cpp
author StephaneLenclud
Thu, 18 Apr 2013 23:25:10 +0200
changeset 402 ded1323b61ee
permissions -rw-r--r--
Front View plug-in does not init if no sensor added.
Fixing some format to make strings shorter.
Now trying to start SoundGraphAccess.exe process from same directory.
Packed mode now can display three sensors along with the current time.
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
}