有问题的应用程序是使用 Qt 5.7 和 VS2015 编写的。上周出现了一个奇怪的问题,我正拼命想解决这个问题。我认为我们有足够的日志记录,但看起来不像。再一次,这个问题只出现在几个用户的机器上。
我们已确保部署在所有 Windows 机器上运行良好,并且该应用程序直到上周在这些机器上运行良好。现在它拒绝打开,但显示在任务栏中。
用户的机器安装了先决条件,即 VC++ 2015。还存在所有必要的 Qt DLL。该应用程序在其他所有机器上都可以正常工作。所以我认为问题出在机器上,但是,重新启动 2 个操作系统(Windows 7 和 Windows 2012),重新安装应用程序,不同版本都失败并显示相同的问题。
我现在的问题是
- 有没有办法在应用程序启动时记录所有依赖项(多次使用依赖walker,但想做类似于我们的日志的事情)。
- 如何诊断此问题以查明是否是看门狗应用程序或防病毒软件导致此问题?试图说服用户关闭防病毒软件以诊断问题。
我使用windeployqt来获取所有依赖项,并在测试时找出了所需的 DLL 的其余部分,例如 VC++ 2015。还在构建使用过的依赖 walker时。很少有人问像qt 应用程序无法启动和qt 应用程序启动问题都指向缺少 DLL。我知道情况并非如此,因为该应用程序在几乎所有 Windows 机器上都可以正常工作。所以我需要帮助和指导来记录依赖关系以及如何诊断这种情况。
如果租约让我知道,我可能会错过一些信息。我正在研究更多,当我得到一些额外的信息时,我会更新这篇文章。
还尝试在 main() 函数中添加大量日志记录。看看我能不能抓到什么。
int main(int argc, char *argv[])
{
// Setup signal handlers
#if(defined(USE_SIGNAL_HANDLERS) && !defined(_DEBUG))
SignalHandlerPointer previousHandler1 = signal (SIGINT, SignalHandler);
SignalHandlerPointer previousHandler2 = signal (SIGILL, SignalHandler);
SignalHandlerPointer previousHandler3 = signal (SIGFPE, SignalHandler);
SignalHandlerPointer previousHandler4 = signal (SIGSEGV, SignalHandler);
SignalHandlerPointer previousHandler5 = signal (SIGTERM, SignalHandler);
#ifdef Q_OS_WIN32
SignalHandlerPointer previousHandler6 = signal (SIGBREAK, SignalHandler);
#endif
SignalHandlerPointer previousHandler7 = signal (SIGABRT, SignalHandler);
#ifdef Q_OS_WIN32
SignalHandlerPointer previousHandler8 = signal (SIGABRT_COMPAT, SignalHandler);
#endif
#endif //USE_SIGNAL_HANDLERS
#if QT_VERSION >= 0x050000
//QT5 fix
#else
QApplication::setGraphicsSystem("raster");
#endif
#ifdef Q_OS_WIN
// MAC_OS: Windows only function
CoInitialize(NULL);
#endif
QApplication::setAttribute(Qt::AA_UseOpenGLES);
MyApplicationApplication a(argc, argv);
// We put the dumps in the appdata:
QString ard_path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
QDir dir(ard_path);
if (!dir.exists())
dir.mkpath(ard_path);
#ifdef DEBUG
//Setting crash dump path
QBreakpadInstance.setDumpPath(ard_path);
#endif // DEBUG
QtWebEngine::initialize();
MyApplication *vtf = 0;
bool allowFiles = true;
bool isLicensed = false;
QString errMsg = QString();
QErrorMessage *error = new QErrorMessage();
error->resize(300, 300);
START_EASYLOGGINGPP(argc, argv);
el::Loggers::setDefaultConfigurations(baseConfiguration(), true);
QApplication::setWindowIcon(QIcon(":/Resources/images/VTF-iconx4.png"));
QCoreApplication::setOrganizationName(VER_COMPANYNAME_STR);
QCoreApplication::setApplicationName(VER_PRODUCTNAME_STR);
QCoreApplication::setApplicationVersion(VER_PRODUCTVERSION_STR);
//part of ERGT-1038
QFont appFont = QApplication::font();
//Default is 8
appFont.setPointSize(10);
QApplication::setFont(appFont);
QTime time = QTime::currentTime();
qsrand((uint)time.msec());
#ifdef ENABLE_MULTILANGUAGE
// Load a language using string from the settings applied by the dialog
InstallTranslation("qt_", a);
InstallTranslation("MyApplication_", a);
InstallTranslation("qtbase_", a);
#endif
bool bTrial = false;
MyApplicationLicenseInfo *info = new MyApplicationLicenseInfo();
info->Initialize(LIC_FIRMCODE_AGD, LIC_PRODCODE_AGD, NUMBER_OF_ACTIVATION_SCHEMES, szActivationSchemes);
if ((info->UsagePeriod() > 0) || info->Error())
{
bTrial = false;
allowFiles = true; // Trial version
}
// Check if it has expired!!
#if(!defined(_DEBUG) && !defined(DISABLE_LOCAL_LICENCE_USAGE_CHECK))
if (info->UsagePeriod() > 0)
{
// Work out the end date
QDateTime dt = QDateTime::fromString(info->StartDate(), "yyyy/MM/dd hh:mm:ss");
dt = dt.addDays(info->UsagePeriod());
if (dt < QDateTime::currentDateTime())
{
QMessageBox::critical(NULL, VER_PRODUCTNAME_STR, QObject::tr("Your license has expired! Please contact sales."));
return -1;
}
}
#endif
CommandLineProcessor cmdProc;
isLicensed = true;
bool batchMode = false;
Globals *glob = NULL;
int iExec = 0;
try
{
if(a.arguments().length() > 1)
{
if (a.arguments().at(1) == "-batch") {
batchMode = true;
} else {
cmdProc.setLicenseInfo(info);
if (!cmdProc.ProcessParameters(a.arguments()))
{
// Quit - error!
QMessageBox::critical(NULL, VER_PRODUCTNAME_STR, QObject::tr("Error in command line: ") + cmdProc.ErrorMessage() + "\r\n\r\nExiting.", QMessageBox::Ok);
return -1;
}
else if (!cmdProc.ErrorMessage().isEmpty() && !cmdProc.failedToAuthenticate())
{
QMessageBox::warning(NULL, VER_PRODUCTNAME_STR, QObject::tr("Warning in command line: ") + cmdProc.ErrorMessage(), QMessageBox::Ok);
}
// Pass everything back to local variables
if (cmdProc.HashGiven()) // Note: '=' here is deliberate!!
{
isLicensed = cmdProc.IsLicensed();
//bTrial = cmdProc.Trial();
}
else
{
isLicensed = true; // Let WIBU be handled below
}
}
}
// End of parameter handling
QString whyItFailed = "";
bool hasError = info->Error();
bool wibuPres = info->WibuPresent();
bool isViewer = info->IsViewer();
if (isLicensed && !(info->Error() && info->WibuPresent()))
{
whyItFailed += "Empty license container";
}
if (!wibuPres)
{
whyItFailed += "Missing Wibu";
}
if(isLicensed && !(info->Error() && info->WibuPresent()))
{
QDir::setCurrent(QCoreApplication::applicationDirPath());
Q_INIT_RESOURCE(MyApplication);
glob = Globals::createInstance(info, allowFiles, bTrial, cmdProc.HashGiven(), isViewer);
if (cmdProc.ActionsFile().isEmpty() && !batchMode)
{
// Load in GUI mode as normal
vtf = new MyApplication(glob, NULL);
#ifdef Q_OS_MAC
QObject::connect(&a,SIGNAL(openFile(const QString &)),vtf,SLOT(openFile(const QString &)));
#endif
if (cmdProc.Service() != 0)
glob->setService(cmdProc.Service());
else if (cmdProc.failedToAuthenticate())
{
LogInDialog lid(LogInDialog::MyLogin, glob, NULL, NULL, cmdProc.ServiceUrl(), cmdProc.ServiceUser());
if (lid.exec() == QDialog::Accepted)
{
glob->Service()->setCurrentProj(cmdProc.ProjectId());
glob->Service()->setCurrentVers(cmdProc.VersionId());
glob->Service()->setFirstFlowLevel(cmdProc.LevelId());
glob->Service()->setFirstFlowName(cmdProc.FlowName());
}
}
// ctx->Initialize() fired off inside VisualTestFlow constructor to avoid looped references
vtf->setWindowState(Qt::WindowMaximized);
vtf->show();
// MyApplication window needs to be created first so that the error messages show
if(!cmdProc.LoadFile().isEmpty())
{
vtf->open(QScopedPointer<FlowLocation>(new FlowLocation(cmdProc.LoadFile())).data());
}
if (glob->Service() != NULL)
{
vtf->tryOpenFirstFlow();
}
QTimer::singleShot(0, vtf, SLOT(windowInitialized()));
a.setStyleSheet(glob->Style()->globalStyle());
}
else
{
glob->SetCommandLine(true);
if (!batchMode) {
// Execute as actions
ActionsFileExecutor *exec = new ActionsFileExecutor(cmdProc.ActionsFile(), cmdProc.LogFile(), glob);
// Check errors
if (!exec->error().isEmpty())
{
// Logs already gone - let's get out of here!
iExec = -1000;
}
else
{
// Execute
iExec = exec->Execute();
}
delete exec;
exec = NULL;
} else {
#ifdef Q_OS_WIN
#ifdef USE_CONSOLE
AllocConsole();
FILE *pFileCon = NULL;
pFileCon = freopen("CONOUT$", "w", stdout);
COORD coordInfo;
coordInfo.X = 130;
coordInfo.Y = 9000;
SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coordInfo);
SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS);
#endif
#endif
BatchScriptCommandProcessor cmdPro(glob);
cmdPro.ParseArguments(a.arguments());
}
}
} else {
LOG(ERROR) << whyItFailed;
error->setWindowTitle(QObject::tr("Error"));
if(glob != NULL && glob->Service() != NULL)
{
if(glob->Service()->error())
error->showMessage(QObject::tr("My service fault:") + "<br/><br/>" + glob->Service()->lastError());
else
error->showMessage(QObject::tr("My service reported that the specified user is not licensed to use %1").arg(VER_PRODUCTNAME_STR));
}
else
{
//error->showMessage(QObject::tr("This user/machine is not licensed to use %1").arg(VER_PRODUCTNAME_STR));
}
}
if (glob == NULL || !glob->IsCommandLine())
{
iExec = a.exec();
}
}
catch (std::runtime_error &ex)
{
// Note: this is where we should put in the "Send error report" function
// Check if the user wants to save
if (true) //cmdProc.ActionsFile().isEmpty())
{
QString sMessage = QObject::tr("%1 has encountered a fatal error: %2").arg(QString(VER_PRODUCTNAME_STR)).arg(QString(ex.what()));
QString sTitle = QObject::tr("Runtime Exception");
LOG(ERROR) << QString(VER_PRODUCTVERSION_STR) << sMessage;
// GTID-2764: Work out if we can save to file or repository
int iIndex = 0;
QList<CrashSaveOptions> listOptions;
if (glob != NULL)
{
if (glob->GetAllowFiles())
{
listOptions.append(CrashSaveOptions(QObject::tr("To File"), iIndex, false, CrashSaveOptions::Yes));
iIndex++;
}
if (glob->Service() != NULL && glob->Service()->sessionID() > 0)
{
listOptions.append(CrashSaveOptions(QObject::tr("To Repository"), iIndex, true, CrashSaveOptions::Yes));
}
}
if(glob != NULL && glob->ContextList() != NULL && !glob->ContextList()->isEmpty())
{
foreach(GlobalContext *ctx, *glob->ContextList())
ctx->unlockFlow();
sMessage += "\r\n\r\n" + QObject::tr("Would you like to attempt to save ");
foreach(GlobalContext *ctx, *glob->ContextList())
{
if(ctx == NULL || ctx->GetProperties() == NULL || ctx->Scene() == NULL || !ctx->Scene()->changesMade())
continue;
try
{
CrashSaveOptions cr = saveOnCrash(sTitle, sMessage + "\"" + ctx->GetProperties()->title() + "\"", listOptions, vtf);
if (cr.dosave == CrashSaveOptions::Yes)
{
if (cr.rep)
{
ctx->saveRep();
ctx->unlockFlow();
}
else
{
ctx->saveFile();
}
}
else if(cr.dosave == CrashSaveOptions::NoToAll)
{
break;
}
}
catch (std::runtime_error &ex)
{
continue;
}
}
}
else
{
QMessageBox::critical(NULL, sTitle, sMessage, QMessageBox::Ok);
}
}
else
{
// ActionsFileExecutor has its own try...catch for logging
}
}
if(vtf != NULL)
delete vtf;
if (glob != NULL)
{
Globals::deleteInstance();
glob = NULL;
}
//_CrtDumpMemoryLeaks();
return iExec;
}