Use AssocQueryString instead of directly querying the registry (#4362)

Co-authored-by: Rasmus Karlsson <rasmus.karlsson@pajlada.com>
This commit is contained in:
Sam Heybey 2023-02-10 21:21:09 -05:00 committed by GitHub
parent d38187f794
commit 1d3ca0bfa3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 95 additions and 18 deletions

View file

@ -47,6 +47,7 @@
- Dev: Added CMake Install Support on Windows. (#4300) - Dev: Added CMake Install Support on Windows. (#4300)
- Dev: Changed conan generator to [`CMakeDeps`](https://docs.conan.io/en/latest/reference/conanfile/tools/cmake/cmakedeps.html) and [`CMakeToolchain`](https://docs.conan.io/en/latest/reference/conanfile/tools/cmake/cmaketoolchain.html). See PR for migration notes. (#4335) - Dev: Changed conan generator to [`CMakeDeps`](https://docs.conan.io/en/latest/reference/conanfile/tools/cmake/cmakedeps.html) and [`CMakeToolchain`](https://docs.conan.io/en/latest/reference/conanfile/tools/cmake/cmaketoolchain.html). See PR for migration notes. (#4335)
- Dev: Refactored 7TV EventAPI implementation. (#4342) - Dev: Refactored 7TV EventAPI implementation. (#4342)
- Dev: Don't rely on undocumented registry keys to find the default browser on Windows. (#4362)
## 2.4.0 ## 2.4.0

View file

@ -1,15 +1,17 @@
#include "util/IncognitoBrowser.hpp" #include "util/IncognitoBrowser.hpp"
#ifdef USEWINSDK
# include "util/WindowsHelper.hpp"
#endif
#include <QProcess> #include <QProcess>
#include <QRegularExpression> #include <QRegularExpression>
#include <QSettings>
#include <QVariant> #include <QVariant>
namespace { namespace {
using namespace chatterino; using namespace chatterino;
#ifdef Q_OS_WIN #ifdef USEWINSDK
QString injectPrivateSwitch(QString command) QString injectPrivateSwitch(QString command)
{ {
// list of command line switches to turn on private browsing in browsers // list of command line switches to turn on private browsing in browsers
@ -47,23 +49,27 @@ QString injectPrivateSwitch(QString command)
QString getCommand() QString getCommand()
{ {
// get default browser prog id // get default browser start command, by protocol if possible, falling back to extension if not
auto browserId = QSettings("HKEY_CURRENT_" QString command =
"USER\\Software\\Microsoft\\Windows\\Shell\\" getAssociatedCommand(AssociationQueryType::Protocol, L"http");
"Associations\\UrlAssociatio"
"ns\\http\\UserChoice",
QSettings::NativeFormat)
.value("Progid")
.toString();
// get default browser start command
auto command =
QSettings("HKEY_CLASSES_ROOT\\" + browserId + "\\shell\\open\\command",
QSettings::NativeFormat)
.value("Default")
.toString();
if (command.isNull()) if (command.isNull())
{ {
// failed to fetch default browser by protocol, try by file extension instead
command =
getAssociatedCommand(AssociationQueryType::FileExtension, L".html");
}
if (command.isNull())
{
// also try the equivalent .htm extension
command =
getAssociatedCommand(AssociationQueryType::FileExtension, L".htm");
}
if (command.isNull())
{
// failed to find browser command
return QString(); return QString();
} }
@ -84,7 +90,7 @@ namespace chatterino {
bool supportsIncognitoLinks() bool supportsIncognitoLinks()
{ {
#ifdef Q_OS_WIN #ifdef USEWINSDK
return !getCommand().isNull(); return !getCommand().isNull();
#else #else
return false; return false;
@ -93,7 +99,7 @@ bool supportsIncognitoLinks()
bool openLinkIncognito(const QString &link) bool openLinkIncognito(const QString &link)
{ {
#ifdef Q_OS_WIN #ifdef USEWINSDK
auto command = getCommand(); auto command = getCommand();
// TODO: split command into program path and incognito argument // TODO: split command into program path and incognito argument

View file

@ -6,6 +6,9 @@
#ifdef USEWINSDK #ifdef USEWINSDK
# include <Shlwapi.h>
# include <VersionHelpers.h>
namespace chatterino { namespace chatterino {
typedef enum MONITOR_DPI_TYPE { typedef enum MONITOR_DPI_TYPE {
@ -17,6 +20,8 @@ typedef enum MONITOR_DPI_TYPE {
typedef HRESULT(CALLBACK *GetDpiForMonitor_)(HMONITOR, MONITOR_DPI_TYPE, UINT *, typedef HRESULT(CALLBACK *GetDpiForMonitor_)(HMONITOR, MONITOR_DPI_TYPE, UINT *,
UINT *); UINT *);
typedef HRESULT(CALLBACK *AssocQueryString_)(ASSOCF, ASSOCSTR, LPCWSTR, LPCWSTR,
LPWSTR, DWORD *);
boost::optional<UINT> getWindowDpi(HWND hwnd) boost::optional<UINT> getWindowDpi(HWND hwnd)
{ {
@ -83,6 +88,67 @@ void setRegisteredForStartup(bool isRegistered)
} }
} }
QString getAssociatedCommand(AssociationQueryType queryType, LPCWSTR query)
{
static HINSTANCE shlwapi = LoadLibrary(L"shlwapi");
if (shlwapi == nullptr)
{
return QString();
}
static auto assocQueryString =
AssocQueryString_(GetProcAddress(shlwapi, "AssocQueryStringW"));
if (assocQueryString == nullptr)
{
return QString();
}
// always error out instead of returning a truncated string when the
// buffer is too small - avoids race condition when the user changes their
// default browser between calls to AssocQueryString
ASSOCF flags = ASSOCF_NOTRUNCATE;
if (queryType == AssociationQueryType::Protocol)
{
// ASSOCF_IS_PROTOCOL was introduced in Windows 8
if (IsWindows8OrGreater())
{
flags |= ASSOCF_IS_PROTOCOL;
}
else
{
return QString();
}
}
DWORD resultSize = 0;
if (FAILED(assocQueryString(flags, ASSOCSTR_COMMAND, query, nullptr,
nullptr, &resultSize)))
{
return QString();
}
if (resultSize <= 1)
{
// resultSize includes the null terminator. if resultSize is 1, the
// returned value would be the empty string.
return QString();
}
QString result;
auto buf = new wchar_t[resultSize];
if (SUCCEEDED(assocQueryString(flags, ASSOCSTR_COMMAND, query, nullptr, buf,
&resultSize)))
{
// QString::fromWCharArray expects the length in characters *not
// including* the null terminator, but AssocQueryStringW calculates
// length including the null terminator
result = QString::fromWCharArray(buf, resultSize - 1);
}
delete[] buf;
return result;
}
} // namespace chatterino } // namespace chatterino
#endif #endif

View file

@ -7,12 +7,16 @@
namespace chatterino { namespace chatterino {
enum class AssociationQueryType { Protocol, FileExtension };
boost::optional<UINT> getWindowDpi(HWND hwnd); boost::optional<UINT> getWindowDpi(HWND hwnd);
void flushClipboard(); void flushClipboard();
bool isRegisteredForStartup(); bool isRegisteredForStartup();
void setRegisteredForStartup(bool isRegistered); void setRegisteredForStartup(bool isRegistered);
QString getAssociatedCommand(AssociationQueryType queryType, LPCWSTR query);
} // namespace chatterino } // namespace chatterino
#endif #endif