gecko-dev/browser/components/shell/WindowsDefaultBrowser.cpp

175 строки
5.4 KiB
C++

/* -*- 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 <shlobj.h>
#include <lm.h>
#include <shlwapi.h>
#include <wchar.h>
#include <windows.h>
#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;
}
// Generates the install directory without a trailing path separator.
bool GetInstallDirectory(mozilla::UniquePtr<wchar_t[]>& installPath) {
installPath = mozilla::GetFullBinaryPath();
// It's not safe to use PathRemoveFileSpecW with strings longer than MAX_PATH
// (including null terminator).
if (wcslen(installPath.get()) >= MAX_PATH) {
return false;
}
PathRemoveFileSpecW(installPath.get());
return true;
}
bool GetAppRegName(mozilla::UniquePtr<wchar_t[]>& aAppRegName) {
mozilla::UniquePtr<wchar_t[]> appDirStr;
bool success = GetInstallDirectory(appDirStr);
if (!success) {
return success;
}
uint64_t hash = CityHash64(reinterpret_cast<char*>(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<wchar_t[]>(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<wchar_t[]> appRegName;
GetAppRegName(appRegName);
int bufferSize = _scwprintf(paramFormat, appRegName.get());
++bufferSize; // Extra byte for terminating null
mozilla::UniquePtr<wchar_t[]> params =
mozilla::MakeUnique<wchar_t[]>(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;
}