本文介绍了为什么ReplaceFile失败并显示ERROR_SHARING_VIOLATION?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

While the documentation is vague, based on this question and comments and this answer, I expected that ReplaceFile called with the third argument (backup filename) should succeed even if there are handles to source and destination files open in other processes without FILE_SHARE_DELETE flag. It's supposed to be overcome the lock by changing just the file metadata (= directory entry), which is not controlled by the lock. (All three files are on the same disk drive, so changing metadata is enough to rename them.)

但是,下面的代码以ERROR_SHARING_VIOLATION失败.这不是我的用例,而只是失败的证明.用例是,我正在尝试重命名偶尔(且无法预测)在系统上其他进程中打开的文件,例如防病毒程序或备份程序,这些文件都不会使用FILE_SHARE_DELETE标志.

However, the code below fails with ERROR_SHARING_VIOLATION. This is not my use case, but just a demonstration of the failure. The use case is that I'm trying to rename files that are occasionally (and unpredictably) open in other processes on the system, such as antivirus or backup programs, which didn't bother to use FILE_SHARE_DELETE flag.

# 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

为什么?

推荐答案

我在使用Windows DLL时遇到过几次类似的问题.

I had experienced several times similar issues using Windows DLLs.

出于性能优化,安全性目的和其他一些低级但又不重要的细节考虑,Windows中的DLL加载了一次(按平台/版本/文化/...).因此,当您引用它们时,就像在代码中所做的一样,如果它们已经被其他进程加载,那么它们也已经加载了它们的数据,安全上下文等.

For performances optimization, security purposes and a bounch of other low level but nonetheless important details I didn't dig into, DLLs in Windows are loaded once (per platform/version/culture/...). So when you refere them, as you did in your code, if they have been already loaded by some other process they have also already loaded their data, security context and so on.

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

In this case the call to the DLL even if it's performed by your process has a different context: here is where the sharing thing starts making sense.

您已经知道Python是一种通用的多平台编程语言,因此它的主要目的是为您提供与底层OS功能的接口,而与它们的公开方式无关.

As you already know, Python is a general purpose, multiplatform programming language, so it's main aim is to give you an interface to the underlying OS features indipendently of how it exposes them.

好吧,事实证明,Windows CreateFile可以作为大量选项提供给它,并且不适用于其他操作系统,其中一个是dwShareMode,它决定了文件的共享方式与其他过程. (此处提供了简单的文档)

Well, it turns out that the windows CreateFile function as a significant high number of options that can be delivered to it and does not apply to other OS, one of which is dwShareMode that dictates how the file is shared with other processes. (Here the ufficial documentation).

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

You cannot request a sharing mode that conflicts with the access mode that is specified in an existing request that has an open handle. CreateFile would fail and the GetLastError function would return ERROR_SHARING_VIOLATION.

我认为核心python开发人员,如果根本不使用该功能(如果Windows支持,则不使用posix),但将该变量的值保留为0会导致您遇到的行为.

I think core python developers, if using that function at all (not the posix one if supported by windows), had left the value of that variable to 0 leading to the behaviour you're experiencing.

为了使您的进程能够调用处理由您自己的代码创建的文件的DLL s函数,必须确保您没有非共享"的内容.描述符向他们开放.

In order for your process to be able to call the DLLs functions that deals with the files created by your own code you have to be sure you do not have "non shared" descriptors open to them.

您至少有两个选择:

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

解决方案2更容易使用,并且更"pythonic".所以我会用它:

Solution 2 is easier and more "pythonic" so I'll go with it:

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)

这篇关于为什么ReplaceFile失败并显示ERROR_SHARING_VIOLATION?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-30 05:59