我不确定你想找到这两个中的哪一个:
但无论哪种方式,答案都是相似的。
显然它是可行的,或者那些工具做不到,对吧?不幸的是,Python 标准库中没有任何帮助。那么,你是怎么做到的呢?
您将需要通过pywin32、ctypes 或其他方式访问 Windows API 函数。
有两种方法可以解决。第一个,与 NT 内核对象 API 混在一起,要困难得多,而且只有在您需要使用非常旧版本的 Windows 时才真正需要。第二个,NtQuerySystemInformation,是你可能想要的。
细节相当复杂(并且没有很好的记录),但在in a CodeProject sample program 和on the Sysinternals forums 进行了解释。
如果您不了解这些页面上的代码,或者不了解如何从 Python 调用它,那么您真的不应该这样做。如果您了解要点,但有疑问或遇到困难,您当然可以在这里(或在 sysinternals 论坛或其他地方)寻求帮助。
但是,即使您以前在 Windows 上使用过 ctypes,也有一些您可能不知道的警告:
其中许多函数要么没有记录,要么文档没有告诉您在哪个 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 路径名或 \?\ 路径名。当然,第一个在没有映射驱动器号的网络驱动器上不起作用;前两个都不适用于路径名很长的文件。
当然还有一个更简单的选择:只需通过subprocess 驱动handle、Unlocker 或其他一些命令行工具。
或者,介于两者之间,构建上面链接的 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)
很可能你在回调中得到的实际上是一个指向某种结构或你必须定义的结构列表的指针——很可能是你调用 Nt/ 时需要的 SYSTEM_HANDLE 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)