nss: upgrade to release 3.73
[LibreOffice.git] / onlineupdate / source / update / common / updatehelper.cxx
blob09f857b1a9e90396b68fdbecbcfce7751b68848c
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #ifdef _WIN32
6 #include <windows.h>
8 // Needed for CreateToolhelp32Snapshot
9 #include <tlhelp32.h>
10 #ifndef ONLY_SERVICE_LAUNCHING
12 #include <stdio.h>
13 #include "shlobj.h"
14 #include "updatehelper.h"
15 #include "uachelper.h"
16 #include "pathhash.h"
18 #include <memory>
20 // Needed for PathAppendW
21 #include <shlwapi.h>
23 BOOL PathAppendSafe(LPWSTR base, LPCWSTR extra);
25 /**
26 * Obtains the path of a file in the same directory as the specified file.
28 * @param destinationBuffer A buffer of size MAX_PATH + 1 to store the result.
29 * @param siblingFIlePath The path of another file in the same directory
30 * @param newFileName The filename of another file in the same directory
31 * @return TRUE if successful
33 BOOL
34 PathGetSiblingFilePath(LPWSTR destinationBuffer,
35 LPCWSTR siblingFilePath,
36 LPCWSTR newFileName)
38 if (wcslen(siblingFilePath) >= MAX_PATH)
40 return FALSE;
43 wcsncpy(destinationBuffer, siblingFilePath, MAX_PATH);
44 if (!PathRemoveFileSpecW(destinationBuffer))
46 return FALSE;
49 if (wcslen(destinationBuffer) + wcslen(newFileName) >= MAX_PATH)
51 return FALSE;
54 return PathAppendSafe(destinationBuffer, newFileName);
57 /**
58 * Launch the post update application as the specified user (helper.exe).
59 * It takes in the path of the callback application to calculate the path
60 * of helper.exe. For service updates this is called from both the system
61 * account and the current user account.
63 * @param installationDir The path to the callback application binary.
64 * @param updateInfoDir The directory where update info is stored.
65 * @param forceSync If true even if the ini file specifies async, the
66 * process will wait for termination of PostUpdate.
67 * @param userToken The user token to run as, if nullptr the current
68 * user will be used.
69 * @return TRUE if there was no error starting the process.
71 BOOL
72 LaunchWinPostProcess(const WCHAR *installationDir,
73 const WCHAR *updateInfoDir,
74 bool forceSync,
75 HANDLE userToken)
77 WCHAR workingDirectory[MAX_PATH + 1] = { L'\0' };
78 wcsncpy(workingDirectory, installationDir, MAX_PATH);
80 // Launch helper.exe to perform post processing (e.g. registry and log file
81 // modifications) for the update.
82 WCHAR inifile[MAX_PATH + 1] = { L'\0' };
83 wcsncpy(inifile, installationDir, MAX_PATH);
84 if (!PathAppendSafe(inifile, L"updater.ini"))
86 return FALSE;
89 WCHAR exefile[MAX_PATH + 1];
90 WCHAR exearg[MAX_PATH + 1];
91 WCHAR exeasync[10];
92 bool async = true;
93 if (!GetPrivateProfileStringW(L"PostUpdateWin", L"ExeRelPath", nullptr,
94 exefile, MAX_PATH + 1, inifile))
96 return FALSE;
99 if (!GetPrivateProfileStringW(L"PostUpdateWin", L"ExeArg", nullptr, exearg,
100 MAX_PATH + 1, inifile))
102 return FALSE;
105 if (!GetPrivateProfileStringW(L"PostUpdateWin", L"ExeAsync", L"TRUE",
106 exeasync,
107 sizeof(exeasync)/sizeof(exeasync[0]),
108 inifile))
110 return FALSE;
113 WCHAR exefullpath[MAX_PATH + 1] = { L'\0' };
114 wcsncpy(exefullpath, installationDir, MAX_PATH);
115 if (!PathAppendSafe(exefullpath, exefile))
117 return false;
120 WCHAR dlogFile[MAX_PATH + 1];
121 if (!PathGetSiblingFilePath(dlogFile, exefullpath, L"uninstall.update"))
123 return FALSE;
126 WCHAR slogFile[MAX_PATH + 1] = { L'\0' };
127 wcsncpy(slogFile, updateInfoDir, MAX_PATH);
128 if (!PathAppendSafe(slogFile, L"update.log"))
130 return FALSE;
133 WCHAR dummyArg[14] = { L'\0' };
134 wcsncpy(dummyArg, L"argv0ignored ", sizeof(dummyArg) / sizeof(dummyArg[0]) - 1);
136 size_t len = wcslen(exearg) + wcslen(dummyArg);
137 WCHAR *cmdline = (WCHAR *) malloc((len + 1) * sizeof(WCHAR));
138 if (!cmdline)
140 return FALSE;
143 wcsncpy(cmdline, dummyArg, len);
144 wcscat(cmdline, exearg);
146 if (forceSync ||
147 !_wcsnicmp(exeasync, L"false", 6) ||
148 !_wcsnicmp(exeasync, L"0", 2))
150 async = false;
153 // We want to launch the post update helper app to update the Windows
154 // registry even if there is a failure with removing the uninstall.update
155 // file or copying the update.log file.
156 CopyFileW(slogFile, dlogFile, false);
158 STARTUPINFOW si = {sizeof(si), 0};
159 si.lpDesktop = L"";
160 PROCESS_INFORMATION pi = {0};
162 bool ok;
163 if (userToken)
165 ok = CreateProcessAsUserW(userToken,
166 exefullpath,
167 cmdline,
168 nullptr, // no special security attributes
169 nullptr, // no special thread attributes
170 false, // don't inherit filehandles
171 0, // No special process creation flags
172 nullptr, // inherit my environment
173 workingDirectory,
174 &si,
175 &pi);
177 else
179 ok = CreateProcessW(exefullpath,
180 cmdline,
181 nullptr, // no special security attributes
182 nullptr, // no special thread attributes
183 false, // don't inherit filehandles
184 0, // No special process creation flags
185 nullptr, // inherit my environment
186 workingDirectory,
187 &si,
188 &pi);
190 free(cmdline);
191 if (ok)
193 if (!async)
194 WaitForSingleObject(pi.hProcess, INFINITE);
195 CloseHandle(pi.hProcess);
196 CloseHandle(pi.hThread);
198 return ok;
202 * Starts the upgrade process for update of the service if it is
203 * already installed.
205 * @param installDir the installation directory where
206 * maintenanceservice_installer.exe is located.
207 * @return TRUE if successful
209 BOOL
210 StartServiceUpdate(LPCWSTR installDir)
212 // Get a handle to the local computer SCM database
213 SC_HANDLE manager = OpenSCManager(nullptr, nullptr,
214 SC_MANAGER_ALL_ACCESS);
215 if (!manager)
217 return FALSE;
220 // Open the service
221 SC_HANDLE svc = OpenServiceW(manager, SVC_NAME,
222 SERVICE_ALL_ACCESS);
223 if (!svc)
225 CloseServiceHandle(manager);
226 return FALSE;
229 // If we reach here, then the service is installed, so
230 // proceed with upgrading it.
232 CloseServiceHandle(manager);
234 // The service exists and we opened it, get the config bytes needed
235 DWORD bytesNeeded;
236 if (!QueryServiceConfigW(svc, nullptr, 0, &bytesNeeded) &&
237 GetLastError() != ERROR_INSUFFICIENT_BUFFER)
239 CloseServiceHandle(svc);
240 return FALSE;
243 // Get the service config information, in particular we want the binary
244 // path of the service.
245 std::unique_ptr<char[]> serviceConfigBuffer = std::make_unique<char[]>(bytesNeeded);
246 if (!QueryServiceConfigW(svc,
247 reinterpret_cast<QUERY_SERVICE_CONFIGW*>(serviceConfigBuffer.get()),
248 bytesNeeded, &bytesNeeded))
250 CloseServiceHandle(svc);
251 return FALSE;
254 CloseServiceHandle(svc);
256 QUERY_SERVICE_CONFIGW &serviceConfig =
257 *reinterpret_cast<QUERY_SERVICE_CONFIGW*>(serviceConfigBuffer.get());
259 PathUnquoteSpacesW(serviceConfig.lpBinaryPathName);
261 // Obtain the temp path of the maintenance service binary
262 WCHAR tmpService[MAX_PATH + 1] = { L'\0' };
263 if (!PathGetSiblingFilePath(tmpService, serviceConfig.lpBinaryPathName,
264 L"maintenanceservice_tmp.exe"))
266 return FALSE;
269 // Get the new maintenance service path from the install dir
270 WCHAR newMaintServicePath[MAX_PATH + 1] = { L'\0' };
271 wcsncpy(newMaintServicePath, installDir, MAX_PATH);
272 PathAppendSafe(newMaintServicePath,
273 L"maintenanceservice.exe");
275 // Copy the temp file in alongside the maintenance service.
276 // This is a requirement for maintenance service upgrades.
277 if (!CopyFileW(newMaintServicePath, tmpService, FALSE))
279 return FALSE;
282 // Start the upgrade comparison process
283 STARTUPINFOW si = {0};
284 si.cb = sizeof(STARTUPINFOW);
285 // No particular desktop because no UI
286 si.lpDesktop = L"";
287 PROCESS_INFORMATION pi = {0};
288 WCHAR cmdLine[64] = { '\0' };
289 wcsncpy(cmdLine, L"dummyparam.exe upgrade",
290 sizeof(cmdLine) / sizeof(cmdLine[0]) - 1);
291 BOOL svcUpdateProcessStarted = CreateProcessW(tmpService,
292 cmdLine,
293 nullptr, nullptr, FALSE,
295 nullptr, installDir, &si, &pi);
296 if (svcUpdateProcessStarted)
298 CloseHandle(pi.hProcess);
299 CloseHandle(pi.hThread);
301 return svcUpdateProcessStarted;
304 #endif
307 * Executes a maintenance service command
309 * @param argc The total number of arguments in argv
310 * @param argv An array of null terminated strings to pass to the service,
311 * @return ERROR_SUCCESS if the service command was started.
312 * Less than 16000, a windows system error code from StartServiceW
313 * More than 20000, 20000 + the last state of the service constant if
314 * the last state is something other than stopped.
315 * 17001 if the SCM could not be opened
316 * 17002 if the service could not be opened
318 DWORD
319 StartServiceCommand(int argc, LPCWSTR* argv)
321 DWORD lastState = WaitForServiceStop(SVC_NAME, 5);
322 if (lastState != SERVICE_STOPPED)
324 return 20000 + lastState;
327 // Get a handle to the SCM database.
328 SC_HANDLE serviceManager = OpenSCManager(nullptr, nullptr,
329 SC_MANAGER_CONNECT |
330 SC_MANAGER_ENUMERATE_SERVICE);
331 if (!serviceManager)
333 return 17001;
336 // Get a handle to the service.
337 SC_HANDLE service = OpenServiceW(serviceManager,
338 SVC_NAME,
339 SERVICE_START);
340 if (!service)
342 CloseServiceHandle(serviceManager);
343 return 17002;
346 // Wait at most 5 seconds trying to start the service in case of errors
347 // like ERROR_SERVICE_DATABASE_LOCKED or ERROR_SERVICE_REQUEST_TIMEOUT.
348 const DWORD maxWaitMS = 5000;
349 DWORD currentWaitMS = 0;
350 DWORD lastError = ERROR_SUCCESS;
351 while (currentWaitMS < maxWaitMS)
353 BOOL result = StartServiceW(service, argc, argv);
354 if (result)
356 lastError = ERROR_SUCCESS;
357 break;
359 else
361 lastError = GetLastError();
363 Sleep(100);
364 currentWaitMS += 100;
366 CloseServiceHandle(service);
367 CloseServiceHandle(serviceManager);
368 return lastError;
371 #ifndef ONLY_SERVICE_LAUNCHING
374 * Launch a service initiated action for a software update with the
375 * specified arguments.
377 * @param exePath The path of the executable to run
378 * @param argc The total number of arguments in argv
379 * @param argv An array of null terminated strings to pass to the exePath,
380 * argv[0] must be the path to the updater.exe
381 * @return ERROR_SUCCESS if successful
383 DWORD
384 LaunchServiceSoftwareUpdateCommand(int argc, LPCWSTR* argv)
386 // The service command is the same as the updater.exe command line except
387 // it has 2 extra args: 1) The Path to updater.exe, and 2) the command
388 // being executed which is "software-update"
389 LPCWSTR *updaterServiceArgv = new LPCWSTR[argc + 2];
390 updaterServiceArgv[0] = L"MozillaMaintenance";
391 updaterServiceArgv[1] = L"software-update";
393 for (int i = 0; i < argc; ++i)
395 updaterServiceArgv[i + 2] = argv[i];
398 // Execute the service command by starting the service with
399 // the passed in arguments.
400 DWORD ret = StartServiceCommand(argc + 2, updaterServiceArgv);
401 delete[] updaterServiceArgv;
402 return ret;
406 * Joins a base directory path with a filename.
408 * @param base The base directory path of size MAX_PATH + 1
409 * @param extra The filename to append
410 * @return TRUE if the file name was successful appended to base
412 BOOL
413 PathAppendSafe(LPWSTR base, LPCWSTR extra)
415 if (wcslen(base) + wcslen(extra) >= MAX_PATH)
417 return FALSE;
420 return PathAppendW(base, extra);
424 * Sets update.status to pending so that the next startup will not use
425 * the service and instead will attempt an update the with a UAC prompt.
427 * @param updateDirPath The path of the update directory
428 * @return TRUE if successful
430 BOOL
431 WriteStatusPending(LPCWSTR updateDirPath)
433 WCHAR updateStatusFilePath[MAX_PATH + 1] = { L'\0' };
434 wcsncpy(updateStatusFilePath, updateDirPath, MAX_PATH);
435 if (!PathAppendSafe(updateStatusFilePath, L"update.status"))
437 return FALSE;
440 const char pending[] = "pending";
441 HANDLE statusFile = CreateFileW(updateStatusFilePath, GENERIC_WRITE, 0,
442 nullptr, CREATE_ALWAYS, 0, nullptr);
443 if (statusFile == INVALID_HANDLE_VALUE)
445 return FALSE;
448 DWORD wrote;
449 BOOL ok = WriteFile(statusFile, pending,
450 sizeof(pending) - 1, &wrote, nullptr);
451 CloseHandle(statusFile);
452 return ok && (wrote == sizeof(pending) - 1);
456 * Sets update.status to a specific failure code
458 * @param updateDirPath The path of the update directory
459 * @return TRUE if successful
461 BOOL
462 WriteStatusFailure(LPCWSTR updateDirPath, int errorCode)
464 WCHAR updateStatusFilePath[MAX_PATH + 1] = { L'\0' };
465 wcsncpy(updateStatusFilePath, updateDirPath, MAX_PATH);
466 if (!PathAppendSafe(updateStatusFilePath, L"update.status"))
468 return FALSE;
471 HANDLE statusFile = CreateFileW(updateStatusFilePath, GENERIC_WRITE, 0,
472 nullptr, CREATE_ALWAYS, 0, nullptr);
473 if (statusFile == INVALID_HANDLE_VALUE)
475 return FALSE;
477 char failure[32];
478 sprintf(failure, "failed: %d", errorCode);
480 DWORD toWrite = strlen(failure);
481 DWORD wrote;
482 BOOL ok = WriteFile(statusFile, failure,
483 toWrite, &wrote, nullptr);
484 CloseHandle(statusFile);
485 return ok && wrote == toWrite;
488 #endif
491 * Waits for a service to enter a stopped state.
492 * This function does not stop the service, it just blocks until the service
493 * is stopped.
495 * @param serviceName The service to wait for.
496 * @param maxWaitSeconds The maximum number of seconds to wait
497 * @return state of the service after a timeout or when stopped.
498 * A value of 255 is returned for an error. Typical values are:
499 * SERVICE_STOPPED 0x00000001
500 * SERVICE_START_PENDING 0x00000002
501 * SERVICE_STOP_PENDING 0x00000003
502 * SERVICE_RUNNING 0x00000004
503 * SERVICE_CONTINUE_PENDING 0x00000005
504 * SERVICE_PAUSE_PENDING 0x00000006
505 * SERVICE_PAUSED 0x00000007
506 * last status not set 0x000000CF
507 * Could no query status 0x000000DF
508 * Could not open service, access denied 0x000000EB
509 * Could not open service, invalid handle 0x000000EC
510 * Could not open service, invalid name 0x000000ED
511 * Could not open service, does not exist 0x000000EE
512 * Could not open service, other error 0x000000EF
513 * Could not open SCM, access denied 0x000000FD
514 * Could not open SCM, database does not exist 0x000000FE;
515 * Could not open SCM, other error 0x000000FF;
516 * Note: The strange choice of error codes above SERVICE_PAUSED are chosen
517 * in case Windows comes out with other service stats higher than 7, they
518 * would likely call it 8 and above. JS code that uses this in TestAUSHelper
519 * only handles values up to 255 so that's why we don't use GetLastError
520 * directly.
522 DWORD
523 WaitForServiceStop(LPCWSTR serviceName, DWORD maxWaitSeconds)
525 // 0x000000CF is defined above to be not set
526 DWORD lastServiceState = 0x000000CF;
528 // Get a handle to the SCM database.
529 SC_HANDLE serviceManager = OpenSCManager(nullptr, nullptr,
530 SC_MANAGER_CONNECT |
531 SC_MANAGER_ENUMERATE_SERVICE);
532 if (!serviceManager)
534 DWORD lastError = GetLastError();
535 switch (lastError)
537 case ERROR_ACCESS_DENIED:
538 return 0x000000FD;
539 case ERROR_DATABASE_DOES_NOT_EXIST:
540 return 0x000000FE;
541 default:
542 return 0x000000FF;
546 // Get a handle to the service.
547 SC_HANDLE service = OpenServiceW(serviceManager,
548 serviceName,
549 SERVICE_QUERY_STATUS);
550 if (!service)
552 DWORD lastError = GetLastError();
553 CloseServiceHandle(serviceManager);
554 switch (lastError)
556 case ERROR_ACCESS_DENIED:
557 return 0x000000EB;
558 case ERROR_INVALID_HANDLE:
559 return 0x000000EC;
560 case ERROR_INVALID_NAME:
561 return 0x000000ED;
562 case ERROR_SERVICE_DOES_NOT_EXIST:
563 return 0x000000EE;
564 default:
565 return 0x000000EF;
569 DWORD currentWaitMS = 0;
570 SERVICE_STATUS_PROCESS ssp;
571 ssp.dwCurrentState = lastServiceState;
572 while (currentWaitMS < maxWaitSeconds * 1000)
574 DWORD bytesNeeded;
575 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
576 sizeof(SERVICE_STATUS_PROCESS), &bytesNeeded))
578 DWORD lastError = GetLastError();
579 switch (lastError)
581 case ERROR_INVALID_HANDLE:
582 ssp.dwCurrentState = 0x000000D9;
583 break;
584 case ERROR_ACCESS_DENIED:
585 ssp.dwCurrentState = 0x000000DA;
586 break;
587 case ERROR_INSUFFICIENT_BUFFER:
588 ssp.dwCurrentState = 0x000000DB;
589 break;
590 case ERROR_INVALID_PARAMETER:
591 ssp.dwCurrentState = 0x000000DC;
592 break;
593 case ERROR_INVALID_LEVEL:
594 ssp.dwCurrentState = 0x000000DD;
595 break;
596 case ERROR_SHUTDOWN_IN_PROGRESS:
597 ssp.dwCurrentState = 0x000000DE;
598 break;
599 // These 3 errors can occur when the service is not yet stopped but
600 // it is stopping.
601 case ERROR_INVALID_SERVICE_CONTROL:
602 case ERROR_SERVICE_CANNOT_ACCEPT_CTRL:
603 case ERROR_SERVICE_NOT_ACTIVE:
604 currentWaitMS += 50;
605 Sleep(50);
606 continue;
607 default:
608 ssp.dwCurrentState = 0x000000DF;
611 // We couldn't query the status so just break out
612 break;
615 // The service is already in use.
616 if (ssp.dwCurrentState == SERVICE_STOPPED)
618 break;
620 currentWaitMS += 50;
621 Sleep(50);
624 lastServiceState = ssp.dwCurrentState;
625 CloseServiceHandle(service);
626 CloseServiceHandle(serviceManager);
627 return lastServiceState;
630 #ifndef ONLY_SERVICE_LAUNCHING
633 * Determines if there is at least one process running for the specified
634 * application. A match will be found across any session for any user.
636 * @param process The process to check for existence
637 * @return ERROR_NOT_FOUND if the process was not found
638 * ERROR_SUCCESS if the process was found and there were no errors
639 * Other Win32 system error code for other errors
641 DWORD
642 IsProcessRunning(LPCWSTR filename)
644 // Take a snapshot of all processes in the system.
645 HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
646 if (INVALID_HANDLE_VALUE == snapshot)
648 return GetLastError();
651 PROCESSENTRY32W processEntry;
652 processEntry.dwSize = sizeof(PROCESSENTRY32W);
653 if (!Process32FirstW(snapshot, &processEntry))
655 DWORD lastError = GetLastError();
656 CloseHandle(snapshot);
657 return lastError;
662 if (wcsicmp(filename, processEntry.szExeFile) == 0)
664 CloseHandle(snapshot);
665 return ERROR_SUCCESS;
668 while (Process32NextW(snapshot, &processEntry));
669 CloseHandle(snapshot);
670 return ERROR_NOT_FOUND;
674 * Waits for the specified application to exit.
676 * @param filename The application to wait for.
677 * @param maxSeconds The maximum amount of seconds to wait for all
678 * instances of the application to exit.
679 * @return ERROR_SUCCESS if no instances of the application exist
680 * WAIT_TIMEOUT if the process is still running after maxSeconds.
681 * Any other Win32 system error code.
683 DWORD
684 WaitForProcessExit(LPCWSTR filename, DWORD maxSeconds)
686 DWORD applicationRunningError = WAIT_TIMEOUT;
687 for (DWORD i = 0; i < maxSeconds; i++)
689 applicationRunningError = IsProcessRunning(filename);
690 if (ERROR_NOT_FOUND == applicationRunningError)
692 return ERROR_SUCCESS;
694 Sleep(1000);
697 if (ERROR_SUCCESS == applicationRunningError)
699 return WAIT_TIMEOUT;
702 return applicationRunningError;
706 * Determines if the fallback key exists or not
708 * @return TRUE if the fallback key exists and there was no error checking
710 BOOL
711 DoesFallbackKeyExist()
713 HKEY testOnlyFallbackKey;
714 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
715 TEST_ONLY_FALLBACK_KEY_PATH, 0,
716 KEY_READ | KEY_WOW64_64KEY,
717 &testOnlyFallbackKey) != ERROR_SUCCESS)
719 return FALSE;
722 RegCloseKey(testOnlyFallbackKey);
723 return TRUE;
726 #endif
729 * Determines if the file system for the specified file handle is local
730 * @param file path to check the filesystem type for, must be at most MAX_PATH
731 * @param isLocal out parameter which will hold TRUE if the drive is local
732 * @return TRUE if the call succeeded
734 BOOL
735 IsLocalFile(LPCWSTR file, BOOL &isLocal)
737 WCHAR rootPath[MAX_PATH + 1] = { L'\0' };
738 if (wcslen(file) > MAX_PATH)
740 return FALSE;
743 wcsncpy(rootPath, file, MAX_PATH);
744 PathStripToRootW(rootPath);
745 isLocal = GetDriveTypeW(rootPath) == DRIVE_FIXED;
746 return TRUE;
751 * Determines the DWORD value of a registry key value
753 * @param key The base key to where the value name exists
754 * @param valueName The name of the value
755 * @param retValue Out parameter which will hold the value
756 * @return TRUE on success
758 static BOOL
759 GetDWORDValue(HKEY key, LPCWSTR valueName, DWORD &retValue)
761 DWORD regDWORDValueSize = sizeof(DWORD);
762 LONG retCode = RegQueryValueExW(key, valueName, 0, nullptr,
763 reinterpret_cast<LPBYTE>(&retValue),
764 &regDWORDValueSize);
765 return ERROR_SUCCESS == retCode;
769 * Determines if the system's elevation type allows
770 * unprompted elevation.
772 * @param isUnpromptedElevation Out parameter which specifies if unprompted
773 * elevation is allowed.
774 * @return TRUE if the user can actually elevate and the value was obtained
775 * successfully.
777 BOOL
778 IsUnpromptedElevation(BOOL &isUnpromptedElevation)
780 if (!UACHelper::CanUserElevate())
782 return FALSE;
785 LPCWSTR UACBaseRegKey =
786 L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System";
787 HKEY baseKey;
788 LONG retCode = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
789 UACBaseRegKey, 0,
790 KEY_READ, &baseKey);
791 if (retCode != ERROR_SUCCESS)
793 return FALSE;
796 DWORD consent;
797 DWORD secureDesktop = 0;
798 BOOL success = GetDWORDValue(baseKey, L"ConsentPromptBehaviorAdmin",
799 consent);
800 success = success &&
801 GetDWORDValue(baseKey, L"PromptOnSecureDesktop", secureDesktop);
802 isUnpromptedElevation = !consent && !secureDesktop;
804 RegCloseKey(baseKey);
805 return success;
807 #endif