mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-08-06 20:03:12 +00:00
237 lines
6.5 KiB
C++
237 lines
6.5 KiB
C++
/*
|
|
This file is part of Telegram Desktop,
|
|
the official desktop application for the Telegram messaging service.
|
|
|
|
For license and copyright information please follow this link:
|
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|
*/
|
|
#include "platform/win/launcher_win.h"
|
|
|
|
#include "core/crash_reports.h"
|
|
#include "core/update_checker.h"
|
|
|
|
#include <windows.h>
|
|
#include <shellapi.h>
|
|
#include <VersionHelpers.h>
|
|
|
|
#include <crtdbg.h>
|
|
#include <cstdlib>
|
|
|
|
namespace Platform {
|
|
namespace {
|
|
|
|
// Turn a CRT/STL assertion (or pure-virtual / invalid-parameter) into a real,
|
|
// crash-reported failure instead of a modal Abort/Retry/Ignore dialog that
|
|
// would block a headless test agent forever.
|
|
[[noreturn]] void TestAgentReportCrash(
|
|
const char *kind,
|
|
const QString &message) {
|
|
if (!message.isEmpty()) {
|
|
CrashReports::SetAnnotation("CrtAssert", message);
|
|
}
|
|
// Be loud on stderr too (captured when the launch redirects it to a file).
|
|
fprintf(stderr,
|
|
"\n[testagent] %s: %s\n",
|
|
kind,
|
|
message.toLocal8Bit().constData());
|
|
fflush(stderr);
|
|
|
|
// Crashes with an access violation -> caught by the crash reporter, dumped
|
|
// to tdata/working + tdata/dumps, and the process exits non-zero. No dialog.
|
|
Unexpected("Test agent: CRT/STL assertion violation.");
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
int TestAgentReportHook(int reportType, char *message, int *returnValue) {
|
|
if (returnValue) {
|
|
*returnValue = 0; // Do not break into the (absent) debugger.
|
|
}
|
|
if (reportType == _CRT_ASSERT || reportType == _CRT_ERROR) {
|
|
TestAgentReportCrash(
|
|
"assert",
|
|
QString::fromLocal8Bit(message ? message : ""));
|
|
} else if (message) {
|
|
fputs(message, stderr);
|
|
fflush(stderr);
|
|
}
|
|
return TRUE; // Handled -> suppress the default message box.
|
|
}
|
|
|
|
int TestAgentReportHookW(int reportType, wchar_t *message, int *returnValue) {
|
|
if (returnValue) {
|
|
*returnValue = 0;
|
|
}
|
|
if (reportType == _CRT_ASSERT || reportType == _CRT_ERROR) {
|
|
TestAgentReportCrash(
|
|
"assert",
|
|
QString::fromWCharArray(message ? message : L""));
|
|
} else if (message) {
|
|
fputws(message, stderr);
|
|
fflush(stderr);
|
|
}
|
|
return TRUE;
|
|
}
|
|
#endif // _DEBUG
|
|
|
|
void InstallTestAgentCrashHandling() {
|
|
// No Windows Error Reporting / general-protection / abort() message boxes —
|
|
// each would block the test agent waiting on a button it cannot press.
|
|
SetErrorMode(SEM_FAILCRITICALERRORS
|
|
| SEM_NOGPFAULTERRORBOX
|
|
| SEM_NOOPENFILEERRORBOX);
|
|
_set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
|
|
|
|
// Pure-virtual call / invalid CRT parameter -> reported crash, not a dialog.
|
|
_set_purecall_handler([] {
|
|
TestAgentReportCrash("purecall", u"Pure virtual function call"_q);
|
|
});
|
|
_set_invalid_parameter_handler([](
|
|
const wchar_t *expression,
|
|
const wchar_t *,
|
|
const wchar_t *,
|
|
unsigned int,
|
|
uintptr_t) {
|
|
TestAgentReportCrash(
|
|
"invalid-parameter",
|
|
QString::fromWCharArray(
|
|
expression ? expression : L"CRT invalid parameter"));
|
|
});
|
|
|
|
#ifdef _DEBUG
|
|
// assert()/_ASSERTE and STL bounds/iterator checks (_STL_VERIFY) report via
|
|
// _CrtDbgReport[W]; route them to stderr (never a window) and convert any
|
|
// assertion into a reported crash through the hooks above.
|
|
for (const auto type : { _CRT_WARN, _CRT_ERROR, _CRT_ASSERT }) {
|
|
_CrtSetReportMode(type, _CRTDBG_MODE_FILE);
|
|
_CrtSetReportFile(type, _CRTDBG_FILE_STDERR);
|
|
}
|
|
_CrtSetReportHook2(_CRT_RPTHOOK_INSTALL, TestAgentReportHook);
|
|
_CrtSetReportHookW2(_CRT_RPTHOOK_INSTALL, TestAgentReportHookW);
|
|
#endif // _DEBUG
|
|
}
|
|
|
|
} // namespace
|
|
|
|
Launcher::Launcher(int argc, char *argv[])
|
|
: Core::Launcher(argc, argv) {
|
|
}
|
|
|
|
void Launcher::initHook() {
|
|
if (cTestAgent()) {
|
|
InstallTestAgentCrashHandling();
|
|
}
|
|
}
|
|
|
|
std::optional<QStringList> Launcher::readArgumentsHook(
|
|
int argc,
|
|
char *argv[]) const {
|
|
auto count = 0;
|
|
if (const auto list = CommandLineToArgvW(GetCommandLine(), &count)) {
|
|
const auto guard = gsl::finally([&] { LocalFree(list); });
|
|
if (count > 0) {
|
|
auto result = QStringList();
|
|
result.reserve(count);
|
|
for (auto i = 0; i != count; ++i) {
|
|
result.push_back(QString::fromWCharArray(list[i]));
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
bool Launcher::launchUpdater(UpdaterLaunch action) {
|
|
if (cExeName().isEmpty()) {
|
|
return false;
|
|
}
|
|
|
|
const auto operation = (action == UpdaterLaunch::JustRelaunch)
|
|
? QString()
|
|
: (cWriteProtected()
|
|
? u"runas"_q
|
|
: QString());
|
|
const auto binaryPath = (action == UpdaterLaunch::JustRelaunch)
|
|
? (cExeDir() + cExeName())
|
|
: (cWriteProtected()
|
|
? (cWorkingDir() + u"tupdates/temp/Updater.exe"_q)
|
|
: (cExeDir() + u"Updater.exe"_q));
|
|
|
|
auto argumentsList = QStringList();
|
|
const auto pushArgument = [&](const QString &argument) {
|
|
argumentsList.push_back(argument.trimmed());
|
|
};
|
|
if (cLaunchMode() == LaunchModeAutoStart) {
|
|
pushArgument(u"-autostart"_q);
|
|
}
|
|
if (Logs::DebugEnabled()) {
|
|
pushArgument(u"-debug"_q);
|
|
}
|
|
if (cStartInTray()) {
|
|
pushArgument(u"-startintray"_q);
|
|
}
|
|
if (customWorkingDir()) {
|
|
pushArgument(u"-workdir"_q);
|
|
pushArgument('"' + cWorkingDir() + '"');
|
|
}
|
|
if (cDataFile() != u"data"_q) {
|
|
pushArgument(u"-key"_q);
|
|
pushArgument('"' + cDataFile() + '"');
|
|
}
|
|
|
|
if (action == UpdaterLaunch::JustRelaunch) {
|
|
pushArgument(u"-noupdate"_q);
|
|
if (cRestartingToSettings()) {
|
|
pushArgument(u"-tosettings"_q);
|
|
}
|
|
} else {
|
|
pushArgument(u"-update"_q);
|
|
pushArgument(u"-exename"_q);
|
|
pushArgument('"' + cExeName() + '"');
|
|
if (cWriteProtected()) {
|
|
pushArgument(u"-writeprotected"_q);
|
|
pushArgument('"' + cExeDir() + '"');
|
|
}
|
|
}
|
|
return launch(operation, binaryPath, argumentsList);
|
|
}
|
|
|
|
bool Launcher::launch(
|
|
const QString &operation,
|
|
const QString &binaryPath,
|
|
const QStringList &argumentsList) {
|
|
const auto convertPath = [](const QString &path) {
|
|
return QDir::toNativeSeparators(path).toStdWString();
|
|
};
|
|
const auto nativeBinaryPath = convertPath(binaryPath);
|
|
const auto nativeWorkingDir = convertPath(cWorkingDir());
|
|
const auto arguments = argumentsList.join(' ');
|
|
|
|
DEBUG_LOG(("Application Info: executing %1 %2"
|
|
).arg(binaryPath
|
|
).arg(arguments
|
|
));
|
|
|
|
Logs::closeMain();
|
|
CrashReports::Finish();
|
|
|
|
const auto hwnd = HWND(0);
|
|
const auto result = ShellExecute(
|
|
hwnd,
|
|
operation.isEmpty() ? nullptr : operation.toStdWString().c_str(),
|
|
nativeBinaryPath.c_str(),
|
|
arguments.toStdWString().c_str(),
|
|
nativeWorkingDir.empty() ? nullptr : nativeWorkingDir.c_str(),
|
|
SW_SHOWNORMAL);
|
|
if (int64(result) < 32) {
|
|
DEBUG_LOG(("Application Error: failed to execute %1, working directory: '%2', result: %3"
|
|
).arg(binaryPath
|
|
).arg(cWorkingDir()
|
|
).arg(int64(result)
|
|
));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
} // namespace Platform
|