接着上面的
DWORD dwChangeFilter = GetChangeFlags1();
进去看源码:
DWORD CDirWatcherDlg::GetChangeFlags1()
{
DWORD dwChangeNotifyFlags = 0UL;
FLAG_INFO arFlagInfo[]={
{IDC_CHECK_FILE_NAME1, FILE_NOTIFY_CHANGE_FILE_NAME},
{IDC_CHECK_DIR_NAME1, FILE_NOTIFY_CHANGE_DIR_NAME},
{IDC_CHECK_LAST_WRITE1, FILE_NOTIFY_CHANGE_LAST_WRITE},
{IDC_CHECK_LAST_ACCESS1, FILE_NOTIFY_CHANGE_LAST_ACCESS},
{IDC_CHECK_SIZE1, FILE_NOTIFY_CHANGE_SIZE},
{IDC_CHECK_ATTRIBUTES1, FILE_NOTIFY_CHANGE_ATTRIBUTES},
{IDC_CHECK_LAST_WRITE1, FILE_NOTIFY_CHANGE_LAST_WRITE},
{IDC_CHECK_LAST_ACCESS1, FILE_NOTIFY_CHANGE_LAST_ACCESS},
{IDC_CHECK_CREATION1, FILE_NOTIFY_CHANGE_CREATION},
{IDC_CHECK_SECURITY1, FILE_NOTIFY_CHANGE_SECURITY}
};
for(int i = 0; i < sizeof(arFlagInfo)/sizeof(arFlagInfo[0]); ++i)
{
dwChangeNotifyFlags |= (IsDlgButtonChecked(arFlagInfo[i].nCtrlId) == BST_CHECKED)? arFlagInfo[i].nFlag : 0;
}
return dwChangeNotifyFlags;
}
找到FLAG_INFO :
typedef struct {
UINT nCtrlId;
UINT nFlag;
} FLAG_INFO;
看样子就是遍历arFlagInfo[] ,仔细一看是取那么多复选框的状态 然后返回
继续回到OnBtnMonitor()
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 );
进去
BOOL CDirectoryChangeWatcher::IsWatchingDirectory(const CString & strDirName)const
/*********************************************
Determines whether or not a directory is being watched
be carefull that you have the same name path name, including the backslash
as was used in the call to WatchDirectory().
ie:
"C:\\Temp"
is different than
"C:\\Temp\\"
**********************************************/
{
CSingleLock lock( const_cast<CCriticalSection*>(&m_csDirWatchInfo), TRUE);
ASSERT( lock.IsLocked() );
int i;
if( GetDirWatchInfo(strDirName, i) )
return TRUE;
return FALSE;
}
弄一个互斥的锁 保证代码块的独占资源
进入GetDirWatchInfo() 他有3个重载
CDirectoryChangeWatcher::CDirWatchInfo * CDirectoryChangeWatcher::GetDirWatchInfo(const CString & strDirName, int & ref_nIdx)const
{
if( strDirName.IsEmpty() )// can't be watching a directory if it's you don't pass in the name of it...
return FALSE; //
CSingleLock lock(const_cast<CCriticalSection*>(&m_csDirWatchInfo), TRUE);
int max = m_DirectoriesToWatch.GetSize();
CDirWatchInfo * p = NULL;
for(int i = 0; i < max; ++i )
{
if( (p = m_DirectoriesToWatch[i]) != NULL
&& p->m_strDirName.CompareNoCase( strDirName ) == 0 )
{
ref_nIdx = i;
return p;
}
}
return NULL;//NOT FOUND
}
做防范检查之后加锁
在分析之前先了解一个类成员m_DirectoriesToWatch
CTypedPtrArray<CPtrArray, CDirWatchInfo*> m_DirectoriesToWatch; //holds info about the directories that we're watching.
这个大家百度一下 我理解的是一个指针数组 里面存的是CDirWatchInfo指针
贴两个网址大家慢慢去看
http://anony3721.blog.163.com/blog/static/5119742011420405729/
http://baike.baidu.com/link?url=RcSG6D-gofozP9XwUYLSZSEV-__qZSu1N0-_M0hPBEYFckTc8qaU1TKOVORunckehh0Olfga6lztTH8-9k_nGa
使用POSITION 类型来遍历m_DirectoriesToWatch
http://technet.microsoft.com/zh-cn/magazine/sdbk3yw6.aspx列出了m_DirectoriesToWatch的可用函数
继续看GetDirWatchInfo()
他就是查找这个目录是否加入了监控队列
如果找到就返回目录信息对象的指针 还返回他的位置
回到IsWatchingDirectory()
现在就很容易明白这个函数的意思了 判断指定目录是否已经加入了监控队列
继续返回 void CDirWatcherDlg::OnBtnMonitor()
查看UnwatchDirectory
如果指定目录已经加入了监控队列 就干掉他
BOOL CDirectoryChangeWatcher::UnwatchDirectory(const CString & strDirToStopWatching)
/***************************************************************
FUNCTION: UnwatchDirectory(const CString & strDirToStopWatching -- if this directory is being watched, the watch is stopped
****************************************************************/
{
BOOL bRetVal = FALSE;
if( m_hCompPort != NULL )//The io completion port must be open
{
ASSERT( !strDirToStopWatching.IsEmpty() );
CSingleLock lock(&m_csDirWatchInfo, TRUE);
ASSERT( lock.IsLocked() );
int nIdx = -1;
CDirWatchInfo * pDirInfo = GetDirWatchInfo(strDirToStopWatching, nIdx);
if( pDirInfo != NULL
&& nIdx != -1 )
{
//stop watching this directory
VERIFY( pDirInfo->UnwatchDirectory( m_hCompPort ) );
//cleanup the object used to watch the directory
m_DirectoriesToWatch.SetAt(nIdx, NULL);
pDirInfo->DeleteSelf(this);
bRetVal = TRUE;
}
}
return bRetVal;
}
我们看到初始化时m_hCompPort被赋为空
就直接返回FALSE;
继续回到void CDirWatcherDlg::OnBtnMonitor()
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();
}
}
获取两个过滤edit的值 对于“包含过滤”来说 *.*和空表示一样的意思
之后做最后一件事:
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 ) );
}
其中看看m_DirChangeHandler 声明:CDirectoryChangeHandler_ListBox m_DirChangeHandler;
class CDirectoryChangeHandler_ListBox : public CDirectoryChangeHandler
这是监控目录监控之后的处理
进入WatchDirectory
ASSERT( dwChangesToWatchFor != 0);
if( strDirToWatch.IsEmpty()
|| dwChangesToWatchFor == 0
|| pChangeHandler == NULL )
{
TRACE(_T("ERROR: You've passed invalid parameters to CDirectoryChangeWatcher::WatchDirectory()\n"));
::SetLastError(ERROR_INVALID_PARAMETER);
return ERROR_INVALID_PARAMETER;
}
这是参数检查
//double check that it's really a directory
if( !IsDirectory( strDirToWatch ) )
{
TRACE(_T("ERROR: CDirectoryChangeWatcher::WatchDirectory() -- %s is not a directory!\n"), strDirToWatch);
::SetLastError(ERROR_BAD_PATHNAME);
return ERROR_BAD_PATHNAME;
}
确定指定路径是个目录
if( IsWatchingDirectory( strDirToWatch) )
{
VERIFY(
UnwatchDirectory( strDirToWatch )
);
}
如果已经监控了 就干掉
CPrivilegeEnabler::Instance(); 跟进去
发现这个类似单例模式 写法新奇 确实实现了单例功能
这个单例类初始化的时候做了一件事
BOOL EnablePrivilege(LPCTSTR pszPrivName, BOOL fEnable /*= TRUE*/)
//
// I think this code is from a Jeffrey Richter book...
//
// Enables user priviledges to be set for this process.
//
// Process needs to have access to certain priviledges in order
// to use the ReadDirectoryChangesW() API. See documentation.
{
BOOL fOk = FALSE;
// Assume function fails
HANDLE hToken;
// Try to open this process's access token
if (OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES, &hToken))
{
// privilege
TOKEN_PRIVILEGES tp = { 1 };
if( LookupPrivilegeValue(NULL, pszPrivName, &tp.Privileges[0].Luid) )
{
tp.Privileges[0].Attributes = fEnable ? SE_PRIVILEGE_ENABLED : 0;
AdjustTokenPrivileges(hToken, FALSE, &tp,
sizeof(tp), NULL, NULL);
fOk = (GetLastError() == ERROR_SUCCESS);
}
CloseHandle(hToken);
}
return(fOk);
}
先了解一下
SE_BACKUP_NAME 备份文件和目录
SE_RESTORE_NAME 还原文件和目录
SE_CHANGE_NOTIFY_NAME 跳过遍历检查,允许用户来回移动目录
EnablePrivilege()
OpenProcessToken函数用来打开与进程相关联的访问令牌
LookupPrivilegeValue函数查看系统权限的特权值,返回信息到一个LUID结构体里。
djustTokenPrivileges这个函数启用或禁止 指定访问令牌的特权。
这三个函数加在一起就是干提升权限的
那这样的话 我们提升了3个权限
回到CDirectoryChangeWatcher.WatchDirectory ()
CreateFile打开目录
重点来了
创建一个监控信息对象 我们进去看看
CDirWatchInfo * pDirInfo = new CDirWatchInfo( hDir, strDirToWatch, pChangeHandler, dwChangesToWatchFor, bWatchSubDirs, m_bAppHasGUI, szIncludeFilter, szExcludeFilter, m_dwFilterFlags);
if( !pDirInfo )
{
TRACE(_T("WARNING: Couldn't allocate a new CDirWatchInfo() object --- File: %s Line: %d\n"), _T( __FILE__ ), __LINE__);
CloseHandle( hDir );
::SetLastError(ERROR_OUTOFMEMORY);
return ERROR_OUTOFMEMORY;
}
如果监控信息对象创建失败就返回
之后我们要进入监控信息对象的构造函数 开始了解解决溢出的问题