3

I am using the following code to check if a file is being used by another application:

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);
}

If the file is being used by another application, the message box is displayed. However, the message box is also displayed if the file does not exists!

So what should I do to solve this problem, should I also check if the file exists (using another function), or can the parameters of CreateFile() be changed to only return INVALID_HANDLE_VALUE if the file is in use and does exists?

James
  • 703
  • 8
  • 17
  • I think the approach is wrong, I have no idea of how to write the code to solve it. But I am almost sure that there must be some kind of lock on the file, so you need to check for the lock instead of checking for failure when trying to open the file. On the other hand if the file fails to open because a lock is held on it, then you must check for the reason via some kind of system error code, perhaps the `errno` variable but I don't know WinAPI. – Iharob Al Asimi Jun 30 '15 at 12:08
  • `CreateFile` can fail for many reasons; failure because the file does not exist should not be a surprise. Your approach is fundamentally flawed. – Jonathan Potter Jun 30 '15 at 12:20
  • If you are going to use `CreateFile()` in this manner, at least use it the right way. You need to request **exclusive access** to the file, not **read-only access** (other processes might allow read access to their files), and then if it fails you need to use `GetLastError()` to differentiate between `ERROR_SHARING_VIOLATION` (file in use) vs `ERROR_FILE_NOT_FOUND`, etc. – Remy Lebeau Jun 30 '15 at 18:35
  • 1
    @Remy: since `dwShareMode` is zero, that code does request exclusive access, doesn't it? (The documentation doesn't say anything about the share mode being affected by the requested access.) – Harry Johnston Jun 30 '15 at 22:28
  • 1
    @HarryJohnston: good catch, I was looking at the wrong parameter. Yes, the code is already requesting exclusive access to the file. However, my comment about needing to use `GetLastError()` to differentiate between different error conditions still stands. – Remy Lebeau Jun 30 '15 at 23:03

2 Answers2

7

If you wish to find out, which process has a file open, use the Restart Manager. The procedure consists of the following steps (as outlined in Raymond Chen's blog entry How do I find out which process has a file open?):

  1. Create a Restart Manager session (RmStartSession).
  2. Add a file resource to the session (RmRegisterResource).
  3. Ask for a list of all processes affected by that resource (RmGetList).
  4. Close the session (RmEndSession).


Sample code:
#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;
}
IInspectable
  • 46,945
  • 8
  • 85
  • 181
  • Windows also has an [`IFileIsInUse`](https://msdn.microsoft.com/en-us/library/windows/desktop/bb775874.aspx) interface for detecting if a file is in use. Both RestartManager and `IFileIsInUse` were introduced in Vista. – Remy Lebeau Jun 30 '15 at 18:38
  • 1
    @RemyLebeau From memory an app has to specifically register for `IFileInUse` to detect it, and I'd guess most don't. – Jonathan Potter Jun 30 '15 at 19:42
  • 1
    @JonathanPotter: true, it is an opt-in feature. Windows Explorer uses `IFileIsInUse` to detect files in use, falling back to `NtQuerySystemInformation` when `IFileIsInUse` is not found for a given file, at last according to this blog: [Did you know? Windows 7 Cool feature](http://programing-fun.blogspot.com/2013/08/did-you-know-windows-7-cool-feature.html). – Remy Lebeau Jun 30 '15 at 20:07
1

You need to use GetLastError() to know why CreateFile() failed, eg:

// 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);
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770