使用 GetFileSize 获取文件大小报告有效文件上的 segv

Get file size using GetFileSize reports a segv on a valid file

我有一个应用程序可以扫描文件并收集有关文件的元数据。一个功能是获取文件的文件大小。为此,我使用 winapi 函数 GetFileSizeEx(Handle, PLARGE_INTEGER)。这些参数需要一个文件 HANDLE 和对 PLARGE_INTEGER aka (*LARGE_INTEGER).

的引用

要在 Qt 中获取文件 HANDLE,建议 here to get the file handle using QFile::handle() and pass the result into _get_osfhandle(int) 并将其转换为 HANDLE。

HANDLE handle = (HANDLE) _get_osfhandle(file.handle()).

使用此句柄,将其与 PLARGE_INTEGER 变量一起传递到 [GetFileSizeEx(Handle, PLARGE_INTEGER)] 其中 returns 0 如果操作失败或 != 0如果操作成功。

GetFileSize(HANDLE, PLARGE_INTEGER) returns 与:

问题:

使用这种方法,我在有效文件上尝试了相同的方法,但是在调用 GetFileSizeEx(Handle, PLARGE_INTEGER) 时我得到了一个 SEGV。我在调用 QFile::open(QIODevice::ReadOnly) 之前和之后都尝试过此操作作为测试,但它仍然失败了。

 int localHandle = file.handle(); // handle returns -1 as expected
 bool localOpen = file.open(QIODevice::ReadOnly);
 if (localOpen) {
      int localFileHandle = file.handle(); // returns a valid >0 handle value
      HANDLE handle = (HANDLE) _get_osfhandle(localFileHandle); // returns a valid > 0 handle value
      PLARGE_INTEGER l = PLARGE_INTEGER(); // representation value of 0
      BOOL b = GetFileSizeEx(handle, l); // segv
      if (!b) {
           qDebug() << getLastErrorMsg();
           return QFileInfo(filepath).size();
      }

      return l->QuadPart;
 }

我是不是做错了什么?


见附件截图

MVCE

#include <QCoreApplication>
#include "windows.h"
#include <comdef.h>
#include <QFile>
#include <QDebug>
#include <QString>
#include <QFileInfo>

#include <windows.h>
#include <fileapi.h>
#include <io.h>

static QString toString(HRESULT hr)
{
     _com_error err{hr};
     const TCHAR* lastError = err.ErrorMessage();
     return QStringLiteral("Error 0x%1: %2").arg((quint32)hr, 8, 16, QLatin1Char('0'))
            .arg(lastError);
}

static QString getLastErrorMsg()
{
     DWORD lastError = GetLastError();
     QString s = toString(HRESULT_FROM_WIN32(lastError));
     return s;
}

static qint64 getWinAPIFileSize(QString filepath)
{
     QFile file(filepath);
     if (!file.exists()) {
          return 0;
     }

     int localHandle = file.handle(); // handle returns -1 as expected
     bool localOpen = file.open(QIODevice::ReadOnly);
     if (localOpen) {
          int localFileHandle = file.handle(); // returns a valid >0 handle value
          HANDLE handle = (HANDLE) _get_osfhandle(localFileHandle); // returns a valid > 0 handle value
          PLARGE_INTEGER l = PLARGE_INTEGER(); // representation value of 0
          BOOL b = GetFileSizeEx(handle, l); // segv
          if (!b) {
               qDebug() << getLastErrorMsg();
               return QFileInfo(filepath).size();
          }

          return l->QuadPart;
     }

     return QFileInfo(filepath).size();
}

int main(int argc, char* argv[])
{
     QCoreApplication a(argc, argv);

     QString src = QString("C:/Users/CybeX/.bash_history"); // change path to a valid file on your PC

     qint64 size = getWinAPIFileSize(src);
     qDebug() << size;

     return a.exec();
}

类型 PLARGE_INTEGER 是“指向 LARGE_INTEGER 的指针”的别名。而 PLARGE_INTEGER() 是类型“指向 LARGE_INTEGER 的指针”的默认值初始化。对于所有指针,这样的初始化都是空指针。

即定义

PLARGE_INTEGER l = PLARGE_INTEGER();

等同于

LARGE_INTEGER* l = nullptr;

这意味着您将 null-pointer 传递给 GetFileSizeEx

您需要创建一个实际的 LARGE_INTEGER 对象,并使用 address-of 运算符将指针传递给该对象 &:

LARGE_INTEGER l;
GetFileSizeEx(handle, &l);  // Pass pointer to the variable l

问题是您对 PLARGE_INTEGER 的处理不当。您正在创建一个实际上并不指向任何地方的 PLARGE_INTEGER(又名 LARGE_INTEGER*)指针,这就是 GetFileSizeEx() 在尝试写出文件大小时崩溃的原因。

你需要分配一个LARGE_INTEGER(不是PLARGE_INTEGER),然后把它的内存地址传给GetFileSizeEx(),eg:

static qint64 getWinAPIFileSize(QString filepath)
{
    QFile file(filepath);

    if (file.open(QIODevice::ReadOnly)) {
        int localFileHandle = file.handle();
        HANDLE handle = (HANDLE) _get_osfhandle(localFileHandle);
        LARGE_INTEGER l;
        if (GetFileSizeEx(handle, &l)) {
            return l.QuadPart;
        }
        qDebug() << getLastErrorMsg();
    }

    return QFileInfo(file).size();
}

也就是说,在这种情况下手动使用 GetFileSizeEx() 真的毫无意义。根据它的实现(我无法检查),QFileInfo.size() 会为你打开文件,或者它会直接从文件系统的元数据中直接查询文件的大小而不实际打开文件:

static qint64 getWinAPIFileSize(QString filepath)
{
    return QFileInfo(filepath).size();
}