【问题标题】:In python how can i know a given file is being used在 python 中我怎么知道给定的文件正在被使用
【发布时间】:2013-08-03 02:01:32
【问题描述】:

我有一个文件列表,一次只有一个文件在使用。所以我想知道特定程序正在使用哪个文件。因为我可以使用'unlocker'来找出像question提到的那样使用的文件。但是我想要一种编程方式,以便我的程序可以帮助我找出答案。有什么办法吗?

特别是,在任何 r/w 模式下的简单“打开”函数都可以访问使用文件,python 不会抛出任何异常。我可以知道哪个文件仅由“解锁器”使用。

我发现 w 模式下的 python 'open' 函数已经获得了该特定程序的访问权限,然后该程序就不能正常工作了。在这一刻,我打开解锁器,我可以看到两个进程正在访问该文件。有没有什么“弱”的方法只能检测文件是否被使用?

【问题讨论】:

  • 打开文件没有.filename.swp文件吗?
  • @Matt:不,没有。
  • @Matt:只有 VIM 对打开的文件这样做。

标签: python windows file process


【解决方案1】:

我不确定你想找到这两个中的哪一个:

  • 给定文件是否存在任何现有 HANDLE,例如 handleProcess Explorer 工具显示?
  • 给定文件是否存在任何现有的锁定句柄,如Unlocker 工具显示的那样。

但无论哪种方式,答案都是相似的。

显然它是可行的,或者那些工具做不到,对吧?不幸的是,Python 标准库中没有任何帮助。那么,你是怎么做到的呢?

您将需要通过pywin32ctypes 或其他方式访问 Windows API 函数。

有两种方法可以解决。第一个,与 NT 内核对象 API 混在一起,要困难得多,而且只有在您需要使用非常旧版本的 Windows 时才真正需要。第二个,NtQuerySystemInformation,是你可能想要的。

细节相当复杂(并且没有很好的记录),但在in a CodeProject sample programon the Sysinternals forums 进行了解释。

如果您不了解这些页面上的代码,或者不了解如何从 Python 调用它,那么您真的不应该这样做。如果您了解要点,但有疑问或遇到困难,您当然可以在这里(或在 sysinternals 论坛或其他地方)寻求帮助。

但是,即使您以前在 Windows 上使用过 ctypes,也有一些您可能不知道的警告:

其中许多函数要么没有记录,要么文档没有告诉您在哪个 DLL 中找到它们。它们都将位于 ntdllkernel32;然而,在某些情况下,记录在案的 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 驱动handleUnlocker 或其他一些命令行工具。


或者,介于两者之间,构建上面链接的 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)

【讨论】:

  • 我知道确切的程序类名(我使用 spyxx.exe 检查它),但 pywin32 对我来说似乎很难。不过我会试试的。
  • 我认为您的意思是窗口类名称,这对您根本没有帮助。但是,无论您拥有什么信息,它都不会让您的工作变得更轻松;没有办法枚举每个打开的句柄,然后过滤掉你想要的句柄(按进程、按路径等)。
  • @prehawk:另外,在我上面的例子中,我用ctypes而不是pywin32来做。我不知道您是否可以通过pywin32 访问您需要的所有内容,但如果可以的话,它可能会比我的示例容易得多。
  • @prehawk:最后一件事:如果您知道如何设置和使用编译器,Cython 可能会容易得多。它使您可以编写与 Python 代码非常相似的代码,但可以直接调用 C 函数,而无需使用 ctypes。从长远来看,它可能会让你的生活更轻松。但在短期内,我怀疑这只会意味着更陡峭的学习曲线。
  • +1 表示 Cython 评论。这看起来像是一个很好的选择 C ​​代码的候选者,特别是因为它只会在 Windows 中被调用。 (当然,你关于学习 C 是一个糟糕的短期解决方案的观点也是正确的)。
【解决方案2】:

你可以使用 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"

【讨论】:

  • 对不起,我的问题刚才不是很清楚。正在使用的文件可以在 r/w 模式下“打开”。这就是让我感到困惑的地方。有没有其他办法?
猜你喜欢
  • 1970-01-01
  • 2018-08-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多