mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-05-06 02:16:04 +08:00
refs #910, obtain DLL and modules info
windows specific utility functions to obtain info about DLL / DLLs loaded (to be used to test P3D/FSX Simconnect DLLs)
This commit is contained in:
committed by
Mathew Sutcliffe
parent
705e83e156
commit
099680c054
@@ -59,7 +59,7 @@ SOURCES += *.cpp \
|
|||||||
$$PWD/weather/*.cpp
|
$$PWD/weather/*.cpp
|
||||||
|
|
||||||
win32 {
|
win32 {
|
||||||
LIBS *= -lShell32 -lDbghelp
|
LIBS *= -lShell32 -lDbghelp -lversion
|
||||||
}
|
}
|
||||||
win32-g++ {
|
win32-g++ {
|
||||||
LIBS *= -lpsapi
|
LIBS *= -lpsapi
|
||||||
|
|||||||
215
src/blackmisc/windllutils.cpp
Normal file
215
src/blackmisc/windllutils.cpp
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
/* Copyright (C) 2017
|
||||||
|
* Swift Project Community / Contributors
|
||||||
|
*
|
||||||
|
* This file is part of Swift Project. It is subject to the license terms in the LICENSE file found in the top-level
|
||||||
|
* directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project,
|
||||||
|
* including this file, may be copied, modified, propagated, or distributed except according to the terms
|
||||||
|
* contained in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "windllutils.h"
|
||||||
|
#include <QDir>
|
||||||
|
#include <QtGlobal>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
#include "Windows.h"
|
||||||
|
#include "winver.h"
|
||||||
|
#include "Strsafe.h"
|
||||||
|
#include <tlhelp32.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace BlackMisc
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
|
||||||
|
namespace PrivateWindows
|
||||||
|
{
|
||||||
|
struct LanguageCodePage
|
||||||
|
{
|
||||||
|
WORD wLanguage;
|
||||||
|
WORD wCodePage;
|
||||||
|
};
|
||||||
|
|
||||||
|
QString languageToIsoCode(const LanguageCodePage &codePage)
|
||||||
|
{
|
||||||
|
const LCID locale = codePage.wLanguage;
|
||||||
|
const int nchars = GetLocaleInfoW(locale, LOCALE_SISO639LANGNAME, NULL, 0);
|
||||||
|
std::vector<wchar_t> language(nchars);
|
||||||
|
GetLocaleInfoW(locale, LOCALE_SISO639LANGNAME, &language[0], nchars);
|
||||||
|
const QString iso = QString::fromWCharArray(&language[0], nchars);
|
||||||
|
return iso;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString queryToQString(const std::vector<BYTE> &pbVersionInfo, const std::vector<TCHAR> &subBlockNameBuffer)
|
||||||
|
{
|
||||||
|
UINT dwBytes = 0;
|
||||||
|
LPVOID lpBuffer = nullptr;
|
||||||
|
VerQueryValueW(&pbVersionInfo[0], &subBlockNameBuffer[0], &lpBuffer, &dwBytes);
|
||||||
|
const QString queryString = QString::fromWCharArray((const wchar_t *) lpBuffer, dwBytes);
|
||||||
|
return queryString;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString queryToQString(const std::vector<BYTE> &pbVersionInfo, const LanguageCodePage &codePage, _In_ LPCTSTR subBlockpSzFormat, std::vector<TCHAR> &subBlockNameBuffer)
|
||||||
|
{
|
||||||
|
HRESULT hr = StringCchPrintf(&subBlockNameBuffer[0], subBlockNameBuffer.size() - 1, subBlockpSzFormat, codePage.wLanguage, codePage.wCodePage);
|
||||||
|
if (FAILED(hr)) { return QString(""); }
|
||||||
|
return queryToQString(pbVersionInfo, subBlockNameBuffer);
|
||||||
|
}
|
||||||
|
} // ns
|
||||||
|
|
||||||
|
CWinDllUtils::DLLInfo CWinDllUtils::getDllInfo(const QString &dllFile)
|
||||||
|
{
|
||||||
|
// http://stackoverflow.com/questions/940707/how-do-i-programatically-get-the-version-of-a-dll-or-exe-file
|
||||||
|
DLLInfo result = {};
|
||||||
|
const QFile dllQFile(dllFile);
|
||||||
|
if (!dllQFile.exists())
|
||||||
|
{
|
||||||
|
result.errorMsg = QString("No file '%1'").arg(dllFile);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
const QString dll(QDir::toNativeSeparators(dllFile));
|
||||||
|
|
||||||
|
// temp std:string object, disposed at end of function
|
||||||
|
const std::wstring dllStr(dll.toStdWString());
|
||||||
|
|
||||||
|
DWORD dwSize = 0;
|
||||||
|
UINT puLenFileInfo = 0;
|
||||||
|
const TCHAR *pszFilePath = dllStr.c_str();
|
||||||
|
VS_FIXEDFILEINFO *pFileInfo = nullptr;
|
||||||
|
|
||||||
|
// Get the version info for the file requested
|
||||||
|
dwSize = GetFileVersionInfoSize(pszFilePath, NULL);
|
||||||
|
if (dwSize == 0)
|
||||||
|
{
|
||||||
|
result.errorMsg.sprintf("Error in GetFileVersionInfoSize: %d\n", GetLastError());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<BYTE> pbVersionInfo(dwSize);
|
||||||
|
if (!GetFileVersionInfo(pszFilePath, 0, dwSize, &pbVersionInfo[0]))
|
||||||
|
{
|
||||||
|
result.errorMsg.sprintf("Error in GetFileVersionInfo: %d\n", GetLastError());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Language independent version of VerQueryValue
|
||||||
|
if (!VerQueryValue(&pbVersionInfo[0], TEXT("\\"), (LPVOID *) &pFileInfo, &puLenFileInfo))
|
||||||
|
{
|
||||||
|
result.errorMsg.sprintf("Error in VerQueryValue: %d\n", GetLastError());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// pFileInfo->dwFileVersionMS is usually zero.
|
||||||
|
// However, you should check this if your version numbers seem to be wrong
|
||||||
|
result.fileVersion.sprintf("%d.%d.%d.%d",
|
||||||
|
(pFileInfo->dwFileVersionMS >> 16) & 0xffff,
|
||||||
|
(pFileInfo->dwFileVersionMS >> 0) & 0xffff,
|
||||||
|
(pFileInfo->dwFileVersionLS >> 16) & 0xffff,
|
||||||
|
(pFileInfo->dwFileVersionLS >> 0) & 0xffff
|
||||||
|
);
|
||||||
|
|
||||||
|
// pFileInfo->dwProductVersionMS is usually zero. However, you should check
|
||||||
|
// this if your version numbers seem to be wrong
|
||||||
|
result.productVersion.sprintf("%d.%d.%d.%d",
|
||||||
|
(pFileInfo->dwProductVersionMS >> 16) & 0xffff,
|
||||||
|
(pFileInfo->dwProductVersionMS >> 0) & 0xffff,
|
||||||
|
(pFileInfo->dwProductVersionLS >> 16) & 0xffff,
|
||||||
|
(pFileInfo->dwProductVersionLS >> 0) & 0xffff
|
||||||
|
);
|
||||||
|
|
||||||
|
PrivateWindows::LanguageCodePage *lpTranslate;
|
||||||
|
|
||||||
|
// Read the list of languages and code pages.
|
||||||
|
VerQueryValue(&pbVersionInfo[0], TEXT("\\VarFileInfo\\Translation"), (LPVOID *)&lpTranslate, &puLenFileInfo);
|
||||||
|
|
||||||
|
// Read the file description for each language and code page.
|
||||||
|
// All names: https://msdn.microsoft.com/en-us/library/windows/desktop/ms647464(v=vs.85).aspx
|
||||||
|
std::vector<TCHAR> subBlockNameBuffer(128);
|
||||||
|
for (UINT i = 0; i < (puLenFileInfo / sizeof(struct PrivateWindows::LanguageCodePage)); i++)
|
||||||
|
{
|
||||||
|
// Retrieve file description for language and code page "i".
|
||||||
|
const PrivateWindows::LanguageCodePage cp = lpTranslate[i];
|
||||||
|
result.iso = PrivateWindows::languageToIsoCode(cp);
|
||||||
|
result.fileDescription = PrivateWindows::queryToQString(pbVersionInfo, cp, TEXT("\\StringFileInfo\\%04x%04x\\FileDescription"), subBlockNameBuffer);
|
||||||
|
result.productName = PrivateWindows::queryToQString(pbVersionInfo, cp, TEXT("\\StringFileInfo\\%04x%04x\\ProductName"), subBlockNameBuffer);
|
||||||
|
result.productVersionName = PrivateWindows::queryToQString(pbVersionInfo, cp, TEXT("\\StringFileInfo\\%04x%04x\\ProductVersion"), subBlockNameBuffer);
|
||||||
|
result.originalFilename = PrivateWindows::queryToQString(pbVersionInfo, cp, TEXT("\\StringFileInfo\\%04x%04x\\OriginalFilename"), subBlockNameBuffer);
|
||||||
|
result.fullFilename = dllQFile.fileName();
|
||||||
|
|
||||||
|
//! \fixme currently stopping at first language, maybe need to change that in future
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// bye
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<CWinDllUtils::ProcessModule> CWinDllUtils::getModules(qint64 processId, const QString &nameFilter)
|
||||||
|
{
|
||||||
|
QList<CWinDllUtils::ProcessModule> results;
|
||||||
|
|
||||||
|
const DWORD dwPID = static_cast<DWORD>(processId < 0 ? QCoreApplication::applicationPid() : processId);
|
||||||
|
HANDLE hModuleSnap = INVALID_HANDLE_VALUE;
|
||||||
|
MODULEENTRY32 me32;
|
||||||
|
|
||||||
|
// Take a snapshot of all modules in the specified process.
|
||||||
|
hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID);
|
||||||
|
if (hModuleSnap == INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
// printError(TEXT("CreateToolhelp32Snapshot (of modules)"));
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the size of the structure before using it.
|
||||||
|
me32.dwSize = sizeof(MODULEENTRY32);
|
||||||
|
|
||||||
|
// Retrieve information about the first module,
|
||||||
|
// and exit if unsuccessful
|
||||||
|
if (!Module32First(hModuleSnap, &me32))
|
||||||
|
{
|
||||||
|
// printError(TEXT("Module32First")); // show cause of failure
|
||||||
|
CloseHandle(hModuleSnap); // clean the snapshot object
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now walk the module list of the process,
|
||||||
|
// and display information about each module
|
||||||
|
do
|
||||||
|
{
|
||||||
|
ProcessModule pm;
|
||||||
|
pm.moduleName = QString::fromWCharArray(static_cast<const wchar_t *>(&me32.szModule[0]));
|
||||||
|
if (nameFilter.isEmpty() || pm.moduleName.contains(nameFilter, Qt::CaseInsensitive))
|
||||||
|
{
|
||||||
|
pm.executable = QString::fromWCharArray(static_cast<const wchar_t *>(&me32.szExePath[0]));
|
||||||
|
pm.processId = static_cast<qint64>(me32.th32ProcessID);
|
||||||
|
results.append(pm);
|
||||||
|
if (!nameFilter.isEmpty()) { break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (Module32Next(hModuleSnap, &me32));
|
||||||
|
|
||||||
|
CloseHandle(hModuleSnap);
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
// Dummy functions
|
||||||
|
CWinDllUtils::DLLInfo CWinDllUtils::getDllInfo(const QString &dllFile)
|
||||||
|
{
|
||||||
|
Q_UNUSED(dllFile);
|
||||||
|
DLLInfo result = {};
|
||||||
|
result.errorMsg = QString("Only works on Windows");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<CWinDllUtils::ProcessModule> CWinDllUtils::getModules(qint64 processId, const QString &nameFilter)
|
||||||
|
{
|
||||||
|
Q_UNUSED(processId);
|
||||||
|
Q_UNUSED(nameFilter);
|
||||||
|
static const QList<CWinDllUtils::ProcessModule> results;
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // ns
|
||||||
72
src/blackmisc/windllutils.h
Normal file
72
src/blackmisc/windllutils.h
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
/* Copyright (C) 2017
|
||||||
|
* Swift Project Community / Contributors
|
||||||
|
*
|
||||||
|
* This file is part of Swift Project. It is subject to the license terms in the LICENSE file found in the top-level
|
||||||
|
* directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project,
|
||||||
|
* including this file, may be copied, modified, propagated, or distributed except according to the terms
|
||||||
|
* contained in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//! \file
|
||||||
|
|
||||||
|
#ifndef BLACKMISC_WINDLLUTILS_H
|
||||||
|
#define BLACKMISC_WINDLLUTILS_H
|
||||||
|
|
||||||
|
#include "blackmiscexport.h"
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace BlackMisc
|
||||||
|
{
|
||||||
|
//! Functions dealing with Windows DLLs
|
||||||
|
class BLACKMISC_EXPORT CWinDllUtils
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//! No ctor
|
||||||
|
CWinDllUtils() = delete;
|
||||||
|
|
||||||
|
//! Info about an DLL
|
||||||
|
struct DLLInfo
|
||||||
|
{
|
||||||
|
QString iso; //!< language iso code
|
||||||
|
QString errorMsg; //!< error message if any
|
||||||
|
QString fileVersion; //!< file version, like 3.2.1.3
|
||||||
|
QString fileDescription; //!< file description
|
||||||
|
QString productVersion; //!< product version, like 3.2.1.3
|
||||||
|
QString productVersionName; //!< version and name
|
||||||
|
QString productName; //!< product name
|
||||||
|
QString originalFilename; //!< original filename
|
||||||
|
QString fullFilename; //!< full filename
|
||||||
|
|
||||||
|
//! Product or file version
|
||||||
|
const QString &productOrFileVersion() const
|
||||||
|
{
|
||||||
|
if (!productVersionName.isEmpty()) { return productVersionName; }
|
||||||
|
if (!productVersion.isEmpty()) { return productVersion; }
|
||||||
|
return fileVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Summary
|
||||||
|
QString summary() const
|
||||||
|
{
|
||||||
|
if (!errorMsg.isEmpty()) { return errorMsg; }
|
||||||
|
static const QString s("Product: '%1' version: %2 language: %3 filename: '%4'");
|
||||||
|
return s.arg(productName, productOrFileVersion(), iso, fullFilename);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Process modules
|
||||||
|
struct ProcessModule
|
||||||
|
{
|
||||||
|
QString moduleName; //!< module / dll name
|
||||||
|
QString executable; //!< full executable path
|
||||||
|
qint64 processId = -1; //!< pid
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Get DDL
|
||||||
|
static DLLInfo getDllInfo(const QString &dllFile);
|
||||||
|
|
||||||
|
//! Process modules per id
|
||||||
|
static QList<ProcessModule> getModules(qint64 processId = -1, const QString &nameFilter = {});
|
||||||
|
};
|
||||||
|
} // ns
|
||||||
|
#endif // guard
|
||||||
Reference in New Issue
Block a user