0

を使用する必要がありますGetVolumeInformationW。理由は不明ですが、Kernel32.dll動的にロードし、実行時に関数アドレスを解決することにしました…結果は、メモリ破損の問題と、スタックの変更による奇妙な副作用です。静的バージョンは魅力のように機能し、私はそれに固執して先に進むことができますが、問題を調査したいと思います。

ソースコードは自明です(さらに情報が必要な場合はコメントしてください。完全版はここにあります):

#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;
}

サンプル出力:

// 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"

問題は、そのような行動の理由は何ですか?私の間違いはどこに隠れていますか?

g++.exe (GCC) 4.4.0、、を使用しQt 4.8.1Creator 2.5.2います。

4

1 に答える 1

4

この問題は、呼び出し規約の不一致が原因である可能性があります。GetVolumeInformationW()呼び出し規約WINAPIがあるので、関数ポインタ型を次のように変更します。

typedef BOOL (WINAPI *fnGetVolumeInformationW)(const wchar_t*,
                                               wchar_t*,
                                               uint32_t,
                                               uint32_t*,
                                               uint32_t*,
                                               uint32_t*,
                                               wchar_t*,
                                               uint32_t);

WINAPIは呼び出し規約__stdcallですが、デフォルトはです__cdecl。戻りタイプはBOOLであり、ではないことに注意してくださいbool

于 2012-11-19T22:29:38.403 に答える