想找个完美的函数(获取文件大小) ( 积分: 100 )

  • 主题发起人 主题发起人 stevenldj
  • 开始时间 开始时间
S

stevenldj

Unregistered / Unconfirmed
GUEST, unregistred user!
想找个功能强大的函数(获取文件大小),能实现以下要求的功能:
1、能获取锁定文件的大小( 比如 C:/PageFile.sys )
2、能获取超过 4GB 的文件的大小( 比如虚拟机的 虚拟磁盘 文件 *.vmdk )

说明:这样的功能,系统自身可以实现,查看文件的属性就知道了。
还有一个软件 TotalSize 也能实现。
那要怎么用 Delphi 来实现呢?

下面是我现在使用的函数,不过都不完美,谁能给个完美的函数,谢谢了!!!
(100分)


======================================================================


//获取文件夹大小
function uGetDirSize(sDirName: string): Int64;
var
sFullName, sName: string;
SearchRec: TSearchRec;
begin
Result := 0;
//格式化路径
if sDirName[Length(sDirName)] <> '/' then sDirName := sDirName + '/';
if FindFirst(sDirName + '*.*', faAnyFile, SearchRec) = 0 then begin
repeat
sName := SearchRec.Name;
if sName[1] = '.' then Continue; //过滤无用信息
sFullName := sDirName + sName;
if (SearchRec.Attr and faDirectory) = faDirectory then
Result := Result + uGetDirSize(sFullName) //递归调用
else begin
Result := Result + SearchRec.Size;
//Result := Result + uGetFileSize(sFullName); //想找个能操作大于4G文件的函数
end;
until FindNext(SearchRec) <> 0
end;
FindClose(SearchRec);
end;

//获取文件大小(文件大小不能超过4G),超过4G将得到错误结果,错误结果不一定为负数。
function uGetFileSize(sFileName: string): Int64;
var
SearchRec: TSearchRec;
begin
Result := 0;
if FindFirst(sFileName, faAnyFile, SearchRec) = 0 then begin
if (SearchRec.Attr and faDirectory) = faDirectory then
Exit
else
Result := SearchRec.Size;
end;
FindClose(SearchRec);
end;

//获取文件大小(文件大小可以超过4G),但不能操作锁定的文件(比如 PageFile.sys ),否则返回0大小
function uGetFileSize1(sFileName: string): Int64;
var
hFile: THandle;
begin
Result := 0;
hFile := FileOpen(sFileName, fmOpenRead);
if hFile <> INVALID_HANDLE_VALUE then
try
Result := FileSeek(hFile, 0, 2);
finally
FileClose(hFile);
end;
end;

//获取文件大小(文件大小可以超过4G),但不能操作锁定的文件(比如 PageFile.sys ),否则会抛出读取异常
function uGetFileSize2(sFileName: string): Int64;
var
F: TFilestream;
begin
Result := 0;
F := TFilestream.Create(sFileName, fmOpenRead);
Result := F.Size;
F.Free;
end;
 
直接用 API GetFileSize
 
GetFileSize 没错
 
三种办法:
1、getfilezie :用在这里不太好,因为这个函数需要先打开文件
2、getfielattributesEX :注意里面有一个类似 dwHighFileSize, dwLowFileSize 的成员
3、在楼主的例子里面,不应该用 SearchRec.Size, 而应该用 SearchRec 里面的一个
Twin32FindData 的成员, 这个成员里面有 dwHighFileSize, dwLowFileSize

最好的答案是:3

楼主给分!!!!!
 
文件尺寸,肯定是问操作系统要, GetFileSize 楼上都是最好的。
 
用 TFileStream 其实可以直接取,不能打开锁定文件是因为没加 share 参数
 
如果要打开文件的话开销就大了,windows统计文件夹总大小时,可能也没有逐个打开文件。
 
不打开的话,就只能去读文件分配表,更麻烦.
 
文件大小是目录表的数字决定的,请系统从目录表取数是最快的,打开文件是最笨的办法。
 
GetFileSize和getfileattributesEX 都要打开文件.一个是程序员手工打开,一个是函数里面打开.都不能取到独占的文件.帖一段Windows的源代码
BOOL STDCALL
GetFileAttributesExW(LPCWSTR lpFileName,
GET_FILEEX_INFO_LEVELS fInfoLevelId,
LPVOID lpFileInformation)
{
FILE_NETWORK_OPEN_INFORMATION FileInformation;
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING FileName;
NTSTATUS Status;
WIN32_FILE_ATTRIBUTE_DATA* FileAttributeData;

DPRINT("GetFileAttributesExW(%S) called/n", lpFileName);


if (fInfoLevelId != GetFileExInfoStandard || lpFileInformation == NULL)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}

/* Validate and translate the filename */
if (!RtlDosPathNameToNtPathName_U ((LPWSTR)lpFileName,
&FileName,
NULL,
NULL))
{
DPRINT1 ("Invalid path/n");
SetLastError (ERROR_BAD_PATHNAME);
return FALSE;
}

/* build the object attributes */
InitializeObjectAttributes (&ObjectAttributes,
&FileName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);

/* Get file attributes */
Status = NtQueryFullAttributesFile(&ObjectAttributes,
&FileInformation);

RtlFreeUnicodeString (&FileName);
if (!NT_SUCCESS (Status))
{
DPRINT ("NtQueryFullAttributesFile() failed (Status %lx)/n", Status);
SetLastErrorByStatus (Status);
return FALSE;
}

FileAttributeData = (WIN32_FILE_ATTRIBUTE_DATA*)lpFileInformation;
FileAttributeData->dwFileAttributes = FileInformation.FileAttributes;
FileAttributeData->ftCreationTime.dwLowDateTime = FileInformation.CreationTime.u.LowPart;
FileAttributeData->ftCreationTime.dwHighDateTime = FileInformation.CreationTime.u.HighPart;
FileAttributeData->ftLastAccessTime.dwLowDateTime = FileInformation.LastAccessTime.u.LowPart;
FileAttributeData->ftLastAccessTime.dwHighDateTime = FileInformation.LastAccessTime.u.HighPart;
FileAttributeData->ftLastWriteTime.dwLowDateTime = FileInformation.LastWriteTime.u.LowPart;
FileAttributeData->ftLastWriteTime.dwHighDateTime = FileInformation.LastWriteTime.u.HighPart;
FileAttributeData->nFileSizeLow = FileInformation.EndOfFile.u.LowPart;
FileAttributeData->nFileSizeHigh = FileInformation.EndOfFile.u.HighPart;

return TRUE;
}

BOOL STDCALL
GetFileAttributesExA(LPCSTR lpFileName,
GET_FILEEX_INFO_LEVELS fInfoLevelId,
LPVOID lpFileInformation)
{
PWCHAR FileNameW;

if (!(FileNameW = FilenameA2W(lpFileName, FALSE)))
return FALSE;

return GetFileAttributesExW(FileNameW, fInfoLevelId, lpFileInformation);
}

GetFileAttributesEx里面使用了NtQueryFullAttributesFile

NTSTATUS
STDCALL
NtQueryFullAttributesFile(IN POBJECT_ATTRIBUTES ObjectAttributes,
OUT PFILE_NETWORK_OPEN_INFORMATION FileInformation)
{
return IopQueryAttributesFile(ObjectAttributes,
FileNetworkOpenInformation,
FileInformation);
}

NtQueryFullAttributesFile则调用了IopQueryAttributesFile

NTSTATUS
IopQueryAttributesFile(IN POBJECT_ATTRIBUTES ObjectAttributes,
IN FILE_INFORMATION_CLASS FileInformationClass,
OUT PVOID FileInformation)
{
IO_STATUS_BLOCK IoStatusBlock;
HANDLE FileHandle;
NTSTATUS Status;
KPROCESSOR_MODE AccessMode;
UNICODE_STRING ObjectName;
OBJECT_CREATE_INFORMATION ObjectCreateInfo;
OBJECT_ATTRIBUTES LocalObjectAttributes;
ULONG BufferSize;
union
{
FILE_BASIC_INFORMATION BasicInformation;
FILE_NETWORK_OPEN_INFORMATION NetworkOpenInformation;
}LocalFileInformation;

if (FileInformationClass == FileBasicInformation)
{
BufferSize = sizeof(FILE_BASIC_INFORMATION);
}
else if (FileInformationClass == FileNetworkOpenInformation)
{
BufferSize = sizeof(FILE_NETWORK_OPEN_INFORMATION);
}
else
{
return STATUS_INVALID_PARAMETER;
}

AccessMode = ExGetPreviousMode();

if (AccessMode != KernelMode)
{
Status = STATUS_SUCCESS;
_SEH_TRY
{
ProbeForWrite(FileInformation,
BufferSize,
sizeof(ULONG));
}
_SEH_HANDLE
{
Status = _SEH_GetExceptionCode();
}
_SEH_END;
if (NT_SUCCESS(Status))
{
Status = ObpCaptureObjectAttributes(ObjectAttributes,
AccessMode,
NULL,
&ObjectCreateInfo,
&ObjectName);
}
if (!NT_SUCCESS(Status))
{
return Status;
}
InitializeObjectAttributes(&LocalObjectAttributes,
&ObjectName,
ObjectCreateInfo.Attributes,
ObjectCreateInfo.RootDirectory,
ObjectCreateInfo.SecurityDescriptor);
}

/* Open the file */
Status = ZwOpenFile(&FileHandle,
SYNCHRONIZE | FILE_READ_ATTRIBUTES,
AccessMode == KernelMode ? ObjectAttributes : &LocalObjectAttributes,
&IoStatusBlock,
0,
FILE_SYNCHRONOUS_IO_NONALERT);
if (AccessMode != KernelMode)
{
ObpReleaseCapturedAttributes(&ObjectCreateInfo);
ExFreePool(ObjectName.Buffer);
}
if (!NT_SUCCESS (Status))
{
DPRINT ("ZwOpenFile() failed (Status %lx)/n", Status);
return Status;
}

/* Get file attributes */
Status = ZwQueryInformationFile(FileHandle,
&IoStatusBlock,
AccessMode == KernelMode ? FileInformation : &LocalFileInformation,
BufferSize,
FileInformationClass);
if (!NT_SUCCESS (Status))
{
DPRINT ("ZwQueryInformationFile() failed (Status %lx)/n", Status);
}
ZwClose(FileHandle);

if (NT_SUCCESS(Status) && AccessMode != KernelMode)
{
_SEH_TRY
{
memcpy(FileInformation, &LocalFileInformation, BufferSize);
}
_SEH_HANDLE
{
Status = _SEH_GetExceptionCode();
}
_SEH_END;
}
return Status;
}

注意ZwOpenFile,IopQueryAttributesFile也是做了打开文件的操作才获得的文件属性.
 
function fnSize(fn: string): Longint;
var SearchRec: TSearchRec;
begin
if FindFirst(fn,faAnyFile,SearchRec) = 0 then
Result:=SearchRec.Size
else
Result:=-1;
SysUtils.FindClose(SearchRec);
end;

查找过程中获取文件大小
没试能否满足楼主的要求,觉得可能行.
 
function GetFileInt64Size(const FileName: string): Int64;
var SearchRec: TSearchRec;
begin
if FindFirst(FileName,faAnyFile,SearchRec) = 0 then
begin
Int64Rec(Result).Lo := SearchRec.FindData.nFileSizeLow;
Int64Rec(Result).Hi := SearchRec.FindData.nFileSizeHigh;
end
else
Result:=-1;
SysUtils.FindClose(SearchRec);
end;
 
结帖

lxggc的回答是正确的,zqw0117的函数是完美的。测试通过。

请大家回答问题的时候自己先测试一下自己的方法。或者你肯定自己的方法是正确的。


======================================


GetFileSize 和 TFilestream 都不能打开锁定的文件。即使用了 Share 也不行。下面是程序代码和出错提示:

function uGetFileSize(sFileName: string): Int64;
var
F: TFilestream;
begin
Result := 0;
F := TFilestream.Create(sFileName, fmOpenRead or fmShareDenyNone);
Result := F.Size;
F.Free;
end;


---------------------------
Debugger Exception Notification
---------------------------
Project FindLargest.exe raised exception class EFOpenError with message 'Cannot open file "C:/Documents and Settings/NetworkService/NTUSER.DAT". 另一个程序正在使用此文件,进程无法访问。'. Process stopped. Use Step or Run to continue.
---------------------------
OK Help
---------------------------

我测试的时候,只要使用了 OpenFile 或 AssignFile 或 TStream 来打开文件的时候,都无法打开锁定的文件。
=======================================


C代码我看不懂,不过谢谢大家参与讨论。

如果大家还有更好的方法,欢迎继续讨论。
 
function GetFileSize(FileName: string):Longint;
var
sr: TSearchRec;
begin
if FindFirst(FileName, faAnyFile, sr) = 0 then
result:=sr.size;
end;
这个怎么样?给分吧[:D]
 
Dos下是怎么得到文件大小的,应该从这里突破
 
dos不支持大于 4g 的文件.
 
楼主结帖怎么不给分?
 
顶顶
我的妈的
获取大小就这么麻烦了
 
呵呵,我记得分都输进去了,怎么就没给出去呢,呵呵,现在给。
 
后退
顶部