Inno Setup 获取在另一个应用程序中打开的超过 2GB 限制的文件大小

Inno Setup Get size of a file over 2GB limit opened in another application

我正在尝试 return 使用来自 vincenzo.net.

ISXKB 的 Public 域代码的文件大小
function CloseHandle (hHandle: THandle): Boolean;
  external 'CloseHandle@kernel32.dll stdcall';

const
    { Some constants for CreateFile (). }
    GENERIC_READ           = 000000;
    GENERIC_WRITE          = 000000;
    GENERIC_EXECUTE        = 000000;
    GENERIC_ALL            = 000000;
    FILE_SHARE_READ        = 1;
    FILE_SHARE_WRITE       = 2;
    FILE_SHARE_DELETE      = 4;
    CREATE_NEW             = 1;
    CREATE_ALWAYS          = 2;
    OPEN_EXISTING          = 3;
    OPEN_ALWAYS            = 4;
    TRUNCATE_EXISTING      = 5;
    FILE_READ_ATTRIBUTES   = ;
    FILE_WRITE_ATTRIBUTES  = 0;

    { General Win32. }
    INVALID_HANDLE_VALUE   = -1;

function CreateFile (
    lpFileName             : String;
    dwDesiredAccess        : Cardinal;
    dwShareMode            : Cardinal;
    lpSecurityAttributes   : Cardinal;
    dwCreationDisposition  : Cardinal;
    dwFlagsAndAttributes   : Cardinal;
    hTemplateFile          : Integer
): Integer;
 external 'CreateFileA@kernel32.dll stdcall';

function GetFileSize (hFile: THandle; var lpFileSizeHigh: Integer): Integer;
  external 'GetFileSize@kernel32.dll stdcall';

function GetTheFileSize (FileName: String): Integer;
var
    hFile:  THandle;
    iSize:  Integer;
    hSize:  Integer;
begin
    hFile := CreateFile (FileName,
        GENERIC_READ,// Desired access.
        FILE_SHARE_READ + FILE_SHARE_WRITE,
        0,                { Security attributes. }
        OPEN_EXISTING,
        FILE_ATTRIBUTE_TEMPORARY,
        0);
    if (INVALID_HANDLE_VALUE = hFile) then
    begin
        Result := 0;
        Exit;
    end;
    iSize := GetFileSize (hFile, hSize);
    CloseHandle (hFile);
    Result := iSize;
end;

但是,这似乎没有按预期工作并且正在 returning 0,我认为这是因为它在 if (INVALID_HANDLE_VALUE = hFile) then Result := 0 退出。我传递给它的文件存在并且可以访问。任何人都可以阐明为什么失败或建议替代方法吗?请注意,我不能使用内置的 FileSize 函数,因为它有 2GB 的限制,这对我的目的来说是不够的。

我假设您使用的是 Unicode 版本的 Inno Setup。

因此您必须使用 CreateFileCreateFileW 的 Unicode 版本,而不是 CreateFileA:

external 'CreateFileW@kernel32.dll stdcall';

无论如何,GettheFileSize 实现(从现在开始不存在的 ISXKB)也有 2 GB 的限制:

This declaration works with files up to 2 GB.
...
... retrieves its low 32 bit part of the file size as an integer, and closes the file again.


要支持 64 位大小,请将其更改为:

function GetTheFileSize (FileName: String): Int64;
...
begin
  ...
  Result := Int64(Cardinal(iSize)) + (Int64(Cardinal(hSize)) shl 32);
end;

总之,有点过分了。正如您所发现的那样,如果另一个应用程序打开文件而不允许其他应用程序至少读取该文件(它在调用 CreateFile 时未指定 FILE_SHARE_READ),则它不起作用。

请注意,FileSize 在这种情况下也不起作用,因为它与 ISXKB 的 GetTheFileSize.

具有基本相同的实现

使用 FindFirst support function 有一个简单的解决方案:

function GetTheFileSize(FileName: String): Int64;
var
  FindRec: TFindRec;
begin
  if FindFirst(FileName, FindRec) then
  begin
    Result := Int64(FindRec.SizeHigh) shl 32 + FindRec.SizeLow;
    FindClose(FindRec);
  end
    else
  begin
    Result := -1;
  end;
end;