如何检查文件是否被其他应用程序使用?

3
我正在使用以下代码来检查文件是否被其他应用程序使用:
HANDLE fh = CreateFile("D:\\1.txt", GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
if (fh == INVALID_HANDLE_VALUE)
{
    MessageBox(NULL, "The file is in use", "Error", 0);
}

如果文件正在被其他应用程序使用,将显示消息框。但是,如果文件不存在,也会显示消息框!
那么我该怎么做才能解决这个问题呢?我应该检查文件是否存在(使用另一个函数),还是可以更改CreateFile()的参数,仅在文件正在使用且存在时返回INVALID_HANDLE_VALUE

我认为这种方法是错误的,我不知道如何编写代码来解决它。但我几乎可以确定文件上一定有某种锁定,因此您需要检查锁定而不是在尝试打开文件时检查失败。另一方面,如果文件无法打开,因为它被锁定了,那么您必须通过某种系统错误代码(可能是errno变量,但我不知道WinAPI)检查原因。 - Iharob Al Asimi
CreateFile 可能因为多种原因而失败;由于文件不存在而导致的失败不应感到意外。你的方法从根本上是有缺陷的。 - Jonathan Potter
如果您要以这种方式使用CreateFile(),请至少正确地使用它。您需要请求对文件的独占访问,而不是只读访问(其他进程可能允许对其文件的读取访问),然后如果失败,您需要使用GetLastError()来区分ERROR_SHARING_VIOLATION(文件正在使用)与ERROR_FILE_NOT_FOUND等错误。 - Remy Lebeau
1
@Remy:由于dwShareMode为零,那段代码确实请求独占访问权限,是吗?(文档没有说明请求的访问权限会受到共享模式的影响。) - Harry Johnston
1
@HarryJohnston:很好的发现,我看错了参数。是的,代码已经在请求对文件的独占访问权限。但是,我关于需要使用GetLastError()来区分不同错误条件的评论仍然成立。 - Remy Lebeau
2个回答

7
如果您想找出哪个进程打开了文件,请使用重启管理器。该过程包括以下步骤(如雷蒙德·陈在他的博客文章如何查找打开文件的进程?中所述):
  1. 创建一个重启管理器会话(RmStartSession)。
  2. 将文件资源添加到会话中(RmRegisterResource)。
  3. 请求受该资源影响的所有进程列表(RmGetList)。
  4. 关闭会话(RmEndSession)。


示例代码:

#include <Windows.h>
#include <RestartManager.h>
#pragma comment(lib, "Rstrtmgr.lib")

bool IsFileLocked( const wchar_t* PathName ) {
    bool isFileLocked = false;

    DWORD dwSession = 0x0;
    wchar_t szSessionKey[CCH_RM_SESSION_KEY + 1] = { 0 };
    if ( RmStartSession( &dwSession, 0x0, szSessionKey ) == ERROR_SUCCESS ) {
        if ( RmRegisterResources( dwSession, 1, &PathName,
                                  0, NULL, 0, NULL ) == ERROR_SUCCESS ) {
            DWORD dwReason = 0x0;
            UINT nProcInfoNeeded = 0;
            UINT nProcInfo = 0;
            if ( RmGetList( dwSession, &nProcInfoNeeded,
                            &nProcInfo, NULL, &dwReason ) == ERROR_MORE_DATA ) {
                isFileLocked = ( nProcInfoNeeded != 0 );
            }
        }
        RmEndSession( dwSession );
    }

    return isFileLocked;
}

WindowsиҝҳжңүдёҖдёӘIFileIsInUseжҺҘеҸЈпјҢз”ЁдәҺжЈҖжөӢж–Ү件жҳҜеҗҰжӯЈеңЁдҪҝз”ЁдёӯгҖӮRestartManagerе’ҢIFileIsInUseйғҪжҳҜеңЁVistaдёӯеј•е…Ҙзҡ„гҖӮ - Remy Lebeau
1
@RemyLebeau 从记忆中来看,一个应用程序必须专门注册IFileInUse才能检测到它,我猜大多数应用程序都没有这样做。 - Jonathan Potter
1
@JonathanPotter:是的,它是一项选择加入的功能。Windows资源管理器使用"IFileIsInUse"来检测正在使用的文件,如果找不到给定文件的"IFileIsInUse",则会回退到"NtQuerySystemInformation",最后根据这篇博客进行操作:你知道吗?Windows 7 酷炫功能 - Remy Lebeau

1

您需要使用GetLastError()来了解为什么CreateFile()失败,例如:

// this is requesting exclusive access to the file, so it will
// fail if the file is already open for any reason. That condition
// is detected by a sharing violation error due to conflicting
// sharing rights...

HANDLE fh = CreateFile("D:\\1.txt", GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
if (fh == INVALID_HANDLE_VALUE)
{
    switch (GetLastError())
    {
        case ERROR_PATH_NOT_FOUND:
        case ERROR_FILE_NOT_FOUND:
            MessageBox(NULL, "The file does not exist", "Error", 0);
            break;

        case ERROR_SHARING_VIOLATION:
            MessageBox(NULL, "The file is in use", "Error", 0);
            break;

        //...

        default:
            MessageBox(NULL, "Error opening the file", "Error", 0);
            break;
    }
}
else
{
    // the file exists and was not in use.
    // don't forget to close the handle...
    CloseHandle(fh);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接