【问题标题】:Why does ReplaceFile fail with ERROR_SHARING_VIOLATION?为什么 ReplaceFile 因 ERROR_SHARING_VIOLATION 而失败?
【发布时间】:2017-04-24 01:47:08
【问题描述】:

虽然文档含糊不清,但基于 this question and commentsthis answer,我预计使用第三个参数(备份文件名)调用的 ReplaceFile 应该成功,即使在其他进程中打开了源文件和目标文件的句柄没有FILE_SHARE_DELETE 标志。应该通过仅更改不受锁控制的文件元数据(=目录条目)来克服锁。 (所有三个文件都在同一个磁盘驱动器上,因此更改元数据足以重命名它们。)

但是,下面的代码因ERROR_SHARING_VIOLATION 而失败。这不是我的用例,而只是失败的演示。用例是我试图重命名在系统上的其他进程中偶尔(并且不可预测地)打开的文件,例如防病毒或备份程序,这些文件没有使用FILE_SHARE_DELETE 标志。

# python 3
import os
import ctypes

fname1 = 'test1.txt'
fname2 = 'test2.txt'
f1 = open(fname1, 'w')
f1.write(fname1)
f2 = open(fname2, 'w')
f2.write(fname2)

# tmp123 does not exist when the program is started
ctypes.windll.kernel32.ReplaceFileW(fname2, fname1, 'tmp123', 0, None, None) # 0
ctypes.GetLastError() # ERROR_SHARING_VIOLATION

# if we close file handles, it works as expected
f1.close()
f2.close()
ctypes.windll.kernel32.ReplaceFileW(fname2, fname1, 'tmp123', 0, None, None) # 1

为什么?

【问题讨论】:

  • 如果文件在另一个应用程序中打开,您可能无法删除文件,原因与此相同。写完后,如果你完成了,你应该总是关闭文件。我认为这在大多数语言中都很常见。您打开一个文件,除非您关闭对它的第一个引用,否则您无法再次打开它以获得 R/W/A priv。
  • @Fallenreaper 我编辑了问题以澄清我想要做什么。我不会对结果感到惊讶,除了我在问题中链接的帖子非常清楚地表明它应该可以正常工作。
  • 啊,我会刷新并阅读您的文档。
  • 您的一篇帖子说这个问题很难解决,因为它天生就内置在 Windows 中,即针对文件的竞争条件的想法。另一个说重命名不会删除文件,也不应该抛出错误。由于这是一个共享错误,这会让我相信即使重命名它也会导致问题,因为另一个进程仍然打开它。一种选择可能是启动某种子进程,当进程完成它们正在做的任何事情时,它会进行替换。如果需要继续,则必须设置等待,或查找并杀死进程
  • ReplaceFile 失败,因为它尝试使用 DELETE 访问掩码打开 lpReplacedFileName。但这会给出错误ERROR_SHARING_VIOLATION 如果文件已经打开而没有FILE_SHARE_DELETE

标签: python winapi window filesystems


【解决方案1】:

我在使用 Windows DLLs 时遇到过多次类似的问题。

出于性能优化、安全目的和其他一些我没有深入研究的低级别但仍然重要的细节,Windows 中的DLLs 仅加载一次(每个平台/版本/文化/...)。因此,当您引用它们时,就像您在代码中所做的那样,如果它们已经被其他进程加载,它们也已经加载了它们的数据、安全上下文等。

在这种情况下,对 DLL 的调用,即使它是由您的进程执行的,也有不同的上下文:这里是共享事物开始有意义的地方。

如您所知,Python 是一种通用的多平台编程语言,因此它的主要目的是为您提供一个与底层操作系统功能的接口,而与它如何公开这些功能无关。

好吧,事实证明,windows CreateFile 可以作为大量选项提供给它,并且不适用于其他操作系统,其中一个是 dwShareMode,它规定了文件的共享方式与其他进程。 (Here the ufficial documentation)。

如果此参数为零且 CreateFile 成功,则文件或设备无法共享,并且在关闭文件或设备的句柄之前无法再次打开。

您不能请求与具有打开句柄的现有请求中指定的访问模式冲突的共享模式。 CreateFile 将失败,GetLastError 函数将返回 ERROR_SHARING_VIOLATION。

我认为核心 python 开发人员,如果完全使用该函数(如果 windows 支持,则不是 posix 函数),已将该变量的值保留为 0,从而导致您遇到的行为。

为了让您的进程能够调用处理由您自己的代码创建的文件的DLLs 函数,您必须确保没有向它们打开“非共享”描述符。

您至少有两个选择:

  1. 创建直接调用 Windows API 并指定所需共享行为的文件。
  2. 通过上下文管理器打开文件

解决方案2 更简单,更“pythonic”,所以我会用它:

import os
import ctypes

fname1 = 'test1.txt'
fname2 = 'test2.txt'

with open(fname1, 'w') as f1:
   f1.write(fname1)

with open(fname2, 'w') as f2:
   f2.write(fname2)

ctypes.windll.kernel32.ReplaceFileW(fname2, fname1, 'tmp123', 0, None, None)

【讨论】:

    猜你喜欢
    • 2012-04-06
    • 2012-09-08
    • 2013-09-24
    • 2011-07-23
    • 1970-01-01
    • 1970-01-01
    • 2015-12-19
    • 2013-04-08
    • 2018-02-02
    相关资源
    最近更新 更多