问题描述
我有一个可以扫描文件并收集有关文件的元数据的应用程序。一种功能是获取文件的文件大小。为此,我正在使用winapi
函数GetFileSizeEx(Handle,PLARGE_INTEGER)。参数需要文件HANDLE
和对PLARGE_INTEGER
的引用,也称为(*LARGE_INTEGER
)。
要在Qt中获取文件HANDLE
,建议here使用QFile::handle()获取文件句柄并将结果传递到_get_osfhandle(int)并将其转换为HANDLE
HANDLE handle = (HANDLE) _get_osfhandle(file.handle()).
使用此句柄,将其与PLARGE_INTEGER
变量一起传递到[GetFileSizeEx(Handle,PLARGE_INTEGER)],如果操作失败,则返回0;如果操作成功,则返回!= 0
。
GetFileSize(HANDLE,PLARGE_INTEGER)
返回:
-
0(或失败),您可以根据GetLastError的列表使用error codes(作为开始,还有更多)来提供帮助。
-
任何非零值表示成功
问题:
使用这种方法,我尝试对有效文件进行相同的操作,但是在调用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;
这意味着您将空指针传递给GetFileSizeEx
。
您需要创建一个实际的LARGE_INTEGER
对象,并使用地址运算符&
将指针传递给该对象:
LARGE_INTEGER l;
GetFileSizeEx(handle,&l); // Pass pointer to the variable l
,
问题是您对PLARGE_INTEGER
的处理不正确。您正在创建实际上没有指向任何地方的PLARGE_INTEGER
(又名LARGE_INTEGER*
)指针,这就是GetFileSizeEx()
在尝试写出文件大小时崩溃的原因。
您需要分配一个LARGE_INTEGER
(而不是PLARGE_INTEGER
),然后将其内存地址传递给GetFileSizeEx()
,例如:
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();
}