【问题标题】:CreateFileMapping fails with hFile other than INVALID_HANDLE_VALUECreateFileMapping 失败,hFile 不是 INVALID_HANDLE_VALUE
【发布时间】:2013-04-15 13:56:26
【问题描述】:

更新 - 已解决并回答,违规行已被注释掉

简要说明

即使我将(GENERIC_WRITE 或 GENERIC_WRITE)用于 CreateFile,将 PAGE_READWRITE 用于 CreateFileMapping,但我在将 CreateFile 与 CreateFileMapping 链接时遇到问题

详细说明

我正在使用 CreateFileMapping 在进程之间共享内存。 假设我没有映射到物理文件,而是使用 INVALID_HANDLE_VALUE 作为 CreateFileMapping 的第一个参数,那么实际的机制工作正常。这很好,但是我想要实现的是创建此映射的主进程使用基于磁盘的文件,并定期将其刷新到驱动器,并在它关闭时自动保存数据。

代码如下... (当我运行这个时,我得到“错误 5:访问被拒绝”作为 ShowMessage)

const MaximumMapSize = 256 * 1024;
var virtualMemoryPath : string = '';

function getFileHandle(mapname : string; maxSize  : dword) :THandle;
var diskfilename : string;

  lpFileName: PChar;
  dwDesiredAccess: DWORD;
  dwShareMode: DWORD;
  lpSecurityAttributes: PSecurityAttributes;
  dwCreationDisposition : dword;
  dwFlagsAndAttributes: DWORD;
  hTemplateFile : THandle ;

  temp : pointer;
begin
    Result := INVALID_HANDLE_VALUE;
    if (maxSize <= MaximumMapSize) and (Length(virtualMemoryPath) > 0) then
    begin

            diskfilename := virtualMemoryPath+mapname+'.bin';

            if FileExists(diskfilename) then
               Sysutils.DeleteFile(diskfilename);

            lpFileName              := PChar(diskfilename);
            //dwDesiredAccess         := GENERIC_WRITE or GENERIC_WRITE;//<<<wrong
            dwDesiredAccess         := GENERIC_READ or GENERIC_WRITE;
            dwShareMode             := 0;//FILE_SHARE_READ or FILE_SHARE_WRITE;//<<wrong
            lpSecurityAttributes    := nil;
            dwCreationDisposition   := CREATE_ALWAYS;
            dwFlagsAndAttributes    := 0;
            hTemplateFile           := 0 ;


            Result := CreateFile(
                lpFileName,
                dwDesiredAccess,
                dwShareMode,
                lpSecurityAttributes,
                dwCreationDisposition,
                dwFlagsAndAttributes,
                hTemplateFile);

           if (Result <> INVALID_HANDLE_VALUE) then
           begin
             GetMem(temp,maxsize);
             ZeroMemory(temp,maxsize);
             FileWrite(result,temp^,maxSize);
             FreeMem (temp,maxsize);
             FlushFileBuffers(result);
             SetFilePointer(result,0,0,FILE_BEGIN);
           end;
    end;
end;

function createMap (mapname : string ; maxSize: dword; var hFile: THandle) : THandle;
var
    lpFileMappingAttributes: PSecurityAttributes;
    flProtect           : DWORD;
    dwMaximumSizeHigh   : DWORD;
    dwMaximumSizeLow    : DWORD;
    lpName              : PChar;

    LastError : dword;
begin

    Result := INVALID_HANDLE_VALUE;

    if (maxSize > MaximumMapSize) then
        exit;

     // create a disk the file or return INVALID_HANDLE_VALUE if settings have not defined a path to folder
     hFile     :=  getFileHandle(mapname,maxSize);

     lpFileMappingAttributes := nil;
     flProtect          := PAGE_READWRITE;
     dwMaximumSizeHigh  := 0;
     dwMaximumSizeLow   := maxSize;
     lpName := PChar(mapname);

     Result := CreateFileMapping(
        hfile,
        lpFileMappingAttributes,
        flProtect,
        dwMaximumSizeHigh,
        dwMaximumSizeLow,
        lpName);

     if (Result = 0) then
     begin

         if (not (hFile = INVALID_HANDLE_VALUE) ) then
            CloseHandle(hFile);

         hFile := 0;
         LastError := GetLastError();
         ShowMessage( Format('Error %d : %s',[LastError,SysErrorMessage(LastError)]) );

     end
     else
     begin

        ShowMessage(Format('Returing handle %d',[result]));
        if (hFile = INVALID_HANDLE_VALUE) then
            hFile := 0;

     end;

end;

【问题讨论】:

  • 那么,当你运行这段代码时会发生什么?能否请您格式化代码?
  • 抱歉,我更新了问题。它告诉我访问被拒绝(GetLastError()= 5)。我之前提到过,但是因为它变得很冗长,所以我删掉了这个问题,所以我删除了一段。
  • 它确实创建了正确大小的文件。

标签: delphi winapi delphi-7


【解决方案1】:
        dwDesiredAccess         := GENERIC_WRITE or GENERIC_WRITE;
        dwShareMode             := FILE_SHARE_READ or FILE_SHARE_WRITE;

这行不通。您将需要读取和写入权限。此外,创建视图需要操作系统确保它是唯一可以写入文件的视图。除了通过视图之外,没有其他机制可以检测到另一个进程写入文件,并确保这样的写入在内存中及时和同步地可见。尤其是多线程访问视图时无法实现同步更新。

读取共享同样不明智,操作系统不保证它何时从视图中更新文件。唯一的保证是它会在所有视图关闭时更新。这也意味着需要有序地关闭 Windows,您无法使其可靠地应对断电等事故。同样,读取需要通过视图完成,而不是文件。唯一合适的选择是不分享。

【讨论】:

  • 除了那是我尝试的第一件事。结果没有区别。 (使用 0 作为 dwShareMode 标志)。
  • 这显然不是正确的共享模式,但系统仍然允许您这样做。换句话说,这不是ERROR_ACCESS_DENIED的原因。
  • 叹息,很难回答不止一件错误的问题。我猜,将其视为难题的一部分。
  • 感谢您的建议。我支持你,但选择了大卫的答案,因为它对解决方案很封闭。我必须承认我最初没有分享,因为这是最有意义的。我一直在尝试几种不同的组合,但没有注意到我使用位掩码值 GENERIC_WRITE 代替了 GENERIC_READ。剪切和粘贴的危险!
  • 这是 SO 的一个常见问题,程序员倾向于在尝试解决原始问题后将其代码破解。好吧,至少我解释了为什么你最初的刷新视图的愿望永远不会去任何地方。从头开始。
【解决方案2】:

错误就在这里:

GENERIC_WRITE or GENERIC_WRITE

你的意思是:

GENERIC_READ or GENERIC_WRITE

修复该问题,您的文件映射就可以创建了。

我同意 Hans 的观点,您不应该共享文件句柄。

【讨论】:

  • 很好地出现在 GENERIC_WRITE 或 GENERIC_WRITE。我不同意你关于参数的顺序。 winapi 功能不允许您仅仅因为您喜欢就交换订单!它们以正确的顺序列出。
  • "winapi 功能不会让您仅仅因为您喜欢就交换订单!"好吧。但这不是因为我喜欢它。我只是读错了。就这样!傻我!
猜你喜欢
  • 2012-01-08
  • 2018-02-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-07
  • 2011-12-01
  • 1970-01-01
  • 2014-12-05
相关资源
最近更新 更多