我有一个适用于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功能。但是,我有几个问题。
- FSCTL_READ_USN_JOURNAL立即返回。它不会阻塞。(不过,这可能与问题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记录的TimeStamp
、Reason
或SourceInfo
字段。如果你需要这些,我相信你可以用FSCTL_READ_USN_JOURNAL
读取它们,指定你想要读取的确切USN。
否,因为在关闭文件句柄之前,无法保证操作系统会写入一个字节。
例外情况可能是在文件句柄上调用flush
,然后调用Windows API函数FlushFileBuffers
,但除非写入文件的程序这样做,否则可能不会写入任何字节。
这可以通过监视FASTIO_WRITE和IRP_MJ_WRITE操作的筛选器驱动程序来完成。这是一篇非常好的操作文章。