"Fossies" - the Fresh Open Source Software Archive

Member "tdesktop-2.6.1/Telegram/SourceFiles/platform/win/specific_win.cpp" (24 Feb 2021, 18899 Bytes) of package /linux/misc/tdesktop-2.6.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "specific_win.cpp" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 2.5.9_vs_2.6.0.

    1 /*
    2 This file is part of Telegram Desktop,
    3 the official desktop application for the Telegram messaging service.
    4 
    5 For license and copyright information please follow this link:
    6 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
    7 */
    8 #include "platform/win/specific_win.h"
    9 
   10 #include "platform/win/main_window_win.h"
   11 #include "platform/win/notifications_manager_win.h"
   12 #include "platform/win/windows_app_user_model_id.h"
   13 #include "platform/win/windows_dlls.h"
   14 #include "base/platform/base_platform_info.h"
   15 #include "base/call_delayed.h"
   16 #include "lang/lang_keys.h"
   17 #include "mainwindow.h"
   18 #include "mainwidget.h"
   19 #include "history/history_location_manager.h"
   20 #include "storage/localstorage.h"
   21 #include "core/crash_reports.h"
   22 
   23 #include <QtCore/QOperatingSystemVersion>
   24 #include <QtWidgets/QApplication>
   25 #include <QtWidgets/QDesktopWidget>
   26 #include <QtGui/QDesktopServices>
   27 #include <QtGui/QWindow>
   28 #include <qpa/qplatformnativeinterface.h>
   29 
   30 #include <Shobjidl.h>
   31 #include <shellapi.h>
   32 
   33 #include <roapi.h>
   34 #include <wrl/client.h>
   35 
   36 #include <openssl/conf.h>
   37 #include <openssl/engine.h>
   38 #include <openssl/err.h>
   39 
   40 #include <dbghelp.h>
   41 #include <shlobj.h>
   42 #include <Shlwapi.h>
   43 #include <Strsafe.h>
   44 #include <Windowsx.h>
   45 #include <WtsApi32.h>
   46 
   47 #include <SDKDDKVer.h>
   48 
   49 #include <sal.h>
   50 #include <Psapi.h>
   51 #include <strsafe.h>
   52 #include <ObjBase.h>
   53 #include <propvarutil.h>
   54 #include <functiondiscoverykeys.h>
   55 #include <intsafe.h>
   56 #include <guiddef.h>
   57 
   58 #ifndef DCX_USESTYLE
   59 #define DCX_USESTYLE 0x00010000
   60 #endif
   61 
   62 #ifndef WM_NCPOINTERUPDATE
   63 #define WM_NCPOINTERUPDATE 0x0241
   64 #define WM_NCPOINTERDOWN 0x0242
   65 #define WM_NCPOINTERUP 0x0243
   66 #endif
   67 
   68 using namespace Microsoft::WRL;
   69 using namespace Platform;
   70 
   71 namespace {
   72 
   73 bool themeInited = false;
   74 bool finished = true;
   75 QMargins simpleMargins, margins;
   76 HICON bigIcon = 0, smallIcon = 0, overlayIcon = 0;
   77 
   78 class _PsInitializer {
   79 public:
   80     _PsInitializer() {
   81         Dlls::start();
   82     }
   83 };
   84 _PsInitializer _psInitializer;
   85 
   86 BOOL CALLBACK _ActivateProcess(HWND hWnd, LPARAM lParam) {
   87     uint64 &processId(*(uint64*)lParam);
   88 
   89     DWORD dwProcessId;
   90     ::GetWindowThreadProcessId(hWnd, &dwProcessId);
   91 
   92     if ((uint64)dwProcessId == processId) { // found top-level window
   93         static const int32 nameBufSize = 1024;
   94         WCHAR nameBuf[nameBufSize];
   95         int32 len = GetWindowText(hWnd, nameBuf, nameBufSize);
   96         if (len && len < nameBufSize) {
   97             if (QRegularExpression(qsl("^Telegram(\\s*\\(\\d+\\))?$")).match(QString::fromStdWString(nameBuf)).hasMatch()) {
   98                 BOOL res = ::SetForegroundWindow(hWnd);
   99                 ::SetFocus(hWnd);
  100                 return FALSE;
  101             }
  102         }
  103     }
  104     return TRUE;
  105 }
  106 
  107 }
  108 
  109 void psActivateProcess(uint64 pid) {
  110     if (pid) {
  111         ::EnumWindows((WNDENUMPROC)_ActivateProcess, (LPARAM)&pid);
  112     }
  113 }
  114 
  115 QString psAppDataPath() {
  116     static const int maxFileLen = MAX_PATH * 10;
  117     WCHAR wstrPath[maxFileLen];
  118     if (GetEnvironmentVariable(L"APPDATA", wstrPath, maxFileLen)) {
  119         QDir appData(QString::fromStdWString(std::wstring(wstrPath)));
  120 #ifdef OS_WIN_STORE
  121         return appData.absolutePath() + qsl("/Telegram Desktop UWP/");
  122 #else // OS_WIN_STORE
  123         return appData.absolutePath() + '/' + AppName.utf16() + '/';
  124 #endif // OS_WIN_STORE
  125     }
  126     return QString();
  127 }
  128 
  129 QString psAppDataPathOld() {
  130     static const int maxFileLen = MAX_PATH * 10;
  131     WCHAR wstrPath[maxFileLen];
  132     if (GetEnvironmentVariable(L"APPDATA", wstrPath, maxFileLen)) {
  133         QDir appData(QString::fromStdWString(std::wstring(wstrPath)));
  134         return appData.absolutePath() + '/' + AppNameOld.utf16() + '/';
  135     }
  136     return QString();
  137 }
  138 
  139 void psDoCleanup() {
  140     try {
  141         psAutoStart(false, true);
  142         psSendToMenu(false, true);
  143         AppUserModelId::cleanupShortcut();
  144     } catch (...) {
  145     }
  146 }
  147 
  148 QRect psDesktopRect() {
  149     static QRect _monitorRect;
  150     static crl::time _monitorLastGot = 0;
  151     auto tnow = crl::now();
  152     if (tnow > _monitorLastGot + 1000LL || tnow < _monitorLastGot) {
  153         _monitorLastGot = tnow;
  154         HMONITOR hMonitor = MonitorFromWindow(App::wnd()->psHwnd(), MONITOR_DEFAULTTONEAREST);
  155         if (hMonitor) {
  156             MONITORINFOEX info;
  157             info.cbSize = sizeof(info);
  158             GetMonitorInfo(hMonitor, &info);
  159             _monitorRect = QRect(info.rcWork.left, info.rcWork.top, info.rcWork.right - info.rcWork.left, info.rcWork.bottom - info.rcWork.top);
  160         } else {
  161             _monitorRect = QApplication::desktop()->availableGeometry(App::wnd());
  162         }
  163     }
  164     return _monitorRect;
  165 }
  166 
  167 int psCleanup() {
  168     __try
  169     {
  170         psDoCleanup();
  171     }
  172     __except(EXCEPTION_EXECUTE_HANDLER)
  173     {
  174         return 0;
  175     }
  176     return 0;
  177 }
  178 
  179 void psDoFixPrevious() {
  180     try {
  181         static const int bufSize = 4096;
  182         DWORD checkType = 0;
  183         DWORD checkSize = bufSize * 2;
  184         WCHAR checkStr[bufSize] = { 0 };
  185         HKEY newKey1 = nullptr;
  186         HKEY newKey2 = nullptr;
  187         HKEY oldKey1 = nullptr;
  188         HKEY oldKey2 = nullptr;
  189 
  190         const auto appId = AppId.utf16();
  191         const auto newKeyStr1 = QString("Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\%1_is1").arg(appId).toStdWString();
  192         const auto newKeyStr2 = QString("Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\%1_is1").arg(appId).toStdWString();
  193         const auto oldKeyStr1 = QString("SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\%1_is1").arg(appId).toStdWString();
  194         const auto oldKeyStr2 = QString("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\%1_is1").arg(appId).toStdWString();
  195         const auto newKeyRes1 = RegOpenKeyEx(HKEY_CURRENT_USER, newKeyStr1.c_str(), 0, KEY_READ, &newKey1);
  196         const auto newKeyRes2 = RegOpenKeyEx(HKEY_CURRENT_USER, newKeyStr2.c_str(), 0, KEY_READ, &newKey2);
  197         const auto oldKeyRes1 = RegOpenKeyEx(HKEY_LOCAL_MACHINE, oldKeyStr1.c_str(), 0, KEY_READ, &oldKey1);
  198         const auto oldKeyRes2 = RegOpenKeyEx(HKEY_LOCAL_MACHINE, oldKeyStr2.c_str(), 0, KEY_READ, &oldKey2);
  199 
  200         const auto existNew1 = (newKeyRes1 == ERROR_SUCCESS) && (RegQueryValueEx(newKey1, L"InstallDate", 0, &checkType, (BYTE*)checkStr, &checkSize) == ERROR_SUCCESS); checkSize = bufSize * 2;
  201         const auto existNew2 = (newKeyRes2 == ERROR_SUCCESS) && (RegQueryValueEx(newKey2, L"InstallDate", 0, &checkType, (BYTE*)checkStr, &checkSize) == ERROR_SUCCESS); checkSize = bufSize * 2;
  202         const auto existOld1 = (oldKeyRes1 == ERROR_SUCCESS) && (RegQueryValueEx(oldKey1, L"InstallDate", 0, &checkType, (BYTE*)checkStr, &checkSize) == ERROR_SUCCESS); checkSize = bufSize * 2;
  203         const auto existOld2 = (oldKeyRes2 == ERROR_SUCCESS) && (RegQueryValueEx(oldKey2, L"InstallDate", 0, &checkType, (BYTE*)checkStr, &checkSize) == ERROR_SUCCESS); checkSize = bufSize * 2;
  204 
  205         if (newKeyRes1 == ERROR_SUCCESS) RegCloseKey(newKey1);
  206         if (newKeyRes2 == ERROR_SUCCESS) RegCloseKey(newKey2);
  207         if (oldKeyRes1 == ERROR_SUCCESS) RegCloseKey(oldKey1);
  208         if (oldKeyRes2 == ERROR_SUCCESS) RegCloseKey(oldKey2);
  209 
  210         if (existNew1 || existNew2) {
  211             const auto deleteKeyRes1 = existOld1 ? RegDeleteKey(HKEY_LOCAL_MACHINE, oldKeyStr1.c_str()) : ERROR_SUCCESS;
  212             const auto deleteKeyRes2 = existOld2 ? RegDeleteKey(HKEY_LOCAL_MACHINE, oldKeyStr2.c_str()) : ERROR_SUCCESS;
  213         }
  214 
  215         QString userDesktopLnk, commonDesktopLnk;
  216         WCHAR userDesktopFolder[MAX_PATH], commonDesktopFolder[MAX_PATH];
  217         HRESULT userDesktopRes = SHGetFolderPath(0, CSIDL_DESKTOPDIRECTORY, 0, SHGFP_TYPE_CURRENT, userDesktopFolder);
  218         HRESULT commonDesktopRes = SHGetFolderPath(0, CSIDL_COMMON_DESKTOPDIRECTORY, 0, SHGFP_TYPE_CURRENT, commonDesktopFolder);
  219         if (SUCCEEDED(userDesktopRes)) {
  220             userDesktopLnk = QString::fromWCharArray(userDesktopFolder) + "\\Telegram.lnk";
  221         }
  222         if (SUCCEEDED(commonDesktopRes)) {
  223             commonDesktopLnk = QString::fromWCharArray(commonDesktopFolder) + "\\Telegram.lnk";
  224         }
  225         QFile userDesktopFile(userDesktopLnk), commonDesktopFile(commonDesktopLnk);
  226         if (QFile::exists(userDesktopLnk) && QFile::exists(commonDesktopLnk) && userDesktopLnk != commonDesktopLnk) {
  227             bool removed = QFile::remove(commonDesktopLnk);
  228         }
  229     } catch (...) {
  230     }
  231 }
  232 
  233 int psFixPrevious() {
  234     __try
  235     {
  236         psDoFixPrevious();
  237     }
  238     __except(EXCEPTION_EXECUTE_HANDLER)
  239     {
  240         return 0;
  241     }
  242     return 0;
  243 }
  244 
  245 namespace Platform {
  246 namespace ThirdParty {
  247 namespace {
  248 
  249 void StartOpenSSL() {
  250     // Don't use dynamic OpenSSL config, it can load unwanted DLLs.
  251     OPENSSL_load_builtin_modules();
  252     ENGINE_load_builtin_engines();
  253     ERR_clear_error();
  254     OPENSSL_no_config();
  255 }
  256 
  257 } // namespace
  258 
  259 void start() {
  260     StartOpenSSL();
  261 }
  262 
  263 } // namespace ThirdParty
  264 
  265 void start() {
  266     Dlls::init();
  267 }
  268 
  269 void finish() {
  270 }
  271 
  272 void SetApplicationIcon(const QIcon &icon) {
  273     QApplication::setWindowIcon(icon);
  274 }
  275 
  276 QString SingleInstanceLocalServerName(const QString &hash) {
  277     return qsl("Global\\") + hash + '-' + cGUIDStr();
  278 }
  279 
  280 std::optional<bool> IsDarkMode() {
  281     static const auto kSystemVersion = QOperatingSystemVersion::current();
  282     static const auto kDarkModeAddedVersion = QOperatingSystemVersion(
  283         QOperatingSystemVersion::Windows,
  284         10,
  285         0,
  286         17763);
  287     static const auto kSupported = (kSystemVersion >= kDarkModeAddedVersion);
  288     if (!kSupported) {
  289         return std::nullopt;
  290     }
  291 
  292     const auto keyName = L""
  293         "Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize";
  294     const auto valueName = L"AppsUseLightTheme";
  295     auto key = HKEY();
  296     auto result = RegOpenKeyEx(HKEY_CURRENT_USER, keyName, 0, KEY_READ, &key);
  297     if (result != ERROR_SUCCESS) {
  298         return std::nullopt;
  299     }
  300 
  301     DWORD value = 0, type = 0, size = sizeof(value);
  302     result = RegQueryValueEx(key, valueName, 0, &type, (LPBYTE)&value, &size);
  303     RegCloseKey(key);
  304     if (result != ERROR_SUCCESS) {
  305         return std::nullopt;
  306     }
  307 
  308     return (value == 0);
  309 }
  310 
  311 bool AutostartSupported() {
  312     return !IsWindowsStoreBuild();
  313 }
  314 
  315 } // namespace Platform
  316 
  317 namespace {
  318     void _psLogError(const char *str, LSTATUS code) {
  319         LPWSTR errorTextFormatted = nullptr;
  320         auto formatFlags = FORMAT_MESSAGE_FROM_SYSTEM
  321             | FORMAT_MESSAGE_ALLOCATE_BUFFER
  322             | FORMAT_MESSAGE_IGNORE_INSERTS;
  323         FormatMessage(
  324             formatFlags,
  325             NULL,
  326             code,
  327             MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  328             (LPTSTR)&errorTextFormatted,
  329             0,
  330             0);
  331         auto errorText = errorTextFormatted
  332             ? errorTextFormatted
  333             : L"(Unknown error)";
  334         LOG((str).arg(code).arg(QString::fromStdWString(errorText)));
  335         LocalFree(errorTextFormatted);
  336     }
  337 
  338     bool _psOpenRegKey(LPCWSTR key, PHKEY rkey) {
  339         DEBUG_LOG(("App Info: opening reg key %1...").arg(QString::fromStdWString(key)));
  340         LSTATUS status = RegOpenKeyEx(HKEY_CURRENT_USER, key, 0, KEY_QUERY_VALUE | KEY_WRITE, rkey);
  341         if (status != ERROR_SUCCESS) {
  342             if (status == ERROR_FILE_NOT_FOUND) {
  343                 status = RegCreateKeyEx(HKEY_CURRENT_USER, key, 0, 0, REG_OPTION_NON_VOLATILE, KEY_QUERY_VALUE | KEY_WRITE, 0, rkey, 0);
  344                 if (status != ERROR_SUCCESS) {
  345                     QString msg = qsl("App Error: could not create '%1' registry key, error %2").arg(QString::fromStdWString(key)).arg(qsl("%1: %2"));
  346                     _psLogError(msg.toUtf8().constData(), status);
  347                     return false;
  348                 }
  349             } else {
  350                 QString msg = qsl("App Error: could not open '%1' registry key, error %2").arg(QString::fromStdWString(key)).arg(qsl("%1: %2"));
  351                 _psLogError(msg.toUtf8().constData(), status);
  352                 return false;
  353             }
  354         }
  355         return true;
  356     }
  357 
  358     bool _psSetKeyValue(HKEY rkey, LPCWSTR value, QString v) {
  359         static const int bufSize = 4096;
  360         DWORD defaultType, defaultSize = bufSize * 2;
  361         WCHAR defaultStr[bufSize] = { 0 };
  362         if (RegQueryValueEx(rkey, value, 0, &defaultType, (BYTE*)defaultStr, &defaultSize) != ERROR_SUCCESS || defaultType != REG_SZ || defaultSize != (v.size() + 1) * 2 || QString::fromStdWString(defaultStr) != v) {
  363             WCHAR tmp[bufSize] = { 0 };
  364             if (!v.isEmpty()) StringCbPrintf(tmp, bufSize, v.replace(QChar('%'), qsl("%%")).toStdWString().c_str());
  365             LSTATUS status = RegSetValueEx(rkey, value, 0, REG_SZ, (BYTE*)tmp, (wcslen(tmp) + 1) * sizeof(WCHAR));
  366             if (status != ERROR_SUCCESS) {
  367                 QString msg = qsl("App Error: could not set %1, error %2").arg(value ? ('\'' + QString::fromStdWString(value) + '\'') : qsl("(Default)")).arg("%1: %2");
  368                 _psLogError(msg.toUtf8().constData(), status);
  369                 return false;
  370             }
  371         }
  372         return true;
  373     }
  374 }
  375 
  376 namespace Platform {
  377 
  378 void RegisterCustomScheme(bool force) {
  379     if (cExeName().isEmpty()) {
  380         return;
  381     }
  382     DEBUG_LOG(("App Info: Checking custom scheme 'tg'..."));
  383 
  384     HKEY rkey;
  385     QString exe = QDir::toNativeSeparators(cExeDir() + cExeName());
  386 
  387     // Legacy URI scheme registration
  388     if (!_psOpenRegKey(L"Software\\Classes\\tg", &rkey)) return;
  389     if (!_psSetKeyValue(rkey, L"URL Protocol", QString())) return;
  390     if (!_psSetKeyValue(rkey, 0, qsl("URL:Telegram Link"))) return;
  391 
  392     if (!_psOpenRegKey(L"Software\\Classes\\tg\\DefaultIcon", &rkey)) return;
  393     if (!_psSetKeyValue(rkey, 0, '"' + exe + qsl(",1\""))) return;
  394 
  395     if (!_psOpenRegKey(L"Software\\Classes\\tg\\shell", &rkey)) return;
  396     if (!_psOpenRegKey(L"Software\\Classes\\tg\\shell\\open", &rkey)) return;
  397     if (!_psOpenRegKey(L"Software\\Classes\\tg\\shell\\open\\command", &rkey)) return;
  398     if (!_psSetKeyValue(rkey, 0, '"' + exe + qsl("\" -workdir \"") + cWorkingDir() + qsl("\" -- \"%1\""))) return;
  399 
  400     // URI scheme registration as Default Program - Windows Vista and above
  401     if (!_psOpenRegKey(L"Software\\Classes\\tdesktop.tg", &rkey)) return;
  402     if (!_psOpenRegKey(L"Software\\Classes\\tdesktop.tg\\DefaultIcon", &rkey)) return;
  403     if (!_psSetKeyValue(rkey, 0, '"' + exe + qsl(",1\""))) return;
  404 
  405     if (!_psOpenRegKey(L"Software\\Classes\\tdesktop.tg\\shell", &rkey)) return;
  406     if (!_psOpenRegKey(L"Software\\Classes\\tdesktop.tg\\shell\\open", &rkey)) return;
  407     if (!_psOpenRegKey(L"Software\\Classes\\tdesktop.tg\\shell\\open\\command", &rkey)) return;
  408     if (!_psSetKeyValue(rkey, 0, '"' + exe + qsl("\" -workdir \"") + cWorkingDir() + qsl("\" -- \"%1\""))) return;
  409 
  410     if (!_psOpenRegKey(L"Software\\TelegramDesktop", &rkey)) return;
  411     if (!_psOpenRegKey(L"Software\\TelegramDesktop\\Capabilities", &rkey)) return;
  412     if (!_psSetKeyValue(rkey, L"ApplicationName", qsl("Telegram Desktop"))) return;
  413     if (!_psSetKeyValue(rkey, L"ApplicationDescription", qsl("Telegram Desktop"))) return;
  414     if (!_psOpenRegKey(L"Software\\TelegramDesktop\\Capabilities\\UrlAssociations", &rkey)) return;
  415     if (!_psSetKeyValue(rkey, L"tg", qsl("tdesktop.tg"))) return;
  416 
  417     if (!_psOpenRegKey(L"Software\\RegisteredApplications", &rkey)) return;
  418     if (!_psSetKeyValue(rkey, L"Telegram Desktop", qsl("SOFTWARE\\TelegramDesktop\\Capabilities"))) return;
  419 }
  420 
  421 PermissionStatus GetPermissionStatus(PermissionType type) {
  422     if (type==PermissionType::Microphone) {
  423         PermissionStatus result=PermissionStatus::Granted;
  424         HKEY hKey;
  425         LSTATUS res=RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\CapabilityAccessManager\\ConsentStore\\microphone", 0, KEY_QUERY_VALUE, &hKey);
  426         if(res==ERROR_SUCCESS) {
  427             wchar_t buf[20];
  428             DWORD length=sizeof(buf);
  429             res=RegQueryValueEx(hKey, L"Value", NULL, NULL, (LPBYTE)buf, &length);
  430             if(res==ERROR_SUCCESS) {
  431                 if(wcscmp(buf, L"Deny")==0) {
  432                     result=PermissionStatus::Denied;
  433                 }
  434             }
  435             RegCloseKey(hKey);
  436         }
  437         return result;
  438     }
  439     return PermissionStatus::Granted;
  440 }
  441 
  442 void RequestPermission(PermissionType type, Fn<void(PermissionStatus)> resultCallback) {
  443     resultCallback(PermissionStatus::Granted);
  444 }
  445 
  446 void OpenSystemSettingsForPermission(PermissionType type) {
  447     if (type == PermissionType::Microphone) {
  448         crl::on_main([] {
  449             ShellExecute(
  450                 nullptr,
  451                 L"open",
  452                 L"ms-settings:privacy-microphone",
  453                 nullptr,
  454                 nullptr,
  455                 SW_SHOWDEFAULT);
  456         });
  457     }
  458 }
  459 
  460 bool OpenSystemSettings(SystemSettingsType type) {
  461     if (type == SystemSettingsType::Audio) {
  462         crl::on_main([] {
  463             WinExec("control.exe mmsys.cpl", SW_SHOW);
  464             //QDesktopServices::openUrl(QUrl("ms-settings:sound"));
  465         });
  466     }
  467     return true;
  468 }
  469 
  470 } // namespace Platform
  471 
  472 void psNewVersion() {
  473     Platform::RegisterCustomScheme();
  474     if (Local::oldSettingsVersion() < 8051) {
  475         AppUserModelId::checkPinned();
  476     }
  477     if (Local::oldSettingsVersion() > 0 && Local::oldSettingsVersion() < 10021) {
  478         // Reset icons cache, because we've changed the application icon.
  479         if (Dlls::SHChangeNotify) {
  480             Dlls::SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr);
  481         }
  482     }
  483 }
  484 
  485 void _manageAppLnk(bool create, bool silent, int path_csidl, const wchar_t *args, const wchar_t *description) {
  486     if (cExeName().isEmpty()) {
  487         return;
  488     }
  489     WCHAR startupFolder[MAX_PATH];
  490     HRESULT hr = SHGetFolderPath(0, path_csidl, 0, SHGFP_TYPE_CURRENT, startupFolder);
  491     if (SUCCEEDED(hr)) {
  492         QString lnk = QString::fromWCharArray(startupFolder) + '\\' + AppFile.utf16() + qsl(".lnk");
  493         if (create) {
  494             ComPtr<IShellLink> shellLink;
  495             hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink));
  496             if (SUCCEEDED(hr)) {
  497                 ComPtr<IPersistFile> persistFile;
  498 
  499                 QString exe = QDir::toNativeSeparators(cExeDir() + cExeName()), dir = QDir::toNativeSeparators(QDir(cWorkingDir()).absolutePath());
  500                 shellLink->SetArguments(args);
  501                 shellLink->SetPath(exe.toStdWString().c_str());
  502                 shellLink->SetWorkingDirectory(dir.toStdWString().c_str());
  503                 shellLink->SetDescription(description);
  504 
  505                 ComPtr<IPropertyStore> propertyStore;
  506                 hr = shellLink.As(&propertyStore);
  507                 if (SUCCEEDED(hr)) {
  508                     PROPVARIANT appIdPropVar;
  509                     hr = InitPropVariantFromString(AppUserModelId::getId(), &appIdPropVar);
  510                     if (SUCCEEDED(hr)) {
  511                         hr = propertyStore->SetValue(AppUserModelId::getKey(), appIdPropVar);
  512                         PropVariantClear(&appIdPropVar);
  513                         if (SUCCEEDED(hr)) {
  514                             hr = propertyStore->Commit();
  515                         }
  516                     }
  517                 }
  518 
  519                 hr = shellLink.As(&persistFile);
  520                 if (SUCCEEDED(hr)) {
  521                     hr = persistFile->Save(lnk.toStdWString().c_str(), TRUE);
  522                 } else {
  523                     if (!silent) LOG(("App Error: could not create interface IID_IPersistFile %1").arg(hr));
  524                 }
  525             } else {
  526                 if (!silent) LOG(("App Error: could not create instance of IID_IShellLink %1").arg(hr));
  527             }
  528         } else {
  529             QFile::remove(lnk);
  530         }
  531     } else {
  532         if (!silent) LOG(("App Error: could not get CSIDL %1 folder %2").arg(path_csidl).arg(hr));
  533     }
  534 }
  535 
  536 void psAutoStart(bool start, bool silent) {
  537     _manageAppLnk(start, silent, CSIDL_STARTUP, L"-autostart", L"Telegram autorun link.\nYou can disable autorun in Telegram settings.");
  538 }
  539 
  540 void psSendToMenu(bool send, bool silent) {
  541     _manageAppLnk(send, silent, CSIDL_SENDTO, L"-sendpath", L"Telegram send to link.\nYou can disable send to menu item in Telegram settings.");
  542 }
  543 
  544 void psWriteDump() {
  545 #ifndef DESKTOP_APP_DISABLE_CRASH_REPORTS
  546     PROCESS_MEMORY_COUNTERS data = { 0 };
  547     if (Dlls::GetProcessMemoryInfo
  548         && Dlls::GetProcessMemoryInfo(
  549             GetCurrentProcess(),
  550             &data,
  551             sizeof(data))) {
  552         const auto mb = 1024 * 1024;
  553         CrashReports::dump()
  554             << "Memory-usage: "
  555             << (data.PeakWorkingSetSize / mb)
  556             << " MB (peak), "
  557             << (data.WorkingSetSize / mb)
  558             << " MB (current)\n";
  559         CrashReports::dump()
  560             << "Pagefile-usage: "
  561             << (data.PeakPagefileUsage / mb)
  562             << " MB (peak), "
  563             << (data.PagefileUsage / mb)
  564             << " MB (current)\n";
  565     }
  566 #endif // DESKTOP_APP_DISABLE_CRASH_REPORTS
  567 }
  568 
  569 bool psLaunchMaps(const Data::LocationPoint &point) {
  570     return QDesktopServices::openUrl(qsl("bingmaps:?lvl=16&collection=point.%1_%2_Point").arg(point.latAsString()).arg(point.lonAsString()));
  571 }