/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /** * This file exists so that LaunchModernSettingsDialogDefaultApps can be called * without linking to libxul. */ #include "WindowsDefaultBrowser.h" #include "city.h" #include "mozilla/ArrayUtils.h" #include "mozilla/UniquePtr.h" #include "mozilla/WindowsVersion.h" #include "mozilla/WinHeaderOnlyUtils.h" // Needed for access to IApplicationActivationManager // This must be before any other includes that might include shlobj.h #ifdef _WIN32_WINNT # undef _WIN32_WINNT #endif #define _WIN32_WINNT 0x0600 #define INITGUID #undef NTDDI_VERSION #define NTDDI_VERSION NTDDI_WIN8 #include #include #include #include #include #define APP_REG_NAME_BASE L"Firefox-" static bool IsWindowsLogonConnected() { WCHAR userName[UNLEN + 1]; DWORD size = mozilla::ArrayLength(userName); if (!GetUserNameW(userName, &size)) { return false; } LPUSER_INFO_24 info; if (NetUserGetInfo(nullptr, userName, 24, (LPBYTE*)&info) != NERR_Success) { return false; } bool connected = info->usri24_internet_identity; NetApiBufferFree(info); return connected; } static bool SettingsAppBelievesConnected() { const wchar_t* keyPath = L"SOFTWARE\\Microsoft\\Windows\\Shell\\Associations"; const wchar_t* valueName = L"IsConnectedAtLogon"; uint32_t value = 0; DWORD size = sizeof(uint32_t); LSTATUS ls = RegGetValueW(HKEY_CURRENT_USER, keyPath, valueName, RRF_RT_ANY, nullptr, &value, &size); if (ls != ERROR_SUCCESS) { return false; } return !!value; } bool GetAppRegName(mozilla::UniquePtr& aAppRegName) { mozilla::UniquePtr appDirStr; bool success = GetInstallDirectory(appDirStr); if (!success) { return success; } uint64_t hash = CityHash64(reinterpret_cast(appDirStr.get()), wcslen(appDirStr.get()) * sizeof(wchar_t)); const wchar_t* format = L"%s%I64X"; int bufferSize = _scwprintf(format, APP_REG_NAME_BASE, hash); ++bufferSize; // Extra byte for terminating null aAppRegName = mozilla::MakeUnique(bufferSize); _snwprintf_s(aAppRegName.get(), bufferSize, _TRUNCATE, format, APP_REG_NAME_BASE, hash); return success; } bool LaunchControlPanelDefaultPrograms() { // Build the path control.exe path safely WCHAR controlEXEPath[MAX_PATH + 1] = {'\0'}; if (!GetSystemDirectoryW(controlEXEPath, MAX_PATH)) { return false; } LPCWSTR controlEXE = L"control.exe"; if (wcslen(controlEXEPath) + wcslen(controlEXE) >= MAX_PATH) { return false; } if (!PathAppendW(controlEXEPath, controlEXE)) { return false; } const wchar_t* paramFormat = L"control.exe /name Microsoft.DefaultPrograms " L"/page pageDefaultProgram\\pageAdvancedSettings?pszAppName=%s"; mozilla::UniquePtr appRegName; GetAppRegName(appRegName); int bufferSize = _scwprintf(paramFormat, appRegName.get()); ++bufferSize; // Extra byte for terminating null mozilla::UniquePtr params = mozilla::MakeUnique(bufferSize); _snwprintf_s(params.get(), bufferSize, _TRUNCATE, paramFormat, appRegName.get()); STARTUPINFOW si = {sizeof(si), 0}; si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_SHOWDEFAULT; PROCESS_INFORMATION pi = {0}; if (!CreateProcessW(controlEXEPath, params.get(), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) { return false; } CloseHandle(pi.hProcess); CloseHandle(pi.hThread); return true; } bool LaunchModernSettingsDialogDefaultApps() { if (!mozilla::IsWindowsBuildOrLater(14965) && !IsWindowsLogonConnected() && SettingsAppBelievesConnected()) { // Use the classic Control Panel to work around a bug of older // builds of Windows 10. return LaunchControlPanelDefaultPrograms(); } IApplicationActivationManager* pActivator; HRESULT hr = CoCreateInstance( CLSID_ApplicationActivationManager, nullptr, CLSCTX_INPROC, IID_IApplicationActivationManager, (void**)&pActivator); if (SUCCEEDED(hr)) { DWORD pid; hr = pActivator->ActivateApplication( L"windows.immersivecontrolpanel_cw5n1h2txyewy" L"!microsoft.windows.immersivecontrolpanel", L"page=SettingsPageAppsDefaults", AO_NONE, &pid); if (SUCCEEDED(hr)) { // Do not check error because we could at least open // the "Default apps" setting. pActivator->ActivateApplication( L"windows.immersivecontrolpanel_cw5n1h2txyewy" L"!microsoft.windows.immersivecontrolpanel", L"page=SettingsPageAppsDefaults" L"&target=SystemSettings_DefaultApps_Browser", AO_NONE, &pid); } pActivator->Release(); return SUCCEEDED(hr); } return true; }