PaulH
PaulH

Reputation: 7853

Watching a file for changes in Windows 7

I have a Visual Studio 2008 C++ application for Windows 7 where I would like to watch a file for changes.

The file may be changed like this:

std::ofstream myfile_;

void LogData( const char* data )
{
    myfile_ << data << std::endl;
    // note that the file output buffer is flushed by std::endl, but the file is not closed.
}

I have tried watching the file's directory using both ReadDirectoryChangesW and FindFirstChangeNotification with FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_LAST_ACCESS | FILE_NOTIFY_CHANGE_SECURITY | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_FILE_NAME flags. But, neither of those APIs will detect file changes until the file handle is actually closed.

Is there any way to detect a change when the file is actually written, but before the file handle is closed?

Thanks, PaulH


Update On @Edwin's suggestion, I'm attempting to use the Journal feature. But, I'm having a couple issues.

  1. FSCTL_READ_USN_JOURNAL returns instantly. It does not block. (though, this may be related to issue 2)
  2. Regardless of where my handle points to (I have tried opening a handle to the directory "C:\Foo\Bar" and to the file "C:\Foo\Bar\MyFile.txt") I seem to get any changes made to the the C: volume. Is there a way to limit what FSCTL_READ_USN_JOURNAL gives me?

Error checking omitted for brevity.

boost::shared_ptr< void > directory( 
    ::CreateFileW( L"C:\\Foo\\Bar\\Myfile.txt", 
                   GENERIC_READ, 
                   FILE_SHARE_READ, 
                   NULL, 
                   OPEN_EXISTING, 
                   FILE_ATTRIBUTE_NORMAL, 
                   NULL ), 
    ::CloseHandle );

USN_JOURNAL_DATA journal = { 0 };
DWORD returned = 0;
::DeviceIoControl( directory.get(), FSCTL_QUERY_USN_JOURNAL, NULL, 0, &journal, sizeof( journal ), &returned, NULL );

BYTE buffer[ 4096 ] = { 0 };
READ_USN_JOURNAL_DATA read = { 0, USN_REASON_DATA_EXTEND | USN_REASON_DATA_TRUNCATION, FALSE, 0, 0, journal.UsnJournalID };
::DeviceIoControl( directory.get(), FSCTL_READ_USN_JOURNAL, &read, sizeof( read ), &buffer, sizeof( buffer ), &returned, NULL );

for( USN_RECORD* record = ( USN_RECORD* )( buffer + sizeof( USN ) );
     ( ( BYTE* )record - buffer ) < returned;
     record = ( USN_RECORD* )( ( BYTE* )record + record->RecordLength ) )
{
    ATLTRACE( L"%s\r\n", record->FileName );
}

Example output (none of these are in the C:\Foo\Bar directory):

AeXProcessList.txt`
AeXProcessList.txt`
AeXAMInventory.txt`
AeXAMInventory.txt`
AeXProcessList.txt`
AeXProcessList.txtP
access.log`
mysqlgeneral.log
E804.tmp
apache_error.log
E804.tmp
CHROME.EXE-5FE9909D.pfh
CHROME.EXE-5FE9909D.pfp
SyncData.sqlite3-journal
CHROME.EXE-5FE9909D.pfh
CHROME.EXE-5FE9909D.pfP
1211.tmp
SyncData.sqlite3-journal
AeXAMInventory.txt

Upvotes: 2

Views: 2962

Answers (4)

Jerry Coffin
Jerry Coffin

Reputation: 490108

To read data for a specific file or directory, I believe you want to use FSCTL_READ_FILE_USN_DATA instead of FSCTL_READ_USN_JOURNAL. I believe the latter always retrieves data for an entire volume. That does not, however, fill in the TimeStamp, Reason, or SourceInfo fields of the USN record you get. If you need those, I believe you can read them with FSCTL_READ_USN_JOURNAL, specifying the exact USN you want to read.

Upvotes: 1

Carey Gregory
Carey Gregory

Reputation: 6846

This can be done with a filter driver that monitors the FASTIO_WRITE and IRP_MJ_WRITE operations. Here is a pretty good how-to article.

Upvotes: 0

engf-010
engf-010

Reputation: 3929

You can use

Change Journal Operations

(see MSDN docs)

That's the only 100% garanteed way to detect any change in the filesystem. But it's pretty complicated.

Upvotes: 2

orlp
orlp

Reputation: 117681

No, because until you close the file handle there is no guarantee a single byte ever gets written by the OS.

The exception would probably be by calling flush on your file handle and then call the Windows API function FlushFileBuffers, but unless the program writing into the file does this no bytes probably get written.

Upvotes: 0

Related Questions