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