問題のアプリケーションは、Qt 5.7 と VS2015 を使用して作成されました。奇妙な問題が先週発生し、私は必死にこれを整理しようとしています. 十分なロギングがあると思いましたが、そうではありません。その後も、この問題はユーザーのマシンのいくつかでのみ見られました。
すべての Windows マシンで展開が正常に機能することを確認しており、アプリケーションは先週までこれらのマシンで正常に動作していました。現在、開くことを拒否していますが、タスクバーに表示されています。
ユーザーのマシンには、VC++ 2015 という前提条件がインストールされています。必要なすべての Qt DLL も存在します。アプリケーションは、他のすべてのマシンで問題なく動作します。したがって、問題はマシンにあると思いますが、2 つの OS (Windows 7 および Windows 2012) の再起動、アプリケーションのクリーンな再インストール、異なるバージョンはすべて失敗し、同じ問題が表示されます。
今の私の質問は
- アプリケーションの起動時にすべての依存関係をログに記録する方法はありますか (依存関係ウォーカーを数回使用しましたが、ログと同様のことをしたかったのです)。
- これを診断して、ウォッチドッグ アプリケーションまたはウイルス対策がこれを引き起こしているかどうかを調べるにはどうすればよいですか? 問題を診断するためにウイルス対策をオフにするようにユーザーを説得しようとしています。
windeployqtを使用してすべての依存関係を取得し、テスト中に VC++ 2015 などの必要な DLL の残りを見つけました。また、ビルド中に依存関係 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;
}