目录监控源码分析1

本文详细解析了使用Windows API ReadDirectoryChangesW()进行目录监控的原理与实践,特别是针对缓冲区溢出可能导致监控遗漏的问题,介绍了如何通过CDirectoryChangeWatcher类实现更稳定的监控方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

好久没写博客了  ,今天记录一下目录监控的源码

对目录的监控有windows api直接支持 ReadDirectoryChangesW()  这是主要的api

对于这个api的心得我直接贴链接了:

http://hi.baidu.com/wytzsjzly/item/919ac32076405b102b0f1c87

上面的帖子说明了两点:

1:#define _WIN32_WINNT 0x0500

2:如果缓存溢出,有些改变信息会丢失  -- 这个的意思就是会使你的监控不到位

解决办法:http://www.codeproject.com/file/directorychangewatcher.asp

这次写的分析主要是分析http://www.codeproject.com/Articles/950/CDirectoryChangeWatcher-ReadDirectoryChangesW-all中demo的源码


开工:

环境:win7 x64 + vs2010

遇到的第一个问题:error C2248: “CDirectoryChangeWatcher::CDirWatchInfo”: 无法访问 private class(在“CDirectoryChangeWatcher”类中声明)

解决方法:

http://www.planet3dnow.de/vbulletin/archive/index.php/t-365915.html

DirectoryChange.h  406line增加  friend class CDelayedDirectoryChangeHandler;


现在就可以看到正常运行了


分析demo开始:

F5开始 开始初始化全局变量

CDirWatcherApp theApp;

接着BOOL CDirWatcherApp::InitInstance()

增加3D效果

#ifdef _AFXDLL
Enable3dControls(); // Call this when using MFC in a shared DLL
#else
Enable3dControlsStatic(); // Call this when linking to MFC statically
#endif

之后弹出CDirWatcherDlg 对话框

CDirWatcherDlg dlg;
m_pMainWnd = &dlg;
int nResponse = dlg.DoModal();

弹出后 分析下各个按钮的作用  ,因为这些按钮会影响某些设置  ps:我们要核心分析的还是缓冲溢出处理  但我们也要先剪掉树枝


先看设置过滤标识设置按钮:

void CDirWatcherDlg::OnBtnSetFilterFlags() 
{
CSetFilterFlagsDlg dlg(this);


dlg.m_dwFilterFlags = m_DirWatcher.GetFilterFlags();


if( dlg.DoModal() == IDOK )
{
//
// The next time a watch is started, these filter flags
// will be in effect.
//
m_DirWatcher.SetFilterFlags( dlg.m_dwFilterFlags );


if( dlg.m_dwFilterFlags & CDirectoryChangeWatcher::FILTERS_DONT_USE_FILTERS )
EnableFilterEditCtrls( FALSE );
else
EnableFilterEditCtrls( TRUE );
}

}

m_DirWatcher的声明是CDirectoryChangeWatcher m_DirWatcher;一个目录监控类的对象

m_DirWatcher先是得到设置对话框的默认值  在按下ok之后再获取一次

跟进去吧  不然我会绕疯的   

DWORD GetFilterFlags()const{return m_dwFilterFlags;}

返回的是CDirectoryChangeWatcher过滤标识m_dwFilterFlags

跟一下m_dwFilterFlags:

在CDirectoryChangeWatcher实例化时有这样的构造函数:

CDirectoryChangeWatcher::CDirectoryChangeWatcher(bool bAppHasGUI /*= true*/, DWORD dwFilterFlags/*=FILTERS_CHECK_FILE_NAME_ONLY*/)
: m_hCompPort( NULL )
 ,m_hThread( NULL )
 ,m_dwThreadID( 0UL )
 ,m_bAppHasGUI( bAppHasGUI )
 ,m_dwFilterFlags( dwFilterFlags == 0? FILTERS_DEFAULT_BEHAVIOR : dwFilterFlags)

{}

m_dwFilterFlags默认是FILTERS_DEFAULT_BEHAVIOR(非0),目录监控类实例化之后  m_dwFilterFlags就是FILTERS_DEFAULT_BEHAVIOR


接下来看m_DirWatcher.SetFilterFlags( dlg.m_dwFilterFlags );

而dlg.m_dwFilterFlags的设置来至于


void CSetFilterFlagsDlg::OnOK() 
{
m_dwFilterFlags = 0UL;
if( IsDlgButtonChecked(IDC_RADIO_DONT_USE_FILTERS) == BST_CHECKED)
m_dwFilterFlags = CDirectoryChangeWatcher::FILTERS_DONT_USE_FILTERS;
else
if( IsDlgButtonChecked(IDC_RADIO_CHECK_FULL_PATH) == BST_CHECKED)
m_dwFilterFlags = CDirectoryChangeWatcher::FILTERS_CHECK_FULL_PATH;
else
if( IsDlgButtonChecked(IDC_RADIO_CHECK_PARTIAL_PATH) == BST_CHECKED)
m_dwFilterFlags = CDirectoryChangeWatcher::FILTERS_CHECK_PARTIAL_PATH;
else
{
m_dwFilterFlags = CDirectoryChangeWatcher::FILTERS_CHECK_FILE_NAME_ONLY;
}


if( IsDlgButtonChecked(IDC_RADIO_DONT_TEST_HANDLER) == BST_CHECKED)
m_dwFilterFlags |= CDirectoryChangeWatcher::FILTERS_DONT_USE_HANDLER_FILTER;
else
if( IsDlgButtonChecked(IDC_RADIO_TEST_HANDLER_BEFORE) == BST_CHECKED)
m_dwFilterFlags |= CDirectoryChangeWatcher::FILTERS_TEST_HANDLER_FIRST;
else
{
//else nothing... there's no flag for test after... it's the default behavior
}


if( IsDlgButtonChecked(IDC_CHECK_WATCHSTARTED) != BST_CHECKED )
m_dwFilterFlags |= CDirectoryChangeWatcher::FILTERS_NO_WATCHSTART_NOTIFICATION;
if( IsDlgButtonChecked(IDC_CHECK_WATCHSTOPPED) != BST_CHECKED)
m_dwFilterFlags |= CDirectoryChangeWatcher::FILTERS_NO_WATCHSTOP_NOTIFICATION;




CDialog::OnOK();
}


先分析下CSetFilterFlagsDlg::m_dwFilterFlags 的初始化   反正本着学习的精神  啰嗦一点也没啥事

在构造的时候初始化为0UL;

m_dwFilterFlags = 0UL;


dlg.m_dwFilterFlags = m_DirWatcher.GetFilterFlags(); 这句设置为FILTERS_DEFAULT_BEHAVIOR


DoModal的时候对话框初始化

if( dlg.DoModal() == IDOK )

顺便看下对话框初始化:

if( m_dwFilterFlags & CDirectoryChangeWatcher::FILTERS_DONT_USE_FILTERS )
CheckDlgButton(IDC_RADIO_DONT_USE_FILTERS, BST_CHECKED);
else
if( m_dwFilterFlags & CDirectoryChangeWatcher::FILTERS_CHECK_FULL_PATH )
CheckDlgButton(IDC_RADIO_CHECK_FULL_PATH, BST_CHECKED);
else
if( m_dwFilterFlags & CDirectoryChangeWatcher::FILTERS_CHECK_PARTIAL_PATH)
CheckDlgButton(IDC_RADIO_CHECK_PARTIAL_PATH, BST_CHECKED);
else{
//the default:
CheckDlgButton(IDC_RADIO_CHECK_FILENAME_ONLY, BST_CHECKED);
}


初始化时选中

IDC_RADIO_CHECK_FILENAME_ONLY  

IDC_RADIO_TEST_HANDLER_AFTER

IDC_CHECK_WATCHSTARTED

IDC_CHECK_WATCHSTOPPED

点击ok的时候同理对其进行设置

回到void CDirWatcherDlg::OnBtnSetFilterFlags() 

if( dlg.m_dwFilterFlags & CDirectoryChangeWatcher::FILTERS_DONT_USE_FILTERS )
EnableFilterEditCtrls( FALSE );
else
EnableFilterEditCtrls( TRUE );

如果不过滤就禁用4个edit控件


接下来我们看看第二个按钮

void CDirWatcherDlg::EnableFilterEditCtrls(BOOL bEnable)

这是用来测试过滤的

void CTestFilterPatternDlg::OnBtnTest() 
{
if(!UpdateData(TRUE) )
return;


CString str;


if( !(m_strTestPath.IsEmpty() && m_strTestPattern.IsEmpty()) )
{
str.Format(_T("%s"), PathMatchSpec(m_strTestPath, m_strTestPattern)? _T("TRUE") : _T("FALSE"));
}

SetDlgItemText(IDC_STATIC_TEST_RESULTS, str);

}

主要是PathMatchSpec函数:

BOOL PathMatchSpec( LPCTSTR pszFileParam,LPCTSTR pszSpec)
功能:用带有通配符(*号和?号)的字符串去比较另一个字符串
例如:PathMatchSpec( “http://news.sina.com.cn” , “*sina.com*” ) 返回TRUE
PathMatchSpec( “c:\\hello.txt” , “*.txt” ) 返回TRUE

这就是一个通配符的测试  而这个测试也不会影响到程序的其他部分

就往下走了

退出按钮就不用说了

由于上下都是一样的  只分析一半就算完成了

现在我觉得从界面看上去需要分析的5个地方:

1:复选框

2:选择要监控的目录

3:过滤设置

4:开始监控按钮

5:清空list

我们先看监控按钮  因为其他的都是设置或清理  都会在监控按钮中用到 

void CDirWatcherDlg::OnBtnMonitor() 

{
// TODO: Add your control notification handler code here


DWORD dwChangeFilter = GetChangeFlags1();
BOOL bWatchSubDir = (BOOL)(IsDlgButtonChecked(IDC_CHECK_SUBDIR1) == BST_CHECKED);

if( dwChangeFilter == 0 ){
MessageBox(_T("You must select one or more change filters in order to monitor a directory"));
return;
}
if( m_DirWatcher.IsWatchingDirectory( m_strDirectoryToMonitor) )
m_DirWatcher.UnwatchDirectory( m_strDirectoryToMonitor );


GetDlgItemText(IDC_EDIT_DIR_TO_MONITOR, m_strDirectoryToMonitor);
if( m_DirWatcher.GetFilterFlags() & CDirectoryChangeWatcher::FILTERS_DONT_USE_FILTERS )
{
m_strIncludeFilter1.Empty();
m_strExcludeFilter1.Empty();
}
else
{
GetDlgItemText(IDC_EDIT_INCLUDE_FILTER1, m_strIncludeFilter1);
GetDlgItemText(IDC_EDIT_EXCLUDE_FILTER1, m_strExcludeFilter1);


if( m_strIncludeFilter1 == _T("*.*") )
{
MessageBox(_T("For performance reasons, don't use *.* as the include filter. An empty string means the same thing."));
SetDlgItemText(IDC_EDIT_INCLUDE_FILTER1, _T(""));
m_strIncludeFilter1.Empty();
}
}



DWORD dwWatch = 0;
if( ERROR_SUCCESS != (dwWatch = m_DirWatcher.WatchDirectory(m_strDirectoryToMonitor, 
dwChangeFilter,
&m_DirChangeHandler,
bWatchSubDir,
m_strIncludeFilter1,
m_strExcludeFilter1)) )
{
MessageBox(_T("Failed to start watch:\n") + GetLastErrorMessageString( dwWatch ) );
}



}




































评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值