michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifdef MOZ_METRO michael@0: // Needed for COM calls to launch Metro applications michael@0: #undef WINVER michael@0: #undef _WIN32_WINNT michael@0: #define WINVER 0x602 michael@0: #define _WIN32_WINNT 0x602 michael@0: #include michael@0: #include michael@0: #pragma comment(lib, "ole32.lib") michael@0: #endif michael@0: michael@0: #include michael@0: michael@0: // Needed for CreateToolhelp32Snapshot michael@0: #include michael@0: #ifndef ONLY_SERVICE_LAUNCHING michael@0: michael@0: #include michael@0: #include "shlobj.h" michael@0: #include "updatehelper.h" michael@0: #include "uachelper.h" michael@0: #include "pathhash.h" michael@0: #include "mozilla/Scoped.h" michael@0: michael@0: // Needed for PathAppendW michael@0: #include michael@0: michael@0: WCHAR* MakeCommandLine(int argc, WCHAR **argv); michael@0: BOOL PathAppendSafe(LPWSTR base, LPCWSTR extra); michael@0: michael@0: /** michael@0: * Obtains the path of a file in the same directory as the specified file. michael@0: * michael@0: * @param destinationBuffer A buffer of size MAX_PATH + 1 to store the result. michael@0: * @param siblingFIlePath The path of another file in the same directory michael@0: * @param newFileName The filename of another file in the same directory michael@0: * @return TRUE if successful michael@0: */ michael@0: BOOL michael@0: PathGetSiblingFilePath(LPWSTR destinationBuffer, michael@0: LPCWSTR siblingFilePath, michael@0: LPCWSTR newFileName) michael@0: { michael@0: if (wcslen(siblingFilePath) >= MAX_PATH) { michael@0: return FALSE; michael@0: } michael@0: michael@0: wcsncpy(destinationBuffer, siblingFilePath, MAX_PATH); michael@0: if (!PathRemoveFileSpecW(destinationBuffer)) { michael@0: return FALSE; michael@0: } michael@0: michael@0: if (wcslen(destinationBuffer) + wcslen(newFileName) >= MAX_PATH) { michael@0: return FALSE; michael@0: } michael@0: michael@0: return PathAppendSafe(destinationBuffer, newFileName); michael@0: } michael@0: michael@0: /** michael@0: * Launch the post update application as the specified user (helper.exe). michael@0: * It takes in the path of the callback application to calculate the path michael@0: * of helper.exe. For service updates this is called from both the system michael@0: * account and the current user account. michael@0: * michael@0: * @param installationDir The path to the callback application binary. michael@0: * @param updateInfoDir The directory where update info is stored. michael@0: * @param forceSync If true even if the ini file specifies async, the michael@0: * process will wait for termination of PostUpdate. michael@0: * @param userToken The user token to run as, if nullptr the current michael@0: * user will be used. michael@0: * @return TRUE if there was no error starting the process. michael@0: */ michael@0: BOOL michael@0: LaunchWinPostProcess(const WCHAR *installationDir, michael@0: const WCHAR *updateInfoDir, michael@0: bool forceSync, michael@0: HANDLE userToken) michael@0: { michael@0: WCHAR workingDirectory[MAX_PATH + 1] = { L'\0' }; michael@0: wcsncpy(workingDirectory, installationDir, MAX_PATH); michael@0: michael@0: // Launch helper.exe to perform post processing (e.g. registry and log file michael@0: // modifications) for the update. michael@0: WCHAR inifile[MAX_PATH + 1] = { L'\0' }; michael@0: wcsncpy(inifile, installationDir, MAX_PATH); michael@0: if (!PathAppendSafe(inifile, L"updater.ini")) { michael@0: return FALSE; michael@0: } michael@0: michael@0: WCHAR exefile[MAX_PATH + 1]; michael@0: WCHAR exearg[MAX_PATH + 1]; michael@0: WCHAR exeasync[10]; michael@0: bool async = true; michael@0: if (!GetPrivateProfileStringW(L"PostUpdateWin", L"ExeRelPath", nullptr, michael@0: exefile, MAX_PATH + 1, inifile)) { michael@0: return FALSE; michael@0: } michael@0: michael@0: if (!GetPrivateProfileStringW(L"PostUpdateWin", L"ExeArg", nullptr, exearg, michael@0: MAX_PATH + 1, inifile)) { michael@0: return FALSE; michael@0: } michael@0: michael@0: if (!GetPrivateProfileStringW(L"PostUpdateWin", L"ExeAsync", L"TRUE", michael@0: exeasync, michael@0: sizeof(exeasync)/sizeof(exeasync[0]), michael@0: inifile)) { michael@0: return FALSE; michael@0: } michael@0: michael@0: WCHAR exefullpath[MAX_PATH + 1] = { L'\0' }; michael@0: wcsncpy(exefullpath, installationDir, MAX_PATH); michael@0: if (!PathAppendSafe(exefullpath, exefile)) { michael@0: return false; michael@0: } michael@0: michael@0: WCHAR dlogFile[MAX_PATH + 1]; michael@0: if (!PathGetSiblingFilePath(dlogFile, exefullpath, L"uninstall.update")) { michael@0: return FALSE; michael@0: } michael@0: michael@0: WCHAR slogFile[MAX_PATH + 1] = { L'\0' }; michael@0: wcsncpy(slogFile, updateInfoDir, MAX_PATH); michael@0: if (!PathAppendSafe(slogFile, L"update.log")) { michael@0: return FALSE; michael@0: } michael@0: michael@0: WCHAR dummyArg[14] = { L'\0' }; michael@0: wcsncpy(dummyArg, L"argv0ignored ", sizeof(dummyArg) / sizeof(dummyArg[0]) - 1); michael@0: michael@0: size_t len = wcslen(exearg) + wcslen(dummyArg); michael@0: WCHAR *cmdline = (WCHAR *) malloc((len + 1) * sizeof(WCHAR)); michael@0: if (!cmdline) { michael@0: return FALSE; michael@0: } michael@0: michael@0: wcsncpy(cmdline, dummyArg, len); michael@0: wcscat(cmdline, exearg); michael@0: michael@0: if (forceSync || michael@0: !_wcsnicmp(exeasync, L"false", 6) || michael@0: !_wcsnicmp(exeasync, L"0", 2)) { michael@0: async = false; michael@0: } michael@0: michael@0: // We want to launch the post update helper app to update the Windows michael@0: // registry even if there is a failure with removing the uninstall.update michael@0: // file or copying the update.log file. michael@0: CopyFileW(slogFile, dlogFile, false); michael@0: michael@0: STARTUPINFOW si = {sizeof(si), 0}; michael@0: si.lpDesktop = L""; michael@0: PROCESS_INFORMATION pi = {0}; michael@0: michael@0: bool ok; michael@0: if (userToken) { michael@0: ok = CreateProcessAsUserW(userToken, michael@0: exefullpath, michael@0: cmdline, michael@0: nullptr, // no special security attributes michael@0: nullptr, // no special thread attributes michael@0: false, // don't inherit filehandles michael@0: 0, // No special process creation flags michael@0: nullptr, // inherit my environment michael@0: workingDirectory, michael@0: &si, michael@0: &pi); michael@0: } else { michael@0: ok = CreateProcessW(exefullpath, michael@0: cmdline, michael@0: nullptr, // no special security attributes michael@0: nullptr, // no special thread attributes michael@0: false, // don't inherit filehandles michael@0: 0, // No special process creation flags michael@0: nullptr, // inherit my environment michael@0: workingDirectory, michael@0: &si, michael@0: &pi); michael@0: } michael@0: free(cmdline); michael@0: if (ok) { michael@0: if (!async) michael@0: WaitForSingleObject(pi.hProcess, INFINITE); michael@0: CloseHandle(pi.hProcess); michael@0: CloseHandle(pi.hThread); michael@0: } michael@0: return ok; michael@0: } michael@0: michael@0: /** michael@0: * Starts the upgrade process for update of the service if it is michael@0: * already installed. michael@0: * michael@0: * @param installDir the installation directory where michael@0: * maintenanceservice_installer.exe is located. michael@0: * @return TRUE if successful michael@0: */ michael@0: BOOL michael@0: StartServiceUpdate(LPCWSTR installDir) michael@0: { michael@0: // Get a handle to the local computer SCM database michael@0: SC_HANDLE manager = OpenSCManager(nullptr, nullptr, michael@0: SC_MANAGER_ALL_ACCESS); michael@0: if (!manager) { michael@0: return FALSE; michael@0: } michael@0: michael@0: // Open the service michael@0: SC_HANDLE svc = OpenServiceW(manager, SVC_NAME, michael@0: SERVICE_ALL_ACCESS); michael@0: if (!svc) { michael@0: CloseServiceHandle(manager); michael@0: return FALSE; michael@0: } michael@0: michael@0: // If we reach here, then the service is installed, so michael@0: // proceed with upgrading it. michael@0: michael@0: CloseServiceHandle(manager); michael@0: michael@0: // The service exists and we opened it, get the config bytes needed michael@0: DWORD bytesNeeded; michael@0: if (!QueryServiceConfigW(svc, nullptr, 0, &bytesNeeded) && michael@0: GetLastError() != ERROR_INSUFFICIENT_BUFFER) { michael@0: CloseServiceHandle(svc); michael@0: return FALSE; michael@0: } michael@0: michael@0: // Get the service config information, in particular we want the binary michael@0: // path of the service. michael@0: mozilla::ScopedDeleteArray serviceConfigBuffer(new char[bytesNeeded]); michael@0: if (!QueryServiceConfigW(svc, michael@0: reinterpret_cast(serviceConfigBuffer.get()), michael@0: bytesNeeded, &bytesNeeded)) { michael@0: CloseServiceHandle(svc); michael@0: return FALSE; michael@0: } michael@0: michael@0: CloseServiceHandle(svc); michael@0: michael@0: QUERY_SERVICE_CONFIGW &serviceConfig = michael@0: *reinterpret_cast(serviceConfigBuffer.get()); michael@0: michael@0: PathUnquoteSpacesW(serviceConfig.lpBinaryPathName); michael@0: michael@0: // Obtain the temp path of the maintenance service binary michael@0: WCHAR tmpService[MAX_PATH + 1] = { L'\0' }; michael@0: if (!PathGetSiblingFilePath(tmpService, serviceConfig.lpBinaryPathName, michael@0: L"maintenanceservice_tmp.exe")) { michael@0: return FALSE; michael@0: } michael@0: michael@0: // Get the new maintenance service path from the install dir michael@0: WCHAR newMaintServicePath[MAX_PATH + 1] = { L'\0' }; michael@0: wcsncpy(newMaintServicePath, installDir, MAX_PATH); michael@0: PathAppendSafe(newMaintServicePath, michael@0: L"maintenanceservice.exe"); michael@0: michael@0: // Copy the temp file in alongside the maintenace service. michael@0: // This is a requirement for maintenance service upgrades. michael@0: if (!CopyFileW(newMaintServicePath, tmpService, FALSE)) { michael@0: return FALSE; michael@0: } michael@0: michael@0: // Start the upgrade comparison process michael@0: STARTUPINFOW si = {0}; michael@0: si.cb = sizeof(STARTUPINFOW); michael@0: // No particular desktop because no UI michael@0: si.lpDesktop = L""; michael@0: PROCESS_INFORMATION pi = {0}; michael@0: WCHAR cmdLine[64] = { '\0' }; michael@0: wcsncpy(cmdLine, L"dummyparam.exe upgrade", michael@0: sizeof(cmdLine) / sizeof(cmdLine[0]) - 1); michael@0: BOOL svcUpdateProcessStarted = CreateProcessW(tmpService, michael@0: cmdLine, michael@0: nullptr, nullptr, FALSE, michael@0: 0, michael@0: nullptr, installDir, &si, &pi); michael@0: if (svcUpdateProcessStarted) { michael@0: CloseHandle(pi.hProcess); michael@0: CloseHandle(pi.hThread); michael@0: } michael@0: return svcUpdateProcessStarted; michael@0: } michael@0: michael@0: #endif michael@0: michael@0: /** michael@0: * Executes a maintenance service command michael@0: * michael@0: * @param argc The total number of arguments in argv michael@0: * @param argv An array of null terminated strings to pass to the service, michael@0: * @return ERROR_SUCCESS if the service command was started. michael@0: * Less than 16000, a windows system error code from StartServiceW michael@0: * More than 20000, 20000 + the last state of the service constant if michael@0: * the last state is something other than stopped. michael@0: * 17001 if the SCM could not be opened michael@0: * 17002 if the service could not be opened michael@0: */ michael@0: DWORD michael@0: StartServiceCommand(int argc, LPCWSTR* argv) michael@0: { michael@0: DWORD lastState = WaitForServiceStop(SVC_NAME, 5); michael@0: if (lastState != SERVICE_STOPPED) { michael@0: return 20000 + lastState; michael@0: } michael@0: michael@0: // Get a handle to the SCM database. michael@0: SC_HANDLE serviceManager = OpenSCManager(nullptr, nullptr, michael@0: SC_MANAGER_CONNECT | michael@0: SC_MANAGER_ENUMERATE_SERVICE); michael@0: if (!serviceManager) { michael@0: return 17001; michael@0: } michael@0: michael@0: // Get a handle to the service. michael@0: SC_HANDLE service = OpenServiceW(serviceManager, michael@0: SVC_NAME, michael@0: SERVICE_START); michael@0: if (!service) { michael@0: CloseServiceHandle(serviceManager); michael@0: return 17002; michael@0: } michael@0: michael@0: // Wait at most 5 seconds trying to start the service in case of errors michael@0: // like ERROR_SERVICE_DATABASE_LOCKED or ERROR_SERVICE_REQUEST_TIMEOUT. michael@0: const DWORD maxWaitMS = 5000; michael@0: DWORD currentWaitMS = 0; michael@0: DWORD lastError = ERROR_SUCCESS; michael@0: while (currentWaitMS < maxWaitMS) { michael@0: BOOL result = StartServiceW(service, argc, argv); michael@0: if (result) { michael@0: lastError = ERROR_SUCCESS; michael@0: break; michael@0: } else { michael@0: lastError = GetLastError(); michael@0: } michael@0: Sleep(100); michael@0: currentWaitMS += 100; michael@0: } michael@0: CloseServiceHandle(service); michael@0: CloseServiceHandle(serviceManager); michael@0: return lastError; michael@0: } michael@0: michael@0: #ifndef ONLY_SERVICE_LAUNCHING michael@0: michael@0: /** michael@0: * Launch a service initiated action for a software update with the michael@0: * specified arguments. michael@0: * michael@0: * @param exePath The path of the executable to run michael@0: * @param argc The total number of arguments in argv michael@0: * @param argv An array of null terminated strings to pass to the exePath, michael@0: * argv[0] must be the path to the updater.exe michael@0: * @return ERROR_SUCCESS if successful michael@0: */ michael@0: DWORD michael@0: LaunchServiceSoftwareUpdateCommand(int argc, LPCWSTR* argv) michael@0: { michael@0: // The service command is the same as the updater.exe command line except michael@0: // it has 2 extra args: 1) The Path to udpater.exe, and 2) the command michael@0: // being executed which is "software-update" michael@0: LPCWSTR *updaterServiceArgv = new LPCWSTR[argc + 2]; michael@0: updaterServiceArgv[0] = L"MozillaMaintenance"; michael@0: updaterServiceArgv[1] = L"software-update"; michael@0: michael@0: for (int i = 0; i < argc; ++i) { michael@0: updaterServiceArgv[i + 2] = argv[i]; michael@0: } michael@0: michael@0: // Execute the service command by starting the service with michael@0: // the passed in arguments. michael@0: DWORD ret = StartServiceCommand(argc + 2, updaterServiceArgv); michael@0: delete[] updaterServiceArgv; michael@0: return ret; michael@0: } michael@0: michael@0: /** michael@0: * Joins a base directory path with a filename. michael@0: * michael@0: * @param base The base directory path of size MAX_PATH + 1 michael@0: * @param extra The filename to append michael@0: * @return TRUE if the file name was successful appended to base michael@0: */ michael@0: BOOL michael@0: PathAppendSafe(LPWSTR base, LPCWSTR extra) michael@0: { michael@0: if (wcslen(base) + wcslen(extra) >= MAX_PATH) { michael@0: return FALSE; michael@0: } michael@0: michael@0: return PathAppendW(base, extra); michael@0: } michael@0: michael@0: /** michael@0: * Sets update.status to pending so that the next startup will not use michael@0: * the service and instead will attempt an update the with a UAC prompt. michael@0: * michael@0: * @param updateDirPath The path of the update directory michael@0: * @return TRUE if successful michael@0: */ michael@0: BOOL michael@0: WriteStatusPending(LPCWSTR updateDirPath) michael@0: { michael@0: WCHAR updateStatusFilePath[MAX_PATH + 1] = { L'\0' }; michael@0: wcsncpy(updateStatusFilePath, updateDirPath, MAX_PATH); michael@0: if (!PathAppendSafe(updateStatusFilePath, L"update.status")) { michael@0: return FALSE; michael@0: } michael@0: michael@0: const char pending[] = "pending"; michael@0: HANDLE statusFile = CreateFileW(updateStatusFilePath, GENERIC_WRITE, 0, michael@0: nullptr, CREATE_ALWAYS, 0, nullptr); michael@0: if (statusFile == INVALID_HANDLE_VALUE) { michael@0: return FALSE; michael@0: } michael@0: michael@0: DWORD wrote; michael@0: BOOL ok = WriteFile(statusFile, pending, michael@0: sizeof(pending) - 1, &wrote, nullptr); michael@0: CloseHandle(statusFile); michael@0: return ok && (wrote == sizeof(pending) - 1); michael@0: } michael@0: michael@0: /** michael@0: * Sets update.status to a specific failure code michael@0: * michael@0: * @param updateDirPath The path of the update directory michael@0: * @return TRUE if successful michael@0: */ michael@0: BOOL michael@0: WriteStatusFailure(LPCWSTR updateDirPath, int errorCode) michael@0: { michael@0: WCHAR updateStatusFilePath[MAX_PATH + 1] = { L'\0' }; michael@0: wcsncpy(updateStatusFilePath, updateDirPath, MAX_PATH); michael@0: if (!PathAppendSafe(updateStatusFilePath, L"update.status")) { michael@0: return FALSE; michael@0: } michael@0: michael@0: HANDLE statusFile = CreateFileW(updateStatusFilePath, GENERIC_WRITE, 0, michael@0: nullptr, CREATE_ALWAYS, 0, nullptr); michael@0: if (statusFile == INVALID_HANDLE_VALUE) { michael@0: return FALSE; michael@0: } michael@0: char failure[32]; michael@0: sprintf(failure, "failed: %d", errorCode); michael@0: michael@0: DWORD toWrite = strlen(failure); michael@0: DWORD wrote; michael@0: BOOL ok = WriteFile(statusFile, failure, michael@0: toWrite, &wrote, nullptr); michael@0: CloseHandle(statusFile); michael@0: return ok && wrote == toWrite; michael@0: } michael@0: michael@0: #endif michael@0: michael@0: /** michael@0: * Waits for a service to enter a stopped state. michael@0: * This function does not stop the service, it just blocks until the service michael@0: * is stopped. michael@0: * michael@0: * @param serviceName The service to wait for. michael@0: * @param maxWaitSeconds The maximum number of seconds to wait michael@0: * @return state of the service after a timeout or when stopped. michael@0: * A value of 255 is returned for an error. Typical values are: michael@0: * SERVICE_STOPPED 0x00000001 michael@0: * SERVICE_START_PENDING 0x00000002 michael@0: * SERVICE_STOP_PENDING 0x00000003 michael@0: * SERVICE_RUNNING 0x00000004 michael@0: * SERVICE_CONTINUE_PENDING 0x00000005 michael@0: * SERVICE_PAUSE_PENDING 0x00000006 michael@0: * SERVICE_PAUSED 0x00000007 michael@0: * last status not set 0x000000CF michael@0: * Could no query status 0x000000DF michael@0: * Could not open service, access denied 0x000000EB michael@0: * Could not open service, invalid handle 0x000000EC michael@0: * Could not open service, invalid name 0x000000ED michael@0: * Could not open service, does not exist 0x000000EE michael@0: * Could not open service, other error 0x000000EF michael@0: * Could not open SCM, access denied 0x000000FD michael@0: * Could not open SCM, database does not exist 0x000000FE; michael@0: * Could not open SCM, other error 0x000000FF; michael@0: * Note: The strange choice of error codes above SERVICE_PAUSED are chosen michael@0: * in case Windows comes out with other service stats higher than 7, they michael@0: * would likely call it 8 and above. JS code that uses this in TestAUSHelper michael@0: * only handles values up to 255 so that's why we don't use GetLastError michael@0: * directly. michael@0: */ michael@0: DWORD michael@0: WaitForServiceStop(LPCWSTR serviceName, DWORD maxWaitSeconds) michael@0: { michael@0: // 0x000000CF is defined above to be not set michael@0: DWORD lastServiceState = 0x000000CF; michael@0: michael@0: // Get a handle to the SCM database. michael@0: SC_HANDLE serviceManager = OpenSCManager(nullptr, nullptr, michael@0: SC_MANAGER_CONNECT | michael@0: SC_MANAGER_ENUMERATE_SERVICE); michael@0: if (!serviceManager) { michael@0: DWORD lastError = GetLastError(); michael@0: switch(lastError) { michael@0: case ERROR_ACCESS_DENIED: michael@0: return 0x000000FD; michael@0: case ERROR_DATABASE_DOES_NOT_EXIST: michael@0: return 0x000000FE; michael@0: default: michael@0: return 0x000000FF; michael@0: } michael@0: } michael@0: michael@0: // Get a handle to the service. michael@0: SC_HANDLE service = OpenServiceW(serviceManager, michael@0: serviceName, michael@0: SERVICE_QUERY_STATUS); michael@0: if (!service) { michael@0: DWORD lastError = GetLastError(); michael@0: CloseServiceHandle(serviceManager); michael@0: switch(lastError) { michael@0: case ERROR_ACCESS_DENIED: michael@0: return 0x000000EB; michael@0: case ERROR_INVALID_HANDLE: michael@0: return 0x000000EC; michael@0: case ERROR_INVALID_NAME: michael@0: return 0x000000ED; michael@0: case ERROR_SERVICE_DOES_NOT_EXIST: michael@0: return 0x000000EE; michael@0: default: michael@0: return 0x000000EF; michael@0: } michael@0: } michael@0: michael@0: DWORD currentWaitMS = 0; michael@0: SERVICE_STATUS_PROCESS ssp; michael@0: ssp.dwCurrentState = lastServiceState; michael@0: while (currentWaitMS < maxWaitSeconds * 1000) { michael@0: DWORD bytesNeeded; michael@0: if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp, michael@0: sizeof(SERVICE_STATUS_PROCESS), &bytesNeeded)) { michael@0: DWORD lastError = GetLastError(); michael@0: switch (lastError) { michael@0: case ERROR_INVALID_HANDLE: michael@0: ssp.dwCurrentState = 0x000000D9; michael@0: break; michael@0: case ERROR_ACCESS_DENIED: michael@0: ssp.dwCurrentState = 0x000000DA; michael@0: break; michael@0: case ERROR_INSUFFICIENT_BUFFER: michael@0: ssp.dwCurrentState = 0x000000DB; michael@0: break; michael@0: case ERROR_INVALID_PARAMETER: michael@0: ssp.dwCurrentState = 0x000000DC; michael@0: break; michael@0: case ERROR_INVALID_LEVEL: michael@0: ssp.dwCurrentState = 0x000000DD; michael@0: break; michael@0: case ERROR_SHUTDOWN_IN_PROGRESS: michael@0: ssp.dwCurrentState = 0x000000DE; michael@0: break; michael@0: // These 3 errors can occur when the service is not yet stopped but michael@0: // it is stopping. michael@0: case ERROR_INVALID_SERVICE_CONTROL: michael@0: case ERROR_SERVICE_CANNOT_ACCEPT_CTRL: michael@0: case ERROR_SERVICE_NOT_ACTIVE: michael@0: currentWaitMS += 50; michael@0: Sleep(50); michael@0: continue; michael@0: default: michael@0: ssp.dwCurrentState = 0x000000DF; michael@0: } michael@0: michael@0: // We couldn't query the status so just break out michael@0: break; michael@0: } michael@0: michael@0: // The service is already in use. michael@0: if (ssp.dwCurrentState == SERVICE_STOPPED) { michael@0: break; michael@0: } michael@0: currentWaitMS += 50; michael@0: Sleep(50); michael@0: } michael@0: michael@0: lastServiceState = ssp.dwCurrentState; michael@0: CloseServiceHandle(service); michael@0: CloseServiceHandle(serviceManager); michael@0: return lastServiceState; michael@0: } michael@0: michael@0: #ifndef ONLY_SERVICE_LAUNCHING michael@0: michael@0: /** michael@0: * Determines if there is at least one process running for the specified michael@0: * application. A match will be found across any session for any user. michael@0: * michael@0: * @param process The process to check for existance michael@0: * @return ERROR_NOT_FOUND if the process was not found michael@0: * ERROR_SUCCESS if the process was found and there were no errors michael@0: * Other Win32 system error code for other errors michael@0: **/ michael@0: DWORD michael@0: IsProcessRunning(LPCWSTR filename) michael@0: { michael@0: // Take a snapshot of all processes in the system. michael@0: HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); michael@0: if (INVALID_HANDLE_VALUE == snapshot) { michael@0: return GetLastError(); michael@0: } michael@0: michael@0: PROCESSENTRY32W processEntry; michael@0: processEntry.dwSize = sizeof(PROCESSENTRY32W); michael@0: if (!Process32FirstW(snapshot, &processEntry)) { michael@0: DWORD lastError = GetLastError(); michael@0: CloseHandle(snapshot); michael@0: return lastError; michael@0: } michael@0: michael@0: do { michael@0: if (wcsicmp(filename, processEntry.szExeFile) == 0) { michael@0: CloseHandle(snapshot); michael@0: return ERROR_SUCCESS; michael@0: } michael@0: } while (Process32NextW(snapshot, &processEntry)); michael@0: CloseHandle(snapshot); michael@0: return ERROR_NOT_FOUND; michael@0: } michael@0: michael@0: /** michael@0: * Waits for the specified applicaiton to exit. michael@0: * michael@0: * @param filename The application to wait for. michael@0: * @param maxSeconds The maximum amount of seconds to wait for all michael@0: * instances of the application to exit. michael@0: * @return ERROR_SUCCESS if no instances of the application exist michael@0: * WAIT_TIMEOUT if the process is still running after maxSeconds. michael@0: * Any other Win32 system error code. michael@0: */ michael@0: DWORD michael@0: WaitForProcessExit(LPCWSTR filename, DWORD maxSeconds) michael@0: { michael@0: DWORD applicationRunningError = WAIT_TIMEOUT; michael@0: for(DWORD i = 0; i < maxSeconds; i++) { michael@0: DWORD applicationRunningError = IsProcessRunning(filename); michael@0: if (ERROR_NOT_FOUND == applicationRunningError) { michael@0: return ERROR_SUCCESS; michael@0: } michael@0: Sleep(1000); michael@0: } michael@0: michael@0: if (ERROR_SUCCESS == applicationRunningError) { michael@0: return WAIT_TIMEOUT; michael@0: } michael@0: michael@0: return applicationRunningError; michael@0: } michael@0: michael@0: /** michael@0: * Determines if the fallback key exists or not michael@0: * michael@0: * @return TRUE if the fallback key exists and there was no error checking michael@0: */ michael@0: BOOL michael@0: DoesFallbackKeyExist() michael@0: { michael@0: HKEY testOnlyFallbackKey; michael@0: if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, michael@0: TEST_ONLY_FALLBACK_KEY_PATH, 0, michael@0: KEY_READ | KEY_WOW64_64KEY, michael@0: &testOnlyFallbackKey) != ERROR_SUCCESS) { michael@0: return FALSE; michael@0: } michael@0: michael@0: RegCloseKey(testOnlyFallbackKey); michael@0: return TRUE; michael@0: } michael@0: michael@0: #endif michael@0: michael@0: /** michael@0: * Determines if the file system for the specified file handle is local michael@0: * @param file path to check the filesystem type for, must be at most MAX_PATH michael@0: * @param isLocal out parameter which will hold TRUE if the drive is local michael@0: * @return TRUE if the call succeeded michael@0: */ michael@0: BOOL michael@0: IsLocalFile(LPCWSTR file, BOOL &isLocal) michael@0: { michael@0: WCHAR rootPath[MAX_PATH + 1] = { L'\0' }; michael@0: if (wcslen(file) > MAX_PATH) { michael@0: return FALSE; michael@0: } michael@0: michael@0: wcsncpy(rootPath, file, MAX_PATH); michael@0: PathStripToRootW(rootPath); michael@0: isLocal = GetDriveTypeW(rootPath) == DRIVE_FIXED; michael@0: return TRUE; michael@0: } michael@0: michael@0: michael@0: /** michael@0: * Determines the DWORD value of a registry key value michael@0: * michael@0: * @param key The base key to where the value name exists michael@0: * @param valueName The name of the value michael@0: * @param retValue Out parameter which will hold the value michael@0: * @return TRUE on success michael@0: */ michael@0: static BOOL michael@0: GetDWORDValue(HKEY key, LPCWSTR valueName, DWORD &retValue) michael@0: { michael@0: DWORD regDWORDValueSize = sizeof(DWORD); michael@0: LONG retCode = RegQueryValueExW(key, valueName, 0, nullptr, michael@0: reinterpret_cast(&retValue), michael@0: ®DWORDValueSize); michael@0: return ERROR_SUCCESS == retCode; michael@0: } michael@0: michael@0: /** michael@0: * Determines if the the system's elevation type allows michael@0: * unprmopted elevation. michael@0: * michael@0: * @param isUnpromptedElevation Out parameter which specifies if unprompted michael@0: * elevation is allowed. michael@0: * @return TRUE if the user can actually elevate and the value was obtained michael@0: * successfully. michael@0: */ michael@0: BOOL michael@0: IsUnpromptedElevation(BOOL &isUnpromptedElevation) michael@0: { michael@0: if (!UACHelper::CanUserElevate()) { michael@0: return FALSE; michael@0: } michael@0: michael@0: LPCWSTR UACBaseRegKey = michael@0: L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System"; michael@0: HKEY baseKey; michael@0: LONG retCode = RegOpenKeyExW(HKEY_LOCAL_MACHINE, michael@0: UACBaseRegKey, 0, michael@0: KEY_READ, &baseKey); michael@0: if (retCode != ERROR_SUCCESS) { michael@0: return FALSE; michael@0: } michael@0: michael@0: DWORD consent, secureDesktop; michael@0: BOOL success = GetDWORDValue(baseKey, L"ConsentPromptBehaviorAdmin", michael@0: consent); michael@0: success = success && michael@0: GetDWORDValue(baseKey, L"PromptOnSecureDesktop", secureDesktop); michael@0: isUnpromptedElevation = !consent && !secureDesktop; michael@0: michael@0: RegCloseKey(baseKey); michael@0: return success; michael@0: } michael@0: michael@0: #ifdef MOZ_METRO michael@0: /* michael@0: * Retrieve the app model id of the firefox metro browser. michael@0: * michael@0: * @aPathBuffer Buffer to fill michael@0: * @aCharLength Length of buffer to fill in characters michael@0: */ michael@0: bool GetDefaultBrowserAppModelID(WCHAR* aIDBuffer, long aCharLength) michael@0: { michael@0: if (!aIDBuffer || aCharLength <= 0) michael@0: return false; michael@0: michael@0: memset(aIDBuffer, 0, (sizeof(WCHAR)*aCharLength)); michael@0: static const WCHAR* kDefaultMetroBrowserIDPathKey = L"FirefoxURL"; michael@0: michael@0: HKEY key; michael@0: if (RegOpenKeyExW(HKEY_CLASSES_ROOT, kDefaultMetroBrowserIDPathKey, michael@0: 0, KEY_READ, &key) != ERROR_SUCCESS) { michael@0: return false; michael@0: } michael@0: DWORD len = aCharLength * sizeof(WCHAR); michael@0: memset(aIDBuffer, 0, len); michael@0: if (RegQueryValueExW(key, L"AppUserModelID", nullptr, nullptr, michael@0: (LPBYTE)aIDBuffer, &len) != ERROR_SUCCESS || !len) { michael@0: RegCloseKey(key); michael@0: return false; michael@0: } michael@0: RegCloseKey(key); michael@0: return true; michael@0: } michael@0: michael@0: HRESULT michael@0: LaunchDefaultMetroBrowser() michael@0: { michael@0: CoInitialize(nullptr); michael@0: HRESULT hr = E_FAIL; michael@0: // The interface that allows us to activate the browser michael@0: IApplicationActivationManager *activateMgr; michael@0: if (FAILED(hr = CoCreateInstance(CLSID_ApplicationActivationManager, michael@0: nullptr, CLSCTX_LOCAL_SERVER, michael@0: IID_IApplicationActivationManager, michael@0: (void**)&activateMgr))) { michael@0: CoUninitialize(); michael@0: return hr; michael@0: } michael@0: michael@0: // Activation is based on the browser's registered app model id michael@0: WCHAR appModelID[256]; michael@0: if (!GetDefaultBrowserAppModelID(appModelID, (sizeof(appModelID)/sizeof(WCHAR)))) { michael@0: activateMgr->Release(); michael@0: CoUninitialize(); michael@0: return hr; michael@0: } michael@0: michael@0: // Hand off focus rights to the out-of-process activation server. Without michael@0: // this the metro interface won't launch. michael@0: CoAllowSetForegroundWindow(activateMgr, nullptr); michael@0: michael@0: // Launch default browser in Metro michael@0: DWORD processID; michael@0: hr = activateMgr->ActivateApplication(appModelID, L"", AO_NOERRORUI, michael@0: &processID); michael@0: activateMgr->Release(); michael@0: CoUninitialize(); michael@0: return hr; michael@0: } michael@0: #endif