Check AppUserModelId better.

This commit is contained in:
John Preston 2023-09-22 11:50:41 +04:00
parent 63a753d35c
commit c6c06c149d
4 changed files with 243 additions and 184 deletions

View File

@ -118,7 +118,7 @@ bool init() {
LOG(("App Error: Object registration failed.")); LOG(("App Error: Object registration failed."));
} }
} }
if (!AppUserModelId::validateShortcut()) { if (!AppUserModelId::ValidateShortcut()) {
LOG(("App Error: Shortcut validation failed.")); LOG(("App Error: Shortcut validation failed."));
return false; return false;
} }
@ -132,7 +132,7 @@ bool init() {
CoTaskMemFree(appUserModelId); CoTaskMemFree(appUserModelId);
}); });
if (AppUserModelId::getId() != appUserModelId) { if (AppUserModelId::Id() != appUserModelId) {
return false; return false;
} }
return true; return true;
@ -308,7 +308,7 @@ void QueryFocusAssist() {
} }
return; return;
} }
const auto appUserModelId = AppUserModelId::getId(); const auto appUserModelId = AppUserModelId::Id();
auto blocked = true; auto blocked = true;
const auto guard = gsl::finally([&] { const auto guard = gsl::finally([&] {
if (FocusAssistBlocks != blocked) { if (FocusAssistBlocks != blocked) {
@ -500,7 +500,7 @@ Manager::Private::Private(Manager *instance)
bool Manager::Private::init() { bool Manager::Private::init() {
return base::WinRT::Try([&] { return base::WinRT::Try([&] {
_notifier = ToastNotificationManager::CreateToastNotifier( _notifier = ToastNotificationManager::CreateToastNotifier(
AppUserModelId::getId()); AppUserModelId::Id());
}); });
} }

View File

@ -209,9 +209,9 @@ bool ManageAppLink(
if (const auto propertyStore = shellLink.try_as<IPropertyStore>()) { if (const auto propertyStore = shellLink.try_as<IPropertyStore>()) {
PROPVARIANT appIdPropVar; PROPVARIANT appIdPropVar;
hr = InitPropVariantFromString(AppUserModelId::getId().c_str(), &appIdPropVar); hr = InitPropVariantFromString(AppUserModelId::Id().c_str(), &appIdPropVar);
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
hr = propertyStore->SetValue(AppUserModelId::getKey(), appIdPropVar); hr = propertyStore->SetValue(AppUserModelId::Key(), appIdPropVar);
PropVariantClear(&appIdPropVar); PropVariantClear(&appIdPropVar);
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
hr = propertyStore->Commit(); hr = propertyStore->Commit();
@ -262,7 +262,7 @@ void psDoCleanup() {
try { try {
Platform::AutostartToggle(false); Platform::AutostartToggle(false);
psSendToMenu(false, true); psSendToMenu(false, true);
AppUserModelId::cleanupShortcut(); AppUserModelId::CleanupShortcut();
DeleteMyModules(); DeleteMyModules();
} catch (...) { } catch (...) {
} }
@ -371,7 +371,7 @@ void start() {
// https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/setlocale-wsetlocale#utf-8-support // https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/setlocale-wsetlocale#utf-8-support
setlocale(LC_ALL, ".UTF8"); setlocale(LC_ALL, ".UTF8");
const auto appUserModelId = AppUserModelId::getId(); const auto appUserModelId = AppUserModelId::Id();
SetCurrentProcessExplicitAppUserModelID(appUserModelId.c_str()); SetCurrentProcessExplicitAppUserModelID(appUserModelId.c_str());
LOG(("AppUserModelID: %1").arg(appUserModelId)); LOG(("AppUserModelID: %1").arg(appUserModelId));
} }
@ -643,7 +643,7 @@ bool OpenSystemSettings(SystemSettingsType type) {
void NewVersionLaunched(int oldVersion) { void NewVersionLaunched(int oldVersion) {
if (oldVersion <= 4009009) { if (oldVersion <= 4009009) {
AppUserModelId::checkPinned(); AppUserModelId::CheckPinned();
} }
if (oldVersion > 0 && oldVersion < 2008012) { if (oldVersion > 0 && oldVersion < 2008012) {
// Reset icons cache, because we've changed the application icon. // Reset icons cache, because we've changed the application icon.

View File

@ -9,35 +9,31 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "platform/win/windows_dlls.h" #include "platform/win/windows_dlls.h"
#include "platform/win/windows_toast_activator.h" #include "platform/win/windows_toast_activator.h"
#include "base/platform/win/base_windows_wrl.h" #include "base/platform/win/base_windows_winrt.h"
#include "core/launcher.h" #include "core/launcher.h"
#include <propvarutil.h> #include <propvarutil.h>
#include <propkey.h> #include <propkey.h>
using namespace Microsoft::WRL;
namespace Platform { namespace Platform {
namespace AppUserModelId { namespace AppUserModelId {
namespace { namespace {
constexpr auto kMaxFileLen = MAX_PATH * 2;
const PROPERTYKEY pkey_AppUserModel_ID = { { 0x9F4C2855, 0x9F79, 0x4B39, { 0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3 } }, 5 }; const PROPERTYKEY pkey_AppUserModel_ID = { { 0x9F4C2855, 0x9F79, 0x4B39, { 0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3 } }, 5 };
const PROPERTYKEY pkey_AppUserModel_StartPinOption = { { 0x9F4C2855, 0x9F79, 0x4B39, { 0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3 } }, 12 }; const PROPERTYKEY pkey_AppUserModel_StartPinOption = { { 0x9F4C2855, 0x9F79, 0x4B39, { 0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3 } }, 12 };
const PROPERTYKEY pkey_AppUserModel_ToastActivator = { { 0x9F4C2855, 0x9F79, 0x4B39, { 0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3 } }, 26 }; const PROPERTYKEY pkey_AppUserModel_ToastActivator = { { 0x9F4C2855, 0x9F79, 0x4B39, { 0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3 } }, 26 };
#ifdef OS_WIN_STORE #ifdef OS_WIN_STORE
const WCHAR AppUserModelIdRelease[] = L"Telegram.TelegramDesktop.Store"; const WCHAR AppUserModelIdBase[] = L"Telegram.TelegramDesktop.Store";
#else // OS_WIN_STORE #else // OS_WIN_STORE
const WCHAR AppUserModelIdRelease[] = L"Telegram.TelegramDesktop"; const WCHAR AppUserModelIdBase[] = L"Telegram.TelegramDesktop";
#endif // OS_WIN_STORE #endif // OS_WIN_STORE
const WCHAR AppUserModelIdAlpha[] = L"Telegram.TelegramDesktop.Alpha";
} // namespace [[nodiscard]] QString PinnedIconsPath() {
WCHAR wstrPath[kMaxFileLen] = {};
QString pinnedPath() { if (GetEnvironmentVariable(L"APPDATA", wstrPath, kMaxFileLen)) {
static const int maxFileLen = MAX_PATH * 10;
WCHAR wstrPath[maxFileLen];
if (GetEnvironmentVariable(L"APPDATA", wstrPath, maxFileLen)) {
auto appData = QDir(QString::fromStdWString(std::wstring(wstrPath))); auto appData = QDir(QString::fromStdWString(std::wstring(wstrPath)));
return appData.absolutePath() return appData.absolutePath()
+ u"/Microsoft/Internet Explorer/Quick Launch/User Pinned/TaskBar/"_q; + u"/Microsoft/Internet Explorer/Quick Launch/User Pinned/TaskBar/"_q;
@ -45,34 +41,74 @@ QString pinnedPath() {
return QString(); return QString();
} }
void checkPinned() { } // namespace
static const int maxFileLen = MAX_PATH * 10;
HRESULT hr = CoInitialize(0); const std::wstring &MyExecutablePath() {
if (!SUCCEEDED(hr)) return; static const auto Path = [&] {
auto result = std::wstring(kMaxFileLen, 0);
const auto length = GetModuleFileName(
GetModuleHandle(nullptr),
result.data(),
kMaxFileLen);
if (!length || length == kMaxFileLen) {
result.clear();
} else {
result.resize(length + 1);
}
return result;
}();
return Path;
}
QString path = pinnedPath(); UniqueFileId MyExecutablePathId() {
std::wstring p = QDir::toNativeSeparators(path).toStdWString(); return GetUniqueFileId(MyExecutablePath().c_str());
}
WCHAR src[MAX_PATH]; UniqueFileId GetUniqueFileId(LPCWSTR path) {
GetModuleFileName(GetModuleHandle(0), src, MAX_PATH); auto info = BY_HANDLE_FILE_INFORMATION{};
BY_HANDLE_FILE_INFORMATION srcinfo = { 0 }; const auto file = CreateFile(
HANDLE srcfile = CreateFile( path,
src, 0,
0x00, 0,
0x00, nullptr,
NULL,
OPEN_EXISTING, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_NORMAL,
NULL); nullptr);
if (srcfile == INVALID_HANDLE_VALUE) return; if (file == INVALID_HANDLE_VALUE) {
BOOL srcres = GetFileInformationByHandle(srcfile, &srcinfo); return {};
CloseHandle(srcfile); }
if (!srcres) return; const auto result = GetFileInformationByHandle(file, &info);
CloseHandle(file);
if (!result) {
return {};
}
return {
.part1 = info.dwVolumeSerialNumber,
.part2 = ((std::uint64_t(info.nFileIndexLow) << 32)
| std::uint64_t(info.nFileIndexHigh)),
};
}
void CheckPinned() {
if (!SUCCEEDED(CoInitialize(0))) {
return;
}
const auto coGuard = gsl::finally([] {
CoUninitialize();
});
const auto path = PinnedIconsPath();
const auto native = QDir::toNativeSeparators(path).toStdWString();
const auto srcid = MyExecutablePathId();
if (!srcid) {
return;
}
LOG(("Checking...")); LOG(("Checking..."));
WIN32_FIND_DATA findData; WIN32_FIND_DATA findData;
HANDLE findHandle = FindFirstFileEx( HANDLE findHandle = FindFirstFileEx(
(p + L"*").c_str(), (native + L"*").c_str(),
FindExInfoStandard, FindExInfoStandard,
&findData, &findData,
FindExSearchNameMatch, FindExSearchNameMatch,
@ -83,62 +119,48 @@ void checkPinned() {
return; return;
} }
do { do {
std::wstring fname = p + findData.cFileName; std::wstring fname = native + findData.cFileName;
LOG(("Checking %1").arg(QString::fromStdWString(fname))); LOG(("Checking %1").arg(QString::fromStdWString(fname)));
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
continue; continue;
} else { } else {
DWORD attributes = GetFileAttributes(fname.c_str()); DWORD attributes = GetFileAttributes(fname.c_str());
if (attributes >= 0xFFFFFFF) continue; // file does not exist if (attributes >= 0xFFFFFFF) {
continue; // file does not exist
}
ComPtr<IShellLink> shellLink; auto shellLink = base::WinRT::TryCreateInstance<IShellLink>(
HRESULT hr = CoCreateInstance( CLSID_ShellLink);
CLSID_ShellLink, if (!shellLink) {
nullptr, continue;
CLSCTX_INPROC_SERVER, }
IID_PPV_ARGS(&shellLink));
auto persistFile = shellLink.try_as<IPersistFile>();
if (!persistFile) {
continue;
}
auto hr = persistFile->Load(fname.c_str(), STGM_READWRITE);
if (!SUCCEEDED(hr)) continue; if (!SUCCEEDED(hr)) continue;
ComPtr<IPersistFile> persistFile; WCHAR dst[MAX_PATH] = { 0 };
hr = shellLink.As(&persistFile); hr = shellLink->GetPath(dst, MAX_PATH, nullptr, 0);
if (!SUCCEEDED(hr)) continue; if (!SUCCEEDED(hr)) continue;
hr = persistFile->Load(fname.c_str(), STGM_READWRITE); if (GetUniqueFileId(dst) == srcid) {
if (!SUCCEEDED(hr)) continue; auto propertyStore = shellLink.try_as<IPropertyStore>();
if (!propertyStore) {
WCHAR dst[MAX_PATH]; return;
hr = shellLink->GetPath(dst, MAX_PATH, 0, 0); }
if (!SUCCEEDED(hr)) continue;
BY_HANDLE_FILE_INFORMATION dstinfo = { 0 };
HANDLE dstfile = CreateFile(
dst,
0x00,
0x00,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (dstfile == INVALID_HANDLE_VALUE) continue;
BOOL dstres = GetFileInformationByHandle(dstfile, &dstinfo);
CloseHandle(dstfile);
if (!dstres) continue;
if (srcinfo.dwVolumeSerialNumber == dstinfo.dwVolumeSerialNumber
&& srcinfo.nFileIndexLow == dstinfo.nFileIndexLow
&& srcinfo.nFileIndexHigh == dstinfo.nFileIndexHigh) {
ComPtr<IPropertyStore> propertyStore;
hr = shellLink.As(&propertyStore);
if (!SUCCEEDED(hr)) return;
PROPVARIANT appIdPropVar; PROPVARIANT appIdPropVar;
hr = propertyStore->GetValue(getKey(), &appIdPropVar); hr = propertyStore->GetValue(Key(), &appIdPropVar);
if (!SUCCEEDED(hr)) return; if (!SUCCEEDED(hr)) return;
LOG(("Reading...")); LOG(("Reading..."));
WCHAR already[MAX_PATH]; WCHAR already[MAX_PATH];
hr = PropVariantToString(appIdPropVar, already, MAX_PATH); hr = PropVariantToString(appIdPropVar, already, MAX_PATH);
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
if (getId() == already) { if (Id() == already) {
LOG(("Already!")); LOG(("Already!"));
PropVariantClear(&appIdPropVar); PropVariantClear(&appIdPropVar);
return; return;
@ -150,10 +172,10 @@ void checkPinned() {
} }
PropVariantClear(&appIdPropVar); PropVariantClear(&appIdPropVar);
hr = InitPropVariantFromString(getId().c_str(), &appIdPropVar); hr = InitPropVariantFromString(Id().c_str(), &appIdPropVar);
if (!SUCCEEDED(hr)) return; if (!SUCCEEDED(hr)) return;
hr = propertyStore->SetValue(getKey(), appIdPropVar); hr = propertyStore->SetValue(Key(), appIdPropVar);
PropVariantClear(&appIdPropVar); PropVariantClear(&appIdPropVar);
if (!SUCCEEDED(hr)) return; if (!SUCCEEDED(hr)) return;
@ -176,9 +198,8 @@ void checkPinned() {
} }
QString systemShortcutPath() { QString systemShortcutPath() {
static const int maxFileLen = MAX_PATH * 10; WCHAR wstrPath[kMaxFileLen] = {};
WCHAR wstrPath[maxFileLen]; if (GetEnvironmentVariable(L"APPDATA", wstrPath, kMaxFileLen)) {
if (GetEnvironmentVariable(L"APPDATA", wstrPath, maxFileLen)) {
auto appData = QDir(QString::fromStdWString(std::wstring(wstrPath))); auto appData = QDir(QString::fromStdWString(std::wstring(wstrPath)));
const auto path = appData.absolutePath(); const auto path = appData.absolutePath();
return path + u"/Microsoft/Windows/Start Menu/Programs/"_q; return path + u"/Microsoft/Windows/Start Menu/Programs/"_q;
@ -186,8 +207,11 @@ QString systemShortcutPath() {
return QString(); return QString();
} }
void cleanupShortcut() { void CleanupShortcut() {
static const int maxFileLen = MAX_PATH * 10; const auto myid = MyExecutablePathId();
if (!myid) {
return;
}
QString path = systemShortcutPath() + u"Telegram.lnk"_q; QString path = systemShortcutPath() + u"Telegram.lnk"_q;
std::wstring p = QDir::toNativeSeparators(path).toStdWString(); std::wstring p = QDir::toNativeSeparators(path).toStdWString();
@ -195,80 +219,69 @@ void cleanupShortcut() {
DWORD attributes = GetFileAttributes(p.c_str()); DWORD attributes = GetFileAttributes(p.c_str());
if (attributes >= 0xFFFFFFF) return; // file does not exist if (attributes >= 0xFFFFFFF) return; // file does not exist
ComPtr<IShellLink> shellLink; auto shellLink = base::WinRT::TryCreateInstance<IShellLink>(
HRESULT hr = CoCreateInstance( CLSID_ShellLink);
CLSID_ShellLink, if (!shellLink) {
nullptr, return;
CLSCTX_INPROC_SERVER, }
IID_PPV_ARGS(&shellLink));
if (!SUCCEEDED(hr)) return;
ComPtr<IPersistFile> persistFile; auto persistFile = shellLink.try_as<IPersistFile>();
hr = shellLink.As(&persistFile); if (!persistFile) {
if (!SUCCEEDED(hr)) return; return;
}
hr = persistFile->Load(p.c_str(), STGM_READWRITE); auto hr = persistFile->Load(p.c_str(), STGM_READWRITE);
if (!SUCCEEDED(hr)) return; if (!SUCCEEDED(hr)) return;
WCHAR szGotPath[MAX_PATH]; WCHAR szGotPath[MAX_PATH];
WIN32_FIND_DATA wfd; hr = shellLink->GetPath(szGotPath, MAX_PATH, nullptr, 0);
hr = shellLink->GetPath(
szGotPath,
MAX_PATH,
(WIN32_FIND_DATA*)&wfd,
SLGP_SHORTPATH);
if (!SUCCEEDED(hr)) return; if (!SUCCEEDED(hr)) return;
const auto full = cExeDir() + cExeName(); if (GetUniqueFileId(szGotPath) == myid) {
if (QDir::toNativeSeparators(full).toStdWString() == szGotPath) {
QFile().remove(path); QFile().remove(path);
} }
} }
bool validateShortcutAt(const QString &path) { bool validateShortcutAt(const QString &path) {
static const int maxFileLen = MAX_PATH * 10; const auto native = QDir::toNativeSeparators(path).toStdWString();
std::wstring p = QDir::toNativeSeparators(path).toStdWString(); DWORD attributes = GetFileAttributes(native.c_str());
if (attributes >= 0xFFFFFFF) {
return false; // file does not exist
}
DWORD attributes = GetFileAttributes(p.c_str()); auto shellLink = base::WinRT::TryCreateInstance<IShellLink>(
if (attributes >= 0xFFFFFFF) return false; // file does not exist CLSID_ShellLink);
if (!shellLink) {
ComPtr<IShellLink> shellLink;
HRESULT hr = CoCreateInstance(
CLSID_ShellLink,
nullptr,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&shellLink));
if (!SUCCEEDED(hr)) return false;
ComPtr<IPersistFile> persistFile;
hr = shellLink.As(&persistFile);
if (!SUCCEEDED(hr)) return false;
hr = persistFile->Load(p.c_str(), STGM_READWRITE);
if (!SUCCEEDED(hr)) return false;
WCHAR szGotPath[MAX_PATH];
WIN32_FIND_DATA wfd;
hr = shellLink->GetPath(
szGotPath,
MAX_PATH,
(WIN32_FIND_DATA*)&wfd,
SLGP_SHORTPATH);
if (!SUCCEEDED(hr)) return false;
const auto full = cExeDir() + cExeName();
if (QDir::toNativeSeparators(full).toStdWString() != szGotPath) {
return false; return false;
} }
ComPtr<IPropertyStore> propertyStore; auto persistFile = shellLink.try_as<IPersistFile>();
hr = shellLink.As(&propertyStore); if (!persistFile) {
return false;
}
auto hr = persistFile->Load(native.c_str(), STGM_READWRITE);
if (!SUCCEEDED(hr)) return false; if (!SUCCEEDED(hr)) return false;
WCHAR szGotPath[kMaxFileLen] = { 0 };
hr = shellLink->GetPath(szGotPath, kMaxFileLen, nullptr, 0);
if (!SUCCEEDED(hr)) {
return false;
}
if (GetUniqueFileId(szGotPath) != MyExecutablePathId()) {
return false;
}
auto propertyStore = shellLink.try_as<IPropertyStore>();
if (!propertyStore) {
return false;
}
PROPVARIANT appIdPropVar; PROPVARIANT appIdPropVar;
PROPVARIANT toastActivatorPropVar; PROPVARIANT toastActivatorPropVar;
hr = propertyStore->GetValue(getKey(), &appIdPropVar); hr = propertyStore->GetValue(Key(), &appIdPropVar);
if (!SUCCEEDED(hr)) return false; if (!SUCCEEDED(hr)) return false;
hr = propertyStore->GetValue( hr = propertyStore->GetValue(
@ -278,7 +291,7 @@ bool validateShortcutAt(const QString &path) {
WCHAR already[MAX_PATH]; WCHAR already[MAX_PATH];
hr = PropVariantToString(appIdPropVar, already, MAX_PATH); hr = PropVariantToString(appIdPropVar, already, MAX_PATH);
const auto good1 = SUCCEEDED(hr) && (getId() == already); const auto good1 = SUCCEEDED(hr) && (Id() == already);
const auto bad1 = !good1 && (appIdPropVar.vt != VT_EMPTY); const auto bad1 = !good1 && (appIdPropVar.vt != VT_EMPTY);
PropVariantClear(&appIdPropVar); PropVariantClear(&appIdPropVar);
@ -294,10 +307,10 @@ bool validateShortcutAt(const QString &path) {
return false; return false;
} }
hr = InitPropVariantFromString(getId().c_str(), &appIdPropVar); hr = InitPropVariantFromString(Id().c_str(), &appIdPropVar);
if (!SUCCEEDED(hr)) return false; if (!SUCCEEDED(hr)) return false;
hr = propertyStore->SetValue(getKey(), appIdPropVar); hr = propertyStore->SetValue(Key(), appIdPropVar);
PropVariantClear(&appIdPropVar); PropVariantClear(&appIdPropVar);
if (!SUCCEEDED(hr)) return false; if (!SUCCEEDED(hr)) return false;
@ -316,7 +329,7 @@ bool validateShortcutAt(const QString &path) {
if (!SUCCEEDED(hr)) return false; if (!SUCCEEDED(hr)) return false;
if (persistFile->IsDirty() == S_OK) { if (persistFile->IsDirty() == S_OK) {
hr = persistFile->Save(p.c_str(), TRUE); hr = persistFile->Save(native.c_str(), TRUE);
if (!SUCCEEDED(hr)) return false; if (!SUCCEEDED(hr)) return false;
} }
@ -338,7 +351,7 @@ bool checkInstalled(QString path = {}) {
|| validateShortcutAt(path + old); || validateShortcutAt(path + old);
} }
bool validateShortcut() { bool ValidateShortcut() {
QString path = systemShortcutPath(); QString path = systemShortcutPath();
if (path.isEmpty() || cExeName().isEmpty()) { if (path.isEmpty() || cExeName().isEmpty()) {
return false; return false;
@ -360,88 +373,109 @@ bool validateShortcut() {
} }
} }
ComPtr<IShellLink> shellLink; auto shellLink = base::WinRT::TryCreateInstance<IShellLink>(
HRESULT hr = CoCreateInstance( CLSID_ShellLink);
CLSID_ShellLink, if (!shellLink) {
nullptr, return false;
CLSCTX_INPROC_SERVER, }
IID_PPV_ARGS(&shellLink));
if (!SUCCEEDED(hr)) return false;
hr = shellLink->SetPath( auto hr = shellLink->SetPath(MyExecutablePath().c_str());
QDir::toNativeSeparators( if (!SUCCEEDED(hr)) {
cExeDir() + cExeName()).toStdWString().c_str()); return false;
if (!SUCCEEDED(hr)) return false; }
hr = shellLink->SetArguments(L""); hr = shellLink->SetArguments(L"");
if (!SUCCEEDED(hr)) return false; if (!SUCCEEDED(hr)) {
return false;
}
hr = shellLink->SetWorkingDirectory( hr = shellLink->SetWorkingDirectory(
QDir::toNativeSeparators( QDir::toNativeSeparators(
QDir(cWorkingDir()).absolutePath()).toStdWString().c_str()); QDir(cWorkingDir()).absolutePath()).toStdWString().c_str());
if (!SUCCEEDED(hr)) return false; if (!SUCCEEDED(hr)) {
return false;
}
ComPtr<IPropertyStore> propertyStore; auto propertyStore = shellLink.try_as<IPropertyStore>();
hr = shellLink.As(&propertyStore); if (!propertyStore) {
if (!SUCCEEDED(hr)) return false; return false;
}
PROPVARIANT appIdPropVar; PROPVARIANT appIdPropVar;
hr = InitPropVariantFromString(getId().c_str(), &appIdPropVar); hr = InitPropVariantFromString(Id().c_str(), &appIdPropVar);
if (!SUCCEEDED(hr)) return false; if (!SUCCEEDED(hr)) {
return false;
}
hr = propertyStore->SetValue(getKey(), appIdPropVar); hr = propertyStore->SetValue(Key(), appIdPropVar);
PropVariantClear(&appIdPropVar); PropVariantClear(&appIdPropVar);
if (!SUCCEEDED(hr)) return false; if (!SUCCEEDED(hr)) {
return false;
}
PROPVARIANT startPinPropVar; PROPVARIANT startPinPropVar;
hr = InitPropVariantFromUInt32( hr = InitPropVariantFromUInt32(
APPUSERMODEL_STARTPINOPTION_NOPINONINSTALL, APPUSERMODEL_STARTPINOPTION_NOPINONINSTALL,
&startPinPropVar); &startPinPropVar);
if (!SUCCEEDED(hr)) return false; if (!SUCCEEDED(hr)) {
return false;
}
hr = propertyStore->SetValue( hr = propertyStore->SetValue(
pkey_AppUserModel_StartPinOption, pkey_AppUserModel_StartPinOption,
startPinPropVar); startPinPropVar);
PropVariantClear(&startPinPropVar); PropVariantClear(&startPinPropVar);
if (!SUCCEEDED(hr)) return false; if (!SUCCEEDED(hr)) {
return false;
}
PROPVARIANT toastActivatorPropVar{}; PROPVARIANT toastActivatorPropVar{};
hr = InitPropVariantFromCLSID( hr = InitPropVariantFromCLSID(
__uuidof(ToastActivator), __uuidof(ToastActivator),
&toastActivatorPropVar); &toastActivatorPropVar);
if (!SUCCEEDED(hr)) return false; if (!SUCCEEDED(hr)) {
return false;
}
hr = propertyStore->SetValue( hr = propertyStore->SetValue(
pkey_AppUserModel_ToastActivator, pkey_AppUserModel_ToastActivator,
toastActivatorPropVar); toastActivatorPropVar);
PropVariantClear(&toastActivatorPropVar); PropVariantClear(&toastActivatorPropVar);
if (!SUCCEEDED(hr)) return false; if (!SUCCEEDED(hr)) {
return false;
}
hr = propertyStore->Commit(); hr = propertyStore->Commit();
if (!SUCCEEDED(hr)) return false; if (!SUCCEEDED(hr)) {
return false;
}
ComPtr<IPersistFile> persistFile; auto persistFile = shellLink.try_as<IPersistFile>();
hr = shellLink.As(&persistFile); if (!persistFile) {
if (!SUCCEEDED(hr)) return false; return false;
}
hr = persistFile->Save( hr = persistFile->Save(
QDir::toNativeSeparators(path).toStdWString().c_str(), QDir::toNativeSeparators(path).toStdWString().c_str(),
TRUE); TRUE);
if (!SUCCEEDED(hr)) return false; if (!SUCCEEDED(hr)) {
return false;
}
LOG(("App Info: Shortcut created and validated at \"%1\"").arg(path)); LOG(("App Info: Shortcut created and validated at \"%1\"").arg(path));
return true; return true;
} }
const std::wstring &getId() { const std::wstring &Id() {
static const std::wstring BaseId(cAlphaVersion() static const auto BaseId = std::wstring(AppUserModelIdBase);
? AppUserModelIdAlpha
: AppUserModelIdRelease);
static auto CheckingInstalled = false; static auto CheckingInstalled = false;
if (CheckingInstalled) { if (CheckingInstalled) {
return BaseId; return BaseId;
} }
static const auto Installed = [] { static const auto Installed = [] {
#ifdef OS_WIN_STORE
return true;
#else // OS_WIN_STORE
CheckingInstalled = true; CheckingInstalled = true;
const auto guard = gsl::finally([] { const auto guard = gsl::finally([] {
CheckingInstalled = false; CheckingInstalled = false;
@ -453,6 +487,7 @@ const std::wstring &getId() {
CoUninitialize(); CoUninitialize();
}); });
return checkInstalled(); return checkInstalled();
#endif
}(); }();
if (Installed) { if (Installed) {
return BaseId; return BaseId;
@ -471,7 +506,7 @@ const std::wstring &getId() {
return PortableId; return PortableId;
} }
const PROPERTYKEY &getKey() { const PROPERTYKEY &Key() {
return pkey_AppUserModel_ID; return pkey_AppUserModel_ID;
} }

View File

@ -12,13 +12,37 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Platform { namespace Platform {
namespace AppUserModelId { namespace AppUserModelId {
void cleanupShortcut(); void CleanupShortcut();
void checkPinned(); void CheckPinned();
const std::wstring &getId(); [[nodiscard]] const std::wstring &Id();
bool validateShortcut(); bool ValidateShortcut();
const PROPERTYKEY &getKey(); [[nodiscard]] const PROPERTYKEY &Key();
[[nodiscard]] const std::wstring &MyExecutablePath();
struct UniqueFileId {
std::uint64_t part1 = 0;
std::uint64_t part2 = 0;
[[nodiscard]] bool valid() const {
return part1 || part2;
}
[[nodiscard]] explicit operator bool() const {
return valid();
}
[[nodiscard]] friend inline auto operator<=>(
UniqueFileId a,
UniqueFileId b) = default;
[[nodiscard]] friend inline bool operator==(
UniqueFileId a,
UniqueFileId b) = default;
};
[[nodiscard]] UniqueFileId GetUniqueFileId(LPCWSTR path);
[[nodiscard]] UniqueFileId MyExecutablePathId();
} // namespace AppUserModelId } // namespace AppUserModelId
} // namespace Platform } // namespace Platform