I need to use GetVolumeInformationW. For reasons uknown, I decided to load Kernel32.dll dynamically, resolve function address at runtime… Result is memory corruption problem and some strange side-effects of modifying stack. Though static version works like a charm and I can just stick with it and move on, I want to investigate the matter.
Source code is self-explaining (comment if you need some more information, full version can be found here):
#include <QtDebug>
#include <QByteArray>
#include <QLibrary>
#include <QDir>
static inline QString LAT1(const char *str, const int len = -1) {
return QString::fromLatin1(str, len);
}
template <typename T>
static inline QByteArray createByteArray(const T *from, const int numElements) {
return QByteArray(reinterpret_cast<const char*>(from), sizeof(T) * numElements);
}
// This one resolves functions from Kernel32.dll dynamically and uses standard types.
// (Dynamic linking.)
QByteArray fingerprintDynamic() {
const uint32_t kMaxPath = 260 + 1; // MAX_PATH + 1
wchar_t path[kMaxPath] = {0};
wchar_t name[kMaxPath] = {0};
wchar_t fileSystem[kMaxPath] = {0};
uint32_t serial = 0;
uint32_t maximumComponentLength = 0;
uint32_t fileSystemFlags = 0;
QLibrary kernel32("kernel32");
typedef uint32_t (*fnGetLastError)(void);
typedef bool (*fnGetVolumeInformationW)(const wchar_t*, wchar_t*, uint32_t, uint32_t*, uint32_t*,
uint32_t*, wchar_t*, uint32_t);
fnGetVolumeInformationW GetVolumeInformationW = reinterpret_cast<fnGetVolumeInformationW>(kernel32.resolve("GetVolumeInformationW"));
fnGetLastError GetLastError = reinterpret_cast<fnGetLastError>(kernel32.resolve("GetLastError"));
if (!GetVolumeInformationW) {
qWarning(LAT1("GetVolumeInformationW() not resolved: %1").arg(kernel32.errorString()).toLatin1().constData());
return QByteArray();
}
else if (!GetLastError) {
qWarning(LAT1("GetLastError() not resolved: %1").arg(kernel32.errorString()).toLatin1().constData());
return QByteArray();
}
QDir::toNativeSeparators(QDir::rootPath()).toWCharArray(path);
bool apiCall = GetVolumeInformationW(path, name, kMaxPath, &serial, &maximumComponentLength,
&fileSystemFlags, fileSystem, kMaxPath);
if (!apiCall)
qWarning(LAT1("GetVolumeInformationW() failed: %1").arg(GetLastError()).toLatin1().constData());
// At this point, fileSystem is correct and contains
// L"NTFS"
// ONLY HAPPENS IN DEBUG MODE
//
// After this call memory becomes corrupted. wcslen() is not a problem.
// And createByteArray<>() is ok too, I believe.
//size_t len; // But if I change stack a bit (like uncomment this line),
// result will be correct, so I guess it's related to memory offset.
return createByteArray<wchar_t>(fileSystem, wcslen(fileSystem));
}
void print(const QByteArray &bytes) {
qDebug() << QString::fromWCharArray(reinterpret_cast<const wchar_t*>(bytes.constData()));
qDebug() << bytes.size() << "bytes" << bytes.toHex();
qDebug() << "";
}
int main(int, char**)
{
qDebug() << "dynamic";
print(fingerprintDynamic());
return 0;
}
Sample output:
// this is DEBUG build
dynamic
"(?("
8 bytes "280052f828000400"
// this is RELEASE build
// (same with `size_t len` uncommenented before `return` in DEBUG)
dynamic
"NTFS"
8 bytes "4e00540046005300"
The question is: what is the reason of such behaviour? where’s my mistake is hiding?
I’m using g++.exe (GCC) 4.4.0, Qt 4.8.1 and Creator 2.5.2.
The problem is probably caused by a mismatch in the calling convention.
GetVolumeInformationW()has calling conventionWINAPIso change the function pointer type to:WINAPIis calling convention__stdcall, whereas the default is__cdecl.Note the return type is
BOOL, and notbool.