我有一个文件列表,一次只使用一个文件。所以我想知道特定程序正在使用哪个文件。因为我可以使用'unlocker'来查找正在使用的文件,如question所述。但我想要一种编程方式,以便我的程序可以帮助我找到答案。有什么办法吗?
特别是,在任何r / w模式下,简单的'open'函数可以访问using文件,而python不会抛出任何异常。我可以告诉哪个文件仅被'unlocker'使用。
我发现w模式下的python'open'函数已经获得了该特定程序的访问权限,然后程序运行得不好。在这一刻我打开解锁器,我可以看到两个进程访问该文件。是否有任何“弱”方法只能检测文件是否被使用?
答案 0 :(得分:3)
我不确定你想要找到这两个中的哪一个:
但无论如何,答案都是类似的。
显然这是可行的,或者那些工具无法做到,对吧?不幸的是,Python stdlib中没有任何内容可以提供帮助。那么,你是怎么做到的?
您需要通过pywin32
,ctypes
或其他方式访问Windows API函数。
有两种方法可以解决这个问题。第一个与NT内核对象API相关的问题要困难得多,而且只有在需要使用非常旧版本的Windows时才需要它。第二个NtQuerySystemInformation
是您可能想要的那个。
细节非常复杂(并没有详细记录),但它们被解释为in a CodeProject sample program和on the Sysinternals forums。
如果你不理解这些页面上的代码,或者如何从Python中调用它,你真的不应该这样做。如果你得到了要点,但有疑问或卡住,当然你可以在这里(或在sysinternals论坛或其他地方)寻求帮助。
但是,即使您之前使用过ctypes
Windows,也有一些您可能不知道的警告:
许多这些功能要么没有记录,要么文档没有告诉你要找到哪个DLL。它们都在ntdll
或kernel32
;但是,在某些情况下,记录的NtFooBar
名称只是真实ZwFooBar
函数的别名,因此如果您在任一DLL中都找不到NtFooBar
,请查找ZwFooBar
}。
至少在旧版本的Windows上,ZwQuerySystemInformation
没有按照您的预期行事:您无法使用0 SystemInformationLength
调用它,请检查ReturnLength
,分配缓冲区那个尺寸,然后再试一次。你唯一能做的就是从一个有足够空间的缓冲区开始,比方说,8个句柄,尝试一下,看看你是否收到错误STATUS_INFO_LENGTH_MISMATCH,增加数字8,再试一次,直到成功(或失败,出现不同的错误) )。代码看起来像这样(假设您已经定义了SYSTEM_HANDLE
结构):
STATUS_INFO_LENGTH_MISMATCH = 0xc0000004
i = 8
while True:
class SYSTEM_HANDLE_INFORMATION(Structure):
_fields_ = [('HandleCount', c_ulong),
('Handles', SYSTEM_HANDLE * i)]
buf = SYSTEM_HANDLE_INFORMATION()
return_length = sizeof(buf)
rc = ntdll.ZwQuerySystemInformation(SystemHandleInformation,
buf, sizeof(buf),
byref(return_length))
if rc == STATUS_INFO_LENGTH_MISMATCH:
i += 8
continue
elif rc == 0:
return buf.Handles[:buf.HandleCount]
else:
raise SomeKindOfError(rc)
最后,文档并没有在任何地方真正解释这一点,但是从一个你知道的HANDLE到文件到路径名的方法有点复杂。只需使用NtQueryObject(ObjectNameInformation)
就可以返回一个内核对象空间路径名,然后您必须将其映射到DOS路径名,可能的UNC正常NT路径名或\?\ pathname。当然,第一个没有映射驱动器号的网络驱动器上的文件不起作用;前两个都不适用于路径名很长的文件。
当然还有一个更简单的选择:只需通过handle
驱动Unlocker
,subprocess
或其他一些命令行工具。
或者介于两者之间,构建上面链接的CodeProject项目,只需通过ctypes
打开其DLL并调用其GetOpenedFiles
方法,它将为您完成艰苦的工作。
由于项目构建了一个普通的WinDLL样式的DLL,你可以用正常的ctypes
方式调用它。如果您从未使用ctypes
,那么文档中的示例几乎可以向您展示您需要了解的所有内容,但我会给出一些假代码来帮助您入门。
首先,我们需要为您要编写的回调函数创建OF_CALLBACK
类型,如Callback functions中所述。由于OF_CALLBACK
的原型是在.h文件中定义的,我无法到达这里,我只是猜测它;你必须看看真实版本并自己翻译。但是你的代码看起来像这样:
from ctypes import windll, c_int, WINFUNCTYPE
from ctypes.wintypes import LPCWSTR, UINT_PTR, HANDLE
# assuming int (* OF_CALLBACK)(int, HANDLE, int, LPCWSTR, UINT_PTR)
OF_CALLBACK = WINFUNCTYPE(c_int, HANDLE, c_int, LPWCSTR, UINT_PTR)
def my_callback(handle, namelen, name, context):
# do whatever you want with each handle, and whatever other args you get
my_of_callback = OF_CALLBACK(my_callback)
OpenFileFinder = windll.OpenFileFinder
# Might be GetOpenedFilesW, as with most Microsoft DLLs, as explained in docs
OpenFileFinder.GetOpenedFiles.argtypes = (LPCWSTR, c_int, OF_CALLBACK, UINT_PTR)
OpenFileFinder.GetOpenedFiles.restype = None
OpenFileFinder.GetOpenedFiles(ru'C:\Path\To\File\To\Check.txt', 0, my_of_callback, None)
你在回调中获得的内容很可能实际上是指向你将要定义的某种结构或结构列表的指针 - 可能与调用它所需的SYSTEM_HANDLE
相同Nt / Zw直接起作用,所以让我举一个例子 - 如果你在回调中找回SYSTEM_HANDLE *
而不是HANDLE
,它就像这样简单:
class SYSTEM_HANDLE(Structure):
_fields_ = [('dwProcessId', DWORD),
('bObjectType', BYTE),
('bFlags', BYTE),
('wValue', WORD),
('pAddress', PVOID),
('GrantedAccess', DWORD)]
# assuming int (* OF_CALLBACK)(int, SYSTEM_HANDLE *, int, LPCWSTR, UINT_PTR)
OF_CALLBACK = WINFUNCTYPE(c_int, POINTER(SYSTEM_HANDLE), c_int, LPWCSTR, UINT_PTR)
答案 1 :(得分:0)
您可以使用try / except块。
check if a file is open in Python
try:
f = open("file.txt", "r")
except IOError:
print "This file is already in use"