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: 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

View file

@ -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
@ -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, by protocol if possible, falling back to extension if not
QString command =
getAssociatedCommand(AssociationQueryType::Protocol, L"http");
// get default browser start command
auto command =
QSettings("HKEY_CLASSES_ROOT\\" + browserId + "\\shell\\open\\command",
QSettings::NativeFormat)
.value("Default")
.toString();
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();
}
@ -84,7 +90,7 @@ namespace chatterino {
bool supportsIncognitoLinks()
{
#ifdef Q_OS_WIN
#ifdef USEWINSDK
return !getCommand().isNull();
#else
return false;
@ -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

View file

@ -6,6 +6,9 @@
#ifdef USEWINSDK
# include <Shlwapi.h>
# include <VersionHelpers.h>
namespace chatterino {
typedef enum MONITOR_DPI_TYPE {
@ -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)
{
@ -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
#endif

View file

@ -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