在Windows 7中监视文件的更改

Watching a file for changes in Windows 7

本文关键字:文件 监视 Windows      更新时间:2023-10-16

我有一个适用于Windows 7的Visual Studio 2008 C++应用程序,我想在其中查看文件的更改。

文件可能会这样更改:

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.
}

我尝试使用ReadDirectoryChangesW和带有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标志的FindFirstChangeNotification来监视文件的目录。但是,在文件句柄实际关闭之前,这两个API都不会检测到文件更改。

是否有任何方法可以在实际写入文件时,但在关闭文件句柄之前检测更改?

谢谢,PaulH


更新根据@Edwin的建议,我正在尝试使用Journal功能。但是,我有几个问题。

  1. FSCTL_READ_USN_JOURNAL立即返回。它不会阻塞。(不过,这可能与问题2有关)
  2. 不管我的句柄指向哪里(我已经尝试打开目录"C:\Foo\Bar"和文件"C:\Foo\Par\MyFile.txt"的句柄),我似乎都会对C:卷进行任何更改。有没有办法限制FSCTL_READ_USN_JOURNAL给我的东西

为简洁起见,省略了错误检查。

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"%srn", record->FileName );
}

示例输出(这些都不在C:\Foo\Bar目录中):

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

您可以使用

更改日记账操作

(请参阅MSDN文档)

这是检测文件系统中任何更改的唯一100%保证的方法。但这相当复杂。

要读取特定文件或目录的数据,我认为您需要使用FSCTL_READ_FILE_USN_DATA而不是FSCTL_READ_USN_JOURNAL。我相信后者总是检索整个卷的数据。但是,这不会填充您获得的USN记录的TimeStampReasonSourceInfo字段。如果你需要这些,我相信你可以用FSCTL_READ_USN_JOURNAL读取它们,指定你想要读取的确切USN。

否,因为在关闭文件句柄之前,无法保证操作系统会写入一个字节。

例外情况可能是在文件句柄上调用flush,然后调用Windows API函数FlushFileBuffers,但除非写入文件的程序这样做,否则可能不会写入任何字节。

这可以通过监视FASTIO_WRITE和IRP_MJ_WRITE操作的筛选器驱动程序来完成。这是一篇非常好的操作文章。