You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

复制持续写入的日志文件时避免文件锁定的技术咨询

问题解答

关于CopyFile()的文件锁定问题

  • 默认调用CopyFile()时,系统会以独占读模式打开源文件(即不允许其他进程写入),这就是你遇到日志停止更新的核心原因:复制过程中源文件被锁定,写入进程无法获取写入权限,直到复制完成或锁定被意外释放。

为什么CopyFileExW加COPY_FILE_OPEN_SOURCE_FOR_WRITE仍出错?

你使用的COPY_FILE_OPEN_SOURCE_FOR_WRITE参数确实会让系统以允许写入共享的方式打开源文件,但测试中出现I/O 103错误,问题出在你的写入测试代码上:

  • Delphi的Append方法默认会请求独占写入权限,而此时源文件被复制进程以“允许读、允许写”的共享模式打开,两者权限冲突就会触发103错误(无法获取独占锁)。这不是CopyFileExW的问题,是测试代码的打开模式和复制进程的共享模式不匹配。

完全无锁定的文件复制实现方案

要实现真正的无锁定复制,核心是确保源文件打开时允许所有共享操作(读、写、删除),以下两种方案可解决问题:

方案1:修正CopyFileExW搭配写入逻辑

保持复制端的参数不变,调整写入进程的文件打开方式,避免请求独占锁:

  • 复制端继续使用CopyFileExW(PChar(source), PChar(dest), nil, nil, nil, COPY_FILE_OPEN_SOURCE_FOR_WRITE);
  • 写入端替换AssignFile为API级操作,用CreateFileW指定全共享模式:
    procedure TForm1.Button1Click(Sender: TObject);
    var
      hFile: THandle;
      buf: string;
      bytesWritten: DWORD;
    begin
      inc(i);
      buf := '['+FormatDateTime('yyyy-MM-dd hh:nn:ss',now)+'] '+'WRITE TEST'+' '+i.ToString + #13#10;
    
      hFile := CreateFileW(PWideChar('\\xxxx\xx\xx\xx\xx\xx\HJobs4.trc'),
                          GENERIC_WRITE,
                          FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE,
                          nil,
                          OPEN_ALWAYS,
                          FILE_ATTRIBUTE_NORMAL,
                          0);
      try
        if hFile = INVALID_HANDLE_VALUE then
        begin
          Memo1.Lines.Add(DateTimeToStr(now)+' 打开失败,错误码:'+IntToStr(GetLastError));
          Exit;
        end;
        // 移动文件指针到末尾
        SetFilePointer(hFile, 0, nil, FILE_END);
        WriteFile(hFile, PChar(buf)^, Length(buf), bytesWritten, nil);
      finally
        if hFile <> INVALID_HANDLE_VALUE then CloseHandle(hFile);
      end;
    end;
    

方案2:手动实现无锁定复制(更灵活可控)

完全绕过CopyFile系列函数,手动分块复制,精准控制源文件共享权限:

function CopyFileUnlocked(const src, dest: string): Boolean;
var
  hSrc, hDest: THandle;
  buf: array[0..4095] of Byte;
  bytesRead, bytesWritten: DWORD;
begin
  Result := False;
  // 打开源文件:允许所有共享操作
  hSrc := CreateFileW(PWideChar(src),
                      GENERIC_READ,
                      FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE,
                      nil,
                      OPEN_EXISTING,
                      FILE_ATTRIBUTE_NORMAL or FILE_FLAG_SEQUENTIAL_SCAN,
                      0);
  if hSrc = INVALID_HANDLE_VALUE then Exit;

  try
    // 创建目标文件
    hDest := CreateFileW(PWideChar(dest),
                         GENERIC_WRITE,
                         0,
                         nil,
                         CREATE_ALWAYS,
                         FILE_ATTRIBUTE_NORMAL,
                         0);
    if hDest = INVALID_HANDLE_VALUE then Exit;

    try
      // 分块复制
      while ReadFile(hSrc, buf, SizeOf(buf), bytesRead, nil) and (bytesRead > 0) do
      begin
        if not WriteFile(hDest, buf, bytesRead, bytesWritten, nil) then Break;
      end;
      Result := (bytesRead = 0);
    finally
      CloseHandle(hDest);
    end;
  finally
    CloseHandle(hSrc);
  end;
end;

该方法完全由你控制源文件的共享权限,确保复制期间其他进程可正常读写源文件。

额外注意事项

  • 复制持续写入的日志文件时,副本无法包含复制过程中新增的内容;若需获取某时间点的完整快照,可考虑使用Windows卷影复制服务(VSS),但会增加实现复杂度;
  • 处理网络共享文件时,需确保SMB协议的共享锁机制兼容,两端进程都遵循共享权限规则。

内容的提问来源于stack exchange,提问作者user1937012

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 19:28:24