/* -*- 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" // This must be before any other includes that might include shlobj.h #define INITGUID #include #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; } static bool IsAppRegistered(HKEY rootKey, const wchar_t* appRegName) { const wchar_t* keyPath = L"Software\\RegisteredApplications"; DWORD size = sizeof(uint32_t); LSTATUS ls = RegGetValueW(rootKey, keyPath, appRegName, RRF_RT_ANY, nullptr, nullptr, &size); return ls == ERROR_SUCCESS; } static bool LaunchMsSettingsProtocol() { mozilla::UniquePtr params = nullptr; if (mozilla::HasPackageIdentity()) { mozilla::UniquePtr packageFamilyName = mozilla::GetPackageFamilyName(); if (packageFamilyName) { const wchar_t* paramFormat = L"ms-settings:defaultapps?registeredAUMID=%s!App"; int bufferSize = _scwprintf(paramFormat, packageFamilyName.get()); ++bufferSize; // Extra byte for terminating null params = mozilla::MakeUnique(bufferSize); _snwprintf_s(params.get(), bufferSize, _TRUNCATE, paramFormat, packageFamilyName.get()); } } if (!params) { mozilla::UniquePtr appRegName; GetAppRegName(appRegName); const wchar_t* paramFormat = IsAppRegistered(HKEY_CURRENT_USER, appRegName.get()) || !IsAppRegistered(HKEY_LOCAL_MACHINE, appRegName.get()) ? L"ms-settings:defaultapps?registeredAppUser=%s" : L"ms-settings:defaultapps?registeredAppMachine=%s"; int bufferSize = _scwprintf(paramFormat, appRegName.get()); ++bufferSize; // Extra byte for terminating null params = mozilla::MakeUnique(bufferSize); _snwprintf_s(params.get(), bufferSize, _TRUNCATE, paramFormat, appRegName.get()); } SHELLEXECUTEINFOW seinfo = {sizeof(seinfo)}; seinfo.lpFile = params.get(); seinfo.nShow = SW_SHOWNORMAL; return ShellExecuteExW(&seinfo); } bool LaunchModernSettingsDialogDefaultApps() { if (mozilla::IsWin11OrLater()) { return LaunchMsSettingsProtocol(); } if (!mozilla::IsWindows10BuildOrLater(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; }