WindowsDefaultBrowser.cpp (6794B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 /** 7 * This file exists so that LaunchModernSettingsDialogDefaultApps can be called 8 * without linking to libxul. 9 */ 10 #include "WindowsDefaultBrowser.h" 11 12 #include "city.h" 13 #include "mozilla/UniquePtr.h" 14 #include "mozilla/WindowsVersion.h" 15 #include "mozilla/WinHeaderOnlyUtils.h" 16 17 // This must be before any other includes that might include shlobj.h 18 #define INITGUID 19 #include <shlobj.h> 20 21 #include <lm.h> 22 #include <shellapi.h> 23 #include <shlwapi.h> 24 #include <wchar.h> 25 #include <windows.h> 26 27 #define APP_REG_NAME_BASE L"Firefox-" 28 29 static bool IsWindowsLogonConnected() { 30 WCHAR userName[UNLEN + 1]; 31 DWORD size = std::size(userName); 32 if (!GetUserNameW(userName, &size)) { 33 return false; 34 } 35 36 LPUSER_INFO_24 info; 37 if (NetUserGetInfo(nullptr, userName, 24, (LPBYTE*)&info) != NERR_Success) { 38 return false; 39 } 40 bool connected = info->usri24_internet_identity; 41 NetApiBufferFree(info); 42 43 return connected; 44 } 45 46 static bool SettingsAppBelievesConnected() { 47 const wchar_t* keyPath = L"SOFTWARE\\Microsoft\\Windows\\Shell\\Associations"; 48 const wchar_t* valueName = L"IsConnectedAtLogon"; 49 50 uint32_t value = 0; 51 DWORD size = sizeof(uint32_t); 52 LSTATUS ls = RegGetValueW(HKEY_CURRENT_USER, keyPath, valueName, RRF_RT_ANY, 53 nullptr, &value, &size); 54 if (ls != ERROR_SUCCESS) { 55 return false; 56 } 57 58 return !!value; 59 } 60 61 bool GetAppRegName(mozilla::UniquePtr<wchar_t[]>& aAppRegName) { 62 mozilla::UniquePtr<wchar_t[]> appDirStr; 63 bool success = mozilla::GetInstallDirectory(appDirStr); 64 if (!success) { 65 return success; 66 } 67 68 uint64_t hash = CityHash64(reinterpret_cast<char*>(appDirStr.get()), 69 wcslen(appDirStr.get()) * sizeof(wchar_t)); 70 71 const wchar_t* format = L"%s%I64X"; 72 int bufferSize = _scwprintf(format, APP_REG_NAME_BASE, hash); 73 ++bufferSize; // Extra byte for terminating null 74 aAppRegName = mozilla::MakeUnique<wchar_t[]>(bufferSize); 75 76 _snwprintf_s(aAppRegName.get(), bufferSize, _TRUNCATE, format, 77 APP_REG_NAME_BASE, hash); 78 79 return success; 80 } 81 82 bool LaunchControlPanelDefaultPrograms() { 83 // Build the path control.exe path safely 84 WCHAR controlEXEPath[MAX_PATH + 1] = {'\0'}; 85 if (!GetSystemDirectoryW(controlEXEPath, MAX_PATH)) { 86 return false; 87 } 88 LPCWSTR controlEXE = L"control.exe"; 89 if (wcslen(controlEXEPath) + wcslen(controlEXE) >= MAX_PATH) { 90 return false; 91 } 92 if (!PathAppendW(controlEXEPath, controlEXE)) { 93 return false; 94 } 95 96 const wchar_t* paramFormat = 97 L"control.exe /name Microsoft.DefaultPrograms " 98 L"/page pageDefaultProgram\\pageAdvancedSettings?pszAppName=%s"; 99 mozilla::UniquePtr<wchar_t[]> appRegName; 100 GetAppRegName(appRegName); 101 int bufferSize = _scwprintf(paramFormat, appRegName.get()); 102 ++bufferSize; // Extra byte for terminating null 103 mozilla::UniquePtr<wchar_t[]> params = 104 mozilla::MakeUnique<wchar_t[]>(bufferSize); 105 _snwprintf_s(params.get(), bufferSize, _TRUNCATE, paramFormat, 106 appRegName.get()); 107 108 STARTUPINFOW si = {sizeof(si), 0}; 109 si.dwFlags = STARTF_USESHOWWINDOW; 110 si.wShowWindow = SW_SHOWDEFAULT; 111 PROCESS_INFORMATION pi = {0}; 112 if (!CreateProcessW(controlEXEPath, params.get(), nullptr, nullptr, FALSE, 0, 113 nullptr, nullptr, &si, &pi)) { 114 return false; 115 } 116 CloseHandle(pi.hProcess); 117 CloseHandle(pi.hThread); 118 119 return true; 120 } 121 122 static bool IsAppRegistered(HKEY rootKey, const wchar_t* appRegName) { 123 const wchar_t* keyPath = L"Software\\RegisteredApplications"; 124 125 DWORD size = sizeof(uint32_t); 126 LSTATUS ls = RegGetValueW(rootKey, keyPath, appRegName, RRF_RT_ANY, nullptr, 127 nullptr, &size); 128 return ls == ERROR_SUCCESS; 129 } 130 131 static bool LaunchMsSettingsProtocol() { 132 mozilla::UniquePtr<wchar_t[]> params = nullptr; 133 if (mozilla::HasPackageIdentity()) { 134 mozilla::UniquePtr<wchar_t[]> packageFamilyName = 135 mozilla::GetPackageFamilyName(); 136 if (packageFamilyName) { 137 const wchar_t* paramFormat = 138 L"ms-settings:defaultapps?registeredAUMID=%s!App"; 139 int bufferSize = _scwprintf(paramFormat, packageFamilyName.get()); 140 ++bufferSize; // Extra byte for terminating null 141 params = mozilla::MakeUnique<wchar_t[]>(bufferSize); 142 _snwprintf_s(params.get(), bufferSize, _TRUNCATE, paramFormat, 143 packageFamilyName.get()); 144 } 145 } 146 if (!params) { 147 mozilla::UniquePtr<wchar_t[]> appRegName; 148 GetAppRegName(appRegName); 149 const wchar_t* paramFormat = 150 IsAppRegistered(HKEY_CURRENT_USER, appRegName.get()) || 151 !IsAppRegistered(HKEY_LOCAL_MACHINE, appRegName.get()) 152 ? L"ms-settings:defaultapps?registeredAppUser=%s" 153 : L"ms-settings:defaultapps?registeredAppMachine=%s"; 154 int bufferSize = _scwprintf(paramFormat, appRegName.get()); 155 ++bufferSize; // Extra byte for terminating null 156 params = mozilla::MakeUnique<wchar_t[]>(bufferSize); 157 _snwprintf_s(params.get(), bufferSize, _TRUNCATE, paramFormat, 158 appRegName.get()); 159 } 160 161 SHELLEXECUTEINFOW seinfo = {sizeof(seinfo)}; 162 seinfo.lpFile = params.get(); 163 seinfo.nShow = SW_SHOWNORMAL; 164 return ShellExecuteExW(&seinfo); 165 } 166 167 bool LaunchModernSettingsDialogDefaultApps() { 168 if (mozilla::IsWin11OrLater()) { 169 return LaunchMsSettingsProtocol(); 170 } 171 172 if (!mozilla::IsWindows10BuildOrLater(14965) && !IsWindowsLogonConnected() && 173 SettingsAppBelievesConnected()) { 174 // Use the classic Control Panel to work around a bug of older 175 // builds of Windows 10. 176 return LaunchControlPanelDefaultPrograms(); 177 } 178 179 IApplicationActivationManager* pActivator; 180 HRESULT hr = CoCreateInstance( 181 CLSID_ApplicationActivationManager, nullptr, CLSCTX_INPROC, 182 IID_IApplicationActivationManager, (void**)&pActivator); 183 184 if (SUCCEEDED(hr)) { 185 DWORD pid; 186 hr = pActivator->ActivateApplication( 187 L"windows.immersivecontrolpanel_cw5n1h2txyewy" 188 L"!microsoft.windows.immersivecontrolpanel", 189 L"page=SettingsPageAppsDefaults", AO_NONE, &pid); 190 if (SUCCEEDED(hr)) { 191 // Do not check error because we could at least open 192 // the "Default apps" setting. 193 pActivator->ActivateApplication( 194 L"windows.immersivecontrolpanel_cw5n1h2txyewy" 195 L"!microsoft.windows.immersivecontrolpanel", 196 L"page=SettingsPageAppsDefaults" 197 L"&target=SystemSettings_DefaultApps_Browser", 198 AO_NONE, &pid); 199 } 200 pActivator->Release(); 201 return SUCCEEDED(hr); 202 } 203 return true; 204 }