swift
windllutils.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2017 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
4 #include "misc/windllutils.h"
5 
6 #include <QCoreApplication>
7 #include <QDir>
8 #include <QtGlobal>
9 
10 #ifdef Q_OS_WIN
11 // clang-format off
12 # include <windows.h>
13 # include <tlhelp32.h>
14 // clang-format on
15 #endif
16 
17 namespace swift::misc
18 {
19 #ifdef Q_OS_WIN
20 
21  namespace PrivateWindows
22  {
23  struct LanguageCodePage
24  {
25  WORD wLanguage;
26  WORD wCodePage;
27  };
28 
29  QString languageToIsoCode(LanguageCodePage codePage)
30  {
31  const LCID locale = codePage.wLanguage;
32  const int nchars = GetLocaleInfo(locale, LOCALE_SISO639LANGNAME, nullptr, 0);
33  std::vector<wchar_t> language(static_cast<size_t>(nchars));
34  GetLocaleInfo(locale, LOCALE_SISO639LANGNAME, language.data(), nchars);
35  const QString iso = QString::fromWCharArray(language.data(), nchars);
36  return iso;
37  }
38 
39  QString queryStringFileInfo(BYTE *pbVersionInfo, LanguageCodePage codePage, const QString &stringName)
40  {
41  constexpr int fieldWidth = 4;
42  constexpr int base = 16;
43  QString subBlockNameBuffer = QStringLiteral("\\StringFileInfo\\%1%2\\%3")
44  .arg(codePage.wLanguage, fieldWidth, base, QLatin1Char('0'))
45  .arg(codePage.wCodePage, fieldWidth, base, QLatin1Char('0'))
46  .arg(stringName);
47  UINT dwBytes = 0;
48  wchar_t *szQueryString = nullptr;
49  VerQueryValue(pbVersionInfo, subBlockNameBuffer.toStdWString().c_str(),
50  reinterpret_cast<LPVOID *>(&szQueryString), &dwBytes);
51  const QString queryString = QString::fromWCharArray(szQueryString, dwBytes);
52  return queryString;
53  }
54  } // namespace PrivateWindows
55 
56  CWinDllUtils::DLLInfo CWinDllUtils::getDllInfo(const QString &dllFile)
57  {
58  // http://stackoverflow.com/questions/940707/how-do-i-programatically-get-the-version-of-a-dll-or-exe-file
59  DLLInfo result;
60  const QFile dllQFile(dllFile);
61  if (!dllQFile.exists())
62  {
63  result.errorMsg = QStringLiteral("No file '%1'").arg(dllFile);
64  return result;
65  }
66  const QString dll(QDir::toNativeSeparators(dllFile));
67 
68  // temp std:string object, disposed at end of function
69  const std::wstring dllStr(dll.toStdWString());
70 
71  DWORD dwSize = 0;
72  UINT puLenFileInfo = 0;
73  const TCHAR *pszFilePath = dllStr.c_str();
74  VS_FIXEDFILEINFO *pFileInfo = nullptr;
75 
76  // Get the version info for the file requested
77  dwSize = GetFileVersionInfoSize(pszFilePath, nullptr);
78  if (dwSize == 0)
79  {
80  result.errorMsg = QStringLiteral("Error in GetFileVersionInfoSize: %1\n").arg(GetLastError());
81  return result;
82  }
83 
84  std::vector<BYTE> pbVersionInfo(dwSize);
85  if (!GetFileVersionInfo(pszFilePath, 0, dwSize, pbVersionInfo.data()))
86  {
87  result.errorMsg = QStringLiteral("Error in GetFileVersionInfo: %1\n").arg(GetLastError());
88  return result;
89  }
90 
91  // Language independent version of VerQueryValue
92  if (!VerQueryValue(pbVersionInfo.data(), TEXT("\\"), reinterpret_cast<LPVOID *>(&pFileInfo), &puLenFileInfo))
93  {
94  result.errorMsg = QStringLiteral("Error in VerQueryValue: %1\n").arg(GetLastError());
95  return result;
96  }
97 
98  // pFileInfo->dwFileVersionMS is usually zero.
99  // However, you should check this if your version numbers seem to be wrong
100  result.fileVersion = QStringLiteral("%1.%2.%3.%4")
101  .arg((pFileInfo->dwFileVersionMS >> 16) & 0xffff)
102  .arg((pFileInfo->dwFileVersionMS >> 0) & 0xffff)
103  .arg((pFileInfo->dwFileVersionLS >> 16) & 0xffff)
104  .arg((pFileInfo->dwFileVersionLS >> 0) & 0xffff);
105 
106  // pFileInfo->dwProductVersionMS is usually zero. However, you should check
107  // this if your version numbers seem to be wrong
108  result.productVersion = QStringLiteral("%1.%2.%3.%4")
109  .arg((pFileInfo->dwProductVersionMS >> 16) & 0xffff)
110  .arg((pFileInfo->dwProductVersionMS >> 0) & 0xffff)
111  .arg((pFileInfo->dwProductVersionLS >> 16) & 0xffff)
112  .arg((pFileInfo->dwProductVersionLS >> 0) & 0xffff);
113 
114  PrivateWindows::LanguageCodePage *lpTranslate;
115 
116  // Read the list of languages and code pages.
117  VerQueryValue(pbVersionInfo.data(), TEXT("\\VarFileInfo\\Translation"),
118  reinterpret_cast<LPVOID *>(&lpTranslate), &puLenFileInfo);
119 
120  // Read the file description for each language and code page.
121  // All names: https://msdn.microsoft.com/en-us/library/windows/desktop/ms647464(v=vs.85).aspx
122  for (UINT i = 0; i < (puLenFileInfo / sizeof(struct PrivateWindows::LanguageCodePage)); i++)
123  {
124  // Retrieve file description for language and code page "i".
125  const PrivateWindows::LanguageCodePage cp = lpTranslate[i];
126  result.iso = PrivateWindows::languageToIsoCode(cp);
127  result.fileDescription =
128  PrivateWindows::queryStringFileInfo(pbVersionInfo.data(), cp, QStringLiteral("FileDescription"));
129  result.productName =
130  PrivateWindows::queryStringFileInfo(pbVersionInfo.data(), cp, QStringLiteral("ProductName"));
131  result.productVersionName =
132  PrivateWindows::queryStringFileInfo(pbVersionInfo.data(), cp, QStringLiteral("ProductVersion"));
133  result.originalFilename =
134  PrivateWindows::queryStringFileInfo(pbVersionInfo.data(), cp, QStringLiteral("OriginalFilename"));
135  result.fullFilename = dllQFile.fileName();
136 
138  break;
139  }
140 
141  // bye
142  return result;
143  }
144 
145  QList<CWinDllUtils::ProcessModule> CWinDllUtils::getModules(qint64 processId, const QString &nameFilter)
146  {
147  QList<CWinDllUtils::ProcessModule> results;
148 
149  const DWORD dwPID = static_cast<DWORD>(processId < 0 ? QCoreApplication::applicationPid() : processId);
150  HANDLE hModuleSnap = INVALID_HANDLE_VALUE;
151  MODULEENTRY32 me32;
152 
153  // Take a snapshot of all modules in the specified process.
154  hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID);
155  if (hModuleSnap == INVALID_HANDLE_VALUE)
156  {
157  // printError(TEXT("CreateToolhelp32Snapshot (of modules)"));
158  return results;
159  }
160 
161  // Set the size of the structure before using it.
162  me32.dwSize = sizeof(MODULEENTRY32);
163 
164  // Retrieve information about the first module,
165  // and exit if unsuccessful
166  if (!Module32First(hModuleSnap, &me32))
167  {
168  // printError(TEXT("Module32First")); // show cause of failure
169  CloseHandle(hModuleSnap); // clean the snapshot object
170  return results;
171  }
172 
173  // Now walk the module list of the process,
174  // and display information about each module
175  do {
176  ProcessModule pm;
177  pm.moduleName = QString::fromWCharArray(static_cast<const wchar_t *>(&me32.szModule[0]));
178  if (nameFilter.isEmpty() || pm.moduleName.contains(nameFilter, Qt::CaseInsensitive))
179  {
180  pm.executable = QString::fromWCharArray(static_cast<const wchar_t *>(&me32.szExePath[0]));
181  pm.processId = static_cast<qint64>(me32.th32ProcessID);
182  results.append(pm);
183  if (!nameFilter.isEmpty()) { break; }
184  }
185  }
186  while (Module32Next(hModuleSnap, &me32));
187 
188  CloseHandle(hModuleSnap);
189  return results;
190  }
191 
192 #else
193  // Dummy functions
195  {
196  Q_UNUSED(dllFile);
197  DLLInfo result;
198  result.errorMsg = QString("Only works on Windows");
199  return result;
200  }
201 
202  QList<CWinDllUtils::ProcessModule> CWinDllUtils::getModules(qint64 processId, const QString &nameFilter)
203  {
204  Q_UNUSED(processId);
205  Q_UNUSED(nameFilter);
206  static const QList<CWinDllUtils::ProcessModule> results;
207  return results;
208  }
209 #endif
210 
211 } // namespace swift::misc
static DLLInfo getDllInfo(const QString &dllFile)
Get DDL.
static QList< ProcessModule > getModules(qint64 processId=-1, const QString &nameFilter={})
Process modules per id.
Free functions in swift::misc.
unsigned long DWORD
Fake Windows DWORD.
QString errorMsg
error message if any
Definition: windllutils.h:27