好久没写博客了 ,今天记录一下目录监控的源码
对目录的监控有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 ) );
}
}