Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use AssocQueryString instead of directly querying the registry #4362

Merged
merged 11 commits into from
Feb 11, 2023
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
- 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: Refactored 7TV EventAPI implementation. (#4342)
- Dev: Don't rely on undocumented registry keys to find the default browser on Windows. (#4362)

## 2.4.0

Expand Down
44 changes: 25 additions & 19 deletions src/util/IncognitoBrowser.cpp
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
#include "util/IncognitoBrowser.hpp"
#ifdef USEWINSDK
# include "util/WindowsHelper.hpp"
#endif

#include <QProcess>
#include <QRegularExpression>
#include <QSettings>
#include <QVariant>

namespace {

using namespace chatterino;

#ifdef Q_OS_WIN
#ifdef USEWINSDK
QString injectPrivateSwitch(QString command)
{
// list of command line switches to turn on private browsing in browsers
Expand Down Expand Up @@ -47,23 +49,27 @@ QString injectPrivateSwitch(QString command)

QString getCommand()
{
// get default browser prog id
auto browserId = QSettings("HKEY_CURRENT_"
"USER\\Software\\Microsoft\\Windows\\Shell\\"
"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();
// get default browser start command, by protocol if possible, falling back to extension if not
QString command =
getAssociatedCommand(AssociationQueryType::Protocol, L"http");

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();
}

Expand All @@ -84,7 +90,7 @@ namespace chatterino {

bool supportsIncognitoLinks()
{
#ifdef Q_OS_WIN
#ifdef USEWINSDK
return !getCommand().isNull();
#else
return false;
Expand All @@ -93,7 +99,7 @@ bool supportsIncognitoLinks()

bool openLinkIncognito(const QString &link)
{
#ifdef Q_OS_WIN
#ifdef USEWINSDK
auto command = getCommand();

// TODO: split command into program path and incognito argument
Expand Down
54 changes: 54 additions & 0 deletions src/util/WindowsHelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

#ifdef USEWINSDK

# include <Shlwapi.h>
# include <VersionHelpers.h>

namespace chatterino {

typedef enum MONITOR_DPI_TYPE {
Expand All @@ -17,6 +20,8 @@ typedef enum MONITOR_DPI_TYPE {

typedef HRESULT(CALLBACK *GetDpiForMonitor_)(HMONITOR, MONITOR_DPI_TYPE, UINT *,
UINT *);
typedef HRESULT(CALLBACK *AssocQueryString_)(ASSOCF, ASSOCSTR, LPCWSTR, LPCWSTR,
LPWSTR, DWORD *);

boost::optional<UINT> getWindowDpi(HWND hwnd)
{
Expand Down Expand Up @@ -83,6 +88,55 @@ void setRegisteredForStartup(bool isRegistered)
}
}

QString getAssociatedCommand(AssociationQueryType queryType, LPCWSTR query)
{
static HINSTANCE shlwapi = LoadLibrary(L"shlwapi");
static auto assocQueryString = AssocQueryString_(
(shlwapi == NULL) ? NULL
: GetProcAddress(shlwapi, "AssocQueryStringW"));
pajlada marked this conversation as resolved.
Show resolved Hide resolved

if (assocQueryString == NULL)
{
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
pajlada marked this conversation as resolved.
Show resolved Hide resolved
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();
}
pajlada marked this conversation as resolved.
Show resolved Hide resolved
}

DWORD resultSize = 0;
if (!SUCCEEDED(assocQueryString(flags, ASSOCSTR_COMMAND, query, NULL, NULL,
pajlada marked this conversation as resolved.
Show resolved Hide resolved
&resultSize)) ||
resultSize == 0)
{
return QString();
}

QString result;
auto buf = new wchar_t[resultSize];
if (SUCCEEDED(assocQueryString(flags, ASSOCSTR_COMMAND, query, NULL, buf,
&resultSize)))
{
result = QString::fromWCharArray(buf, resultSize);
}
delete[] buf;
return result;
}

} // namespace chatterino

#endif
4 changes: 4 additions & 0 deletions src/util/WindowsHelper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@

namespace chatterino {

enum class AssociationQueryType { Protocol, FileExtension };

boost::optional<UINT> getWindowDpi(HWND hwnd);
void flushClipboard();

bool isRegisteredForStartup();
void setRegisteredForStartup(bool isRegistered);

QString getAssociatedCommand(AssociationQueryType queryType, LPCWSTR query);

} // namespace chatterino

#endif