diff --git a/src/libs/utils/winutils.cpp b/src/libs/utils/winutils.cpp index 7815d553fb9fcb0a44b7c52906ee9b15d16e42d5..4e7d31c3fcfe62a11115de73b57879ba22418230 100644 --- a/src/libs/utils/winutils.cpp +++ b/src/libs/utils/winutils.cpp @@ -31,6 +31,10 @@ #include <windows.h> #include <QtCore/QString> +#include <QtCore/QVector> +#include <QtCore/QDebug> +#include <QtCore/QLibrary> +#include <QtCore/QTextStream> namespace Utils { @@ -51,4 +55,60 @@ QTCREATOR_UTILS_EXPORT QString winErrorMessage(unsigned long error) return rc; } +QTCREATOR_UTILS_EXPORT QString winGetDLLVersion(WinDLLVersionType t, + const QString &name, + QString *errorMessage) +{ + // Resolve required symbols from the version.dll + typedef DWORD (APIENTRY *GetFileVersionInfoSizeProtoType)(LPCTSTR, LPDWORD); + typedef BOOL (APIENTRY *GetFileVersionInfoWProtoType)(LPCWSTR, DWORD, DWORD, LPVOID); + typedef BOOL (APIENTRY *VerQueryValueWProtoType)(const LPVOID, LPWSTR lpSubBlock, LPVOID, PUINT); + + const char *versionDLLC = "version.dll"; + QLibrary versionLib(QLatin1String(versionDLLC), 0); + if (!versionLib.load()) { + *errorMessage = QString::fromLatin1("Unable load %1: %2").arg(QLatin1String(versionDLLC), versionLib.errorString()); + return QString(); + } + // MinGW requires old-style casts + GetFileVersionInfoSizeProtoType getFileVersionInfoSizeW = (GetFileVersionInfoSizeProtoType)(versionLib.resolve("GetFileVersionInfoSizeW")); + GetFileVersionInfoWProtoType getFileVersionInfoW = (GetFileVersionInfoWProtoType)(versionLib.resolve("GetFileVersionInfoW")); + VerQueryValueWProtoType verQueryValueW = (VerQueryValueWProtoType)(versionLib.resolve("VerQueryValueW")); + if (!getFileVersionInfoSizeW || !getFileVersionInfoW || !verQueryValueW) { + *errorMessage = QString::fromLatin1("Unable to resolve all required symbols in %1").arg(QLatin1String(versionDLLC)); + return QString(); + } + + // Now go ahead, read version info resource + DWORD dummy = 0; + const LPCTSTR fileName = reinterpret_cast<LPCTSTR>(name.utf16()); // MinGWsy + const DWORD infoSize = (*getFileVersionInfoSizeW)(fileName, &dummy); + if (infoSize == 0) { + *errorMessage = QString::fromLatin1("Unable to determine the size of the version information of %1: %2").arg(name, winErrorMessage(GetLastError())); + return QString(); + } + QByteArray dataV(infoSize + 1, '\0'); + char *data = dataV.data(); + if (!(*getFileVersionInfoW)(fileName, dummy, infoSize, data)) { + *errorMessage = QString::fromLatin1("Unable to determine the version information of %1: %2").arg(name, winErrorMessage(GetLastError())); + return QString(); + } + VS_FIXEDFILEINFO *versionInfo; + UINT len = 0; + if (!(*verQueryValueW)(data, TEXT("\\"), &versionInfo, &len)) { + *errorMessage = QString::fromLatin1("Unable to determine version string of %1: %2").arg(name, winErrorMessage(GetLastError())); + return QString(); + } + QString rc; + switch (t) { + case WinDLLFileVersion: + QTextStream(&rc) << HIWORD(versionInfo->dwFileVersionMS) << '.' << LOWORD(versionInfo->dwFileVersionMS); + break; + case WinDLLProductVersion: + QTextStream(&rc) << HIWORD(versionInfo->dwProductVersionMS) << '.' << LOWORD(versionInfo->dwProductVersionMS); + break; + } + return rc; +} + } // namespace Utils diff --git a/src/libs/utils/winutils.h b/src/libs/utils/winutils.h index 19b8986ce1b6192321a73c720b5c812942cdabcd..8a73961ce3e0c6ed78b076bfd24a20e451916fb1 100644 --- a/src/libs/utils/winutils.h +++ b/src/libs/utils/winutils.h @@ -42,5 +42,10 @@ namespace Utils { // code as returned by the GetLastError()-API. QTCREATOR_UTILS_EXPORT QString winErrorMessage(unsigned long error); +// Determine a DLL version +enum WinDLLVersionType { WinDLLFileVersion, WinDLLProductVersion }; +QTCREATOR_UTILS_EXPORT QString winGetDLLVersion(WinDLLVersionType t, + const QString &name, + QString *errorMessage); } // namespace Utils #endif // WINUTILS_H diff --git a/src/plugins/debugger/cdb/cdbdebugengine.cpp b/src/plugins/debugger/cdb/cdbdebugengine.cpp index c7f04055708f0b3bd1baf0b28fc99fa57d98c92c..1086adf8e559a1112f1dd686fe1fe85bb264db43 100644 --- a/src/plugins/debugger/cdb/cdbdebugengine.cpp +++ b/src/plugins/debugger/cdb/cdbdebugengine.cpp @@ -236,7 +236,9 @@ static inline QString libPath(const QString &libName, const QString &path = QStr return rc; } -bool DebuggerEngineLibrary::init(const QString &path, QString *errorMessage) +bool DebuggerEngineLibrary::init(const QString &path, + QString *dbgEngDLL, + QString *errorMessage) { // Load the dependent help lib first const QString helpLibPath = libPath(QLatin1String(dbgHelpDllC), path); @@ -252,6 +254,7 @@ bool DebuggerEngineLibrary::init(const QString &path, QString *errorMessage) *errorMessage = msgLibLoadFailed(engineLibPath, lib.errorString()); return false; } + *dbgEngDLL = engineLibPath; // Locate symbols void *createFunc = lib.resolve(debugCreateFuncC); if (!createFunc) { @@ -317,9 +320,8 @@ bool CdbDebugEnginePrivate::init(QString *errorMessage) enum { bufLen = 10240 }; // Load the DLL DebuggerEngineLibrary lib; - if (!lib.init(m_options->path, errorMessage)) + if (!lib.init(m_options->path, &m_dbengDLL, errorMessage)) return false; - // Initialize the COM interfaces HRESULT hr; hr = lib.debugCreate( __uuidof(IDebugClient5), reinterpret_cast<void**>(&m_cif.debugClient)); @@ -587,9 +589,38 @@ void CdbDebugEnginePrivate::clearDisplay() manager()->registerHandler()->removeAll(); } +void CdbDebugEnginePrivate::checkVersion() +{ + static bool versionNotChecked = true; + // Check for version 6.11 (extended expression syntax) + if (versionNotChecked) { + versionNotChecked = false; + // Get engine DLL version + QString errorMessage; + const QString version = Utils::winGetDLLVersion(Utils::WinDLLProductVersion, m_dbengDLL, &errorMessage); + if (version.isEmpty()) { + qWarning("%s\n", qPrintable(errorMessage)); + return; + } + // Compare + const double minVersion = 6.11; + manager()->showDebuggerOutput(LogMisc, CdbDebugEngine::tr("Version: %1").arg(version)); + if (version.toDouble() < minVersion) { + const QString msg = CdbDebugEngine::tr( + "<html>The installed version of the <i>Debugging Tools for Windows</i> (%1) " + "is rather old. Upgrading to version %2 is recommended " + "for the proper display of Qt's data types.</html>").arg(version).arg(minVersion); + Core::ICore::instance()->showWarningWithOptions(CdbDebugEngine::tr("Debugger"), msg, QString(), + QLatin1String(Constants::DEBUGGER_SETTINGS_CATEGORY), + CdbOptionsPage::settingsId()); + } + } +} + void CdbDebugEngine::startDebugger(const QSharedPointer<DebuggerStartParameters> &sp) -{ +{ setState(AdapterStarting, Q_FUNC_INFO, __LINE__); + m_d->checkVersion(); if (m_d->m_hDebuggeeProcess) { warning(QLatin1String("Internal error: Attempt to start debugger while another process is being debugged.")); setState(AdapterStartFailed, Q_FUNC_INFO, __LINE__); diff --git a/src/plugins/debugger/cdb/cdbdebugengine_p.h b/src/plugins/debugger/cdb/cdbdebugengine_p.h index 73fb7d41fa0a85dc300a5bf3eddd45cc01760f83..b0a01d378961428133fded48999b46a1b5d024f2 100644 --- a/src/plugins/debugger/cdb/cdbdebugengine_p.h +++ b/src/plugins/debugger/cdb/cdbdebugengine_p.h @@ -57,7 +57,7 @@ class DebuggerEngineLibrary { public: DebuggerEngineLibrary(); - bool init(const QString &path, QString *errorMessage); + bool init(const QString &path, QString *dbgEngDLL, QString *errorMessage); inline HRESULT debugCreate(REFIID interfaceId, PVOID *interfaceHandle) const { return m_debugCreate(interfaceId, interfaceHandle); } @@ -108,8 +108,9 @@ struct CdbDebugEnginePrivate const QSharedPointer<CdbOptions> &options, CdbDebugEngine* engine); bool init(QString *errorMessage); - ~CdbDebugEnginePrivate(); + ~CdbDebugEnginePrivate(); + void checkVersion(); void processCreatedAttached(ULONG64 processHandle, ULONG64 initialThreadHandle); void setDebuggeeHandles(HANDLE hDebuggeeProcess, HANDLE hDebuggeeThread); @@ -177,6 +178,7 @@ struct CdbDebugEnginePrivate DebuggerStartMode m_mode; Utils::ConsoleProcess m_consoleStubProc; + QString m_dbengDLL; }; // helper functions