加载中
加载中
表情图片
评为精选
鼓励
加载中...
分享
加载中...
文件下载
加载中...
修改排序
加载中...
【小程序】文件夹监视器
acmilan2015/07/15软件综合 IP:四川
使用WinAPI监视文件夹变化:ReadDirectoryChangesW

实现界面(Visual C++ 2010):
捕获.png

用这个之前要先用CreateFile打开文件夹:
Other
m_dirhandle = ::CreateFile( // 打开所选目录!             path, // 目录名             FILE_LIST_DIRECTORY, // 以列出目录权限打开             FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, // 允许其它程序读写删除             NULL, // 没有安全选项             OPEN_EXISTING, // 存在时打开             FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OVERLAPPED, // 备份存取异步存取             NULL); // 没有文件模板

然后新建线程进行监视:
Other
char notify[65536]; // 储存FILE_NOTIFY_INFORMATION链表的缓冲区 DWORD dwBytes; // 返回字节数 FILE_NOTIFY_INFORMATION *pnotify; // FILE_NOTIFY_INFORMATION指针                  DWORD monflag = FILE_NOTIFY_CHANGE_CREATION             | FILE_NOTIFY_CHANGE_FILE_NAME             | FILE_NOTIFY_CHANGE_LAST_WRITE; // 监控的内容                  while(true) // 监控循环 {     memset(notify, 0, sizeof(notify)); // 清零缓冲区     pnotify = (FILE_NOTIFY_INFORMATION*)notify; // 指向缓冲区开头的链表第一项                      if (::ReadDirectoryChangesW(         pWnd->m_dirhandle, // 目录句柄         notify, sizeof(notify), // 缓冲区及大小         TRUE, // 是否监控子项目         monflag, // 监控的内容         &dwBytes, // 返回字节数         NULL, NULL)) // 异步调用相关,这里是同步调用,因此全填NULL     {         if (dwBytes == 0) // 缓冲区溢出         {             // TODO: 缓冲区溢出,将无任何信息被返回         }         else             while (true) // 链表迭代循环             {                 // TODO: 处理返回的FILE_NOTIFY_INFORMATION结构                                  if (pnotify->NextEntryOffset) // 如果返回了另一个项目(通常是新文件名)                 {                     pnotify = (FILE_NOTIFY_INFORMATION *)                         ((char*)pnotify + pnotify->NextEntryOffset); // 迭代链表下一项                     continue; // 继续                 }                 break; // 退出             }     } }

监视完成之后关闭文件夹:
Other
if (m_dirhandle) // 如果目录已被打开,则关闭目录 {     CloseHandle(m_dirhandle);     m_dirhandle = INVALID_HANDLE_VALUE; }

附件包含示例程序和源代码,基于VS2010和MFC
attachment icon FolderMonitor.rar 826.44KB RAR 20次下载
来自:计算机科学 / 软件综合
3
新版本公告
~~空空如也
acmilan 作者
10年2个月前 IP:四川
779026
向导生成的所有控件变量:
Other
void CFolderMonitorDlg::DoDataExchange(CDataExchange* pDX) {     CDialogEx::DoDataExchange(pDX);     DDX_Control(pDX, IDC_BTN_START, m_startbutton);     DDX_Control(pDX, IDC_EDIT1, m_folderedit);     DDX_Control(pDX, IDC_MFCSHELLTREE1, m_foldertree);     DDX_Control(pDX, IDC_LIST1, m_resultlist);     DDX_Control(pDX, IDC_CHECK1, m_flag_filename);     DDX_Control(pDX, IDC_CHECK2, m_flag_dirname);     DDX_Control(pDX, IDC_CHECK3, m_flag_attr);     DDX_Control(pDX, IDC_CHECK4, m_flag_size);     DDX_Control(pDX, IDC_CHECK5, m_flag_lastwrite);     DDX_Control(pDX, IDC_CHECK6, m_flag_lastaccess);     DDX_Control(pDX, IDC_CHECK7, m_flag_creation);     DDX_Control(pDX, IDC_CHECK8, m_flag_security); }

其它变量:
Other
CString m_getpath; CString m_getopr; CString m_gettime; CWinThread *m_monthread; HANDLE m_dirhandle; CMutex m_mutex;

消息映射:
Other
#define WM_TRHEADMSG (WM_USER + 8) BEGIN_MESSAGE_MAP(CFolderMonitorDlg, CDialogEx)     ON_WM_SYSCOMMAND()     ON_WM_PAINT()     ON_WM_QUERYDRAGICON()     ON_NOTIFY(TVN_SELCHANGED, IDC_MFCSHELLTREE1, &CFolderMonitorDlg::OnTvnSelchangedMfcshelltree1)     ON_BN_CLICKED(IDC_BTN_START, &CFolderMonitorDlg::OnBnClickedBtnStart)     ON_MESSAGE(WM_TRHEADMSG, &CFolderMonitorDlg::ThreadMsg)     ON_WM_DESTROY()     ON_COMMAND_RANGE(IDC_CHECK1, IDC_CHECK8, &CFolderMonitorDlg::OnCheckBox) END_MESSAGE_MAP()

核心代码(FolderMonitorDlg.h):
Other
////////////////////////////////////////////////////////////////////////////////////// //BOOL CFolderMonitorDlg::OnInitDialog() //{ //  ResetComponents(); //} void CFolderMonitorDlg::ResetComponents(void) {     while (m_resultlist.DeleteColumn(0)); // 删除原有表列     m_resultlist.InsertColumn(0, _T("路径"), 0, 200); // 添加表列     m_resultlist.InsertColumn(1, _T("操作"), 0, 80);     m_resultlist.InsertColumn(2, _T("时间"), 0, 80);     m_resultlist.ModifyStyle(LVS_TYPEMASK, LVS_REPORT); // 设置为列表式     DWORD exstyle = m_resultlist.GetExtendedStyle(); // 设置为全行选择、有格线     m_resultlist.SetExtendedStyle(exstyle|LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES);     m_flag_filename.SetCheck(true); // 设置默认监控项目     m_flag_creation.SetCheck(true);     m_flag_lastwrite.SetCheck(true);     OnCheckBox(IDC_CHECK1); // 更新m_monflag } void CFolderMonitorDlg::OnTvnSelchangedMfcshelltree1(NMHDR *pNMHDR, LRESULT *pResult) {     LPNMTREEVIEW pNMTreeView = reinterpret_cast<lpnmtreeview>(pNMHDR);     // TODO: 在此添加控件通知处理程序代码     *pResult = 0;     CString path;     m_foldertree.GetItemPath(path);     m_folderedit.SetWindowText(path); // 根据文件夹树所选项目,更新文本框 } UINT MonitorThread(LPVOID pvoid); void CFolderMonitorDlg::OnBnClickedBtnStart() {     // TODO: 在此添加控件通知处理程序代码     CString btntext;     m_startbutton.GetWindowText(btntext);     if (btntext == _T("开始"))     {         CString path;         m_folderedit.GetWindowText(path); // 获取文本框中的字符串         m_dirhandle = ::CreateFile( // 打开所选目录!             path, // 目录名             FILE_LIST_DIRECTORY, // 以列出目录权限打开             FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, // 允许其它程序读写删除             NULL, // 没有安全选项             OPEN_EXISTING, // 存在时打开             FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OVERLAPPED, // 备份存取异步存取             NULL); // 没有文件模板         if (m_dirhandle == INVALID_HANDLE_VALUE) // 如果打开失败则退出             return;         m_monthread = new CWinThread(MonitorThread, this); // 新建并启动工作线程         m_monthread->CreateThread();         m_startbutton.SetWindowText(_T("停止"));     }     else if (btntext == _T("停止"))     {         OnDestroy(); // 停止工作线程并清理资源         m_startbutton.SetWindowText(_T("清除"));     }     else if (btntext == _T("清除"))     {         m_resultlist.DeleteAllItems(); // 清空列表框         m_startbutton.SetWindowText(_T("开始"));     }     else     {         AfxMessageBox(_T("出现了不该出现的情况"));         m_startbutton.SetWindowText(_T("停止"));     } } void CFolderMonitorDlg::OnDestroy() {     if (m_monthread != NULL) // 如果线程存在,则终止线程     {         if (m_monthread->m_hThread != INVALID_HANDLE_VALUE)             m_monthread->SuspendThread();         delete m_monthread;         m_monthread = NULL;     }     if (m_dirhandle != INVALID_HANDLE_VALUE) // 如果目录已打开,则关闭目录     {         CloseHandle(m_dirhandle);         m_dirhandle = INVALID_HANDLE_VALUE;     } } //BEGIN_MESSAGE_MAP(CFolderMonitorDlg, CDialogEx) //  ON_COMMAND_RANGE(IDC_CHECK1, IDC_CHECK8, &CFolderMonitorDlg::OnCheckBox) //END_MESSAGE_MAP() void CFolderMonitorDlg::OnCheckBox(UINT nID) {     m_mutex.Lock(); // 赋值时可能会被工作线程异步读取,所以要上锁     m_monflag = 0;     if (m_flag_attr.GetCheck()) m_monflag |= FILE_NOTIFY_CHANGE_ATTRIBUTES;     if (m_flag_creation.GetCheck()) m_monflag |= FILE_NOTIFY_CHANGE_CREATION;     if (m_flag_dirname.GetCheck()) m_monflag |= FILE_NOTIFY_CHANGE_DIR_NAME;     if (m_flag_filename.GetCheck()) m_monflag |= FILE_NOTIFY_CHANGE_FILE_NAME;     if (m_flag_lastaccess.GetCheck()) m_monflag |= FILE_NOTIFY_CHANGE_LAST_ACCESS;     if (m_flag_lastwrite.GetCheck()) m_monflag |= FILE_NOTIFY_CHANGE_LAST_WRITE;     if (m_flag_security.GetCheck()) m_monflag |= FILE_NOTIFY_CHANGE_SECURITY;     if (m_flag_size.GetCheck()) m_monflag |= FILE_NOTIFY_CHANGE_SIZE;     m_mutex.Unlock(); } UINT MonitorThread(LPVOID pvoid) {     CFolderMonitorDlg *pWnd = (CFolderMonitorDlg*)pvoid;     char notify[65536]; // 储存FILE_NOTIFY_INFORMATION链表的缓冲区     DWORD dwBytes; // 返回字节数     FILE_NOTIFY_INFORMATION *pnotify; // FILE_NOTIFY_INFORMATION指针     if (pWnd->m_dirhandle == INVALID_HANDLE_VALUE) // 如果文件夹没打开则直接结束         return 1;     while(true) // 监控循环     {         memset(notify, 0, sizeof(notify)); // 清零缓冲区         pnotify = (FILE_NOTIFY_INFORMATION*)notify; // 指向缓冲区开头的链表第一项         pWnd->m_mutex.Lock(); // 因为可能会被主线程异步赋值,所以要进行锁定         DWORD monflag = pWnd->m_monflag; // 获取flag(见OnCheckBox)         pWnd->m_mutex.Unlock();         if (::ReadDirectoryChangesW(             pWnd->m_dirhandle, // 目录句柄(见OnBnClickedBtnStart)             ¬ify, sizeof(notify), // 缓冲区及大小             TRUE, // 是否监控子项目             monflag, // 监控的内容(见OnCheckBox)             &dwBytes, // 返回字节数             NULL, NULL)) // 异步调用相关,这里是同步调用,因此全填NULL         {             if (!dwBytes) // 如果缓冲区溢出             {                 pWnd->m_getpath = _T("[缓冲区溢出]");                 pWnd->m_getopr = _T("未知");                 SYSTEMTIME st;                 GetLocalTime(&st);                 pWnd->m_gettime = CTime(st).Format("%H:%M:%S"); // 获取系统时间                 pWnd->SendMessage(WM_TRHEADMSG, 0, 0); // 通知主线程添加列表项             }             else                 while(true) // 链表迭代循环                 {                     LPWSTR wbuf = pWnd->m_getpath.GetBufferSetLength(pnotify->FileNameLength / 2);                     memcpy(wbuf, pnotify->FileName, pnotify->FileNameLength);                     pWnd->m_getpath.ReleaseBuffer(pnotify->FileNameLength / 2); // 获取文件名                     pWnd->m_getopr = // 获取操作类型                         pnotify->Action == FILE_ACTION_ADDED ? _T("新建") :                         pnotify->Action == FILE_ACTION_MODIFIED ? _T("更改") :                         pnotify->Action == FILE_ACTION_REMOVED ? _T("删除") :                         pnotify->Action == FILE_ACTION_RENAMED_OLD_NAME ? _T("重命名") :                         pnotify->Action == FILE_ACTION_RENAMED_NEW_NAME ? _T("新名称") :                         _T("未知");                     SYSTEMTIME st;                     GetLocalTime(&st);                     pWnd->m_gettime = CTime(st).Format("%H:%M:%S"); // 获取系统时间                     pWnd->SendMessage(WM_TRHEADMSG, 0, 0); // 通知主线程添加列表项                     if (pnotify->NextEntryOffset) // 如果返回了另一个项目(通常是新文件名)                     {                         pnotify = (FILE_NOTIFY_INFORMATION *)                             ((char*)pnotify + pnotify->NextEntryOffset); // 迭代链表下一项                         continue; // 继续                     }                     break; // 退出                 }         }     } } //#define WM_THREADMSG (WM_USER + 8) //BEGIN_MESSAGE_MAP(CFolderMonitorDlg, CDialogEx) //  ON_MESSAGE(WM_TRHEADMSG, &CFolderMonitorDlg::ThreadMsg) //END_MESSAGE_MAP() LRESULT CFolderMonitorDlg::ThreadMsg(WPARAM wParam, LPARAM lParam) {     // 工作线程调用此消息以插入列表项     m_resultlist.InsertItem(0, m_getpath); // 根据返回的字符串插入表项,设置其它列的值     m_resultlist.SetItemText(0, 1, m_getopr);     m_resultlist.SetItemText(0, 2, m_gettime);     return TRUE; }</lpnmtreeview>

(完)
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
acmilan作者
10年2个月前 IP:四川
779030
这个ReadDirectoryChangesW的蛋疼之处在于:
1.返回一个链表缓冲区,而不是直接返回一个结构
2.如果缓冲区不够大,那么lpReturnedBytes为0,并且缓冲区里什么也没有
3.pnotify->FileName字符串只能是Unicode字符串wchar_t,并且不以L'\0'结尾

4.pnotify->FileNameLength返回字节数而不是字符数

5.文件被更改时会收到重复的通知,在程序中应用时需要做延时处理
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论

想参与大家的讨论?现在就 登录 或者 注册

所属专业
上级专业
同级专业
acmilan
进士 学者 笔友
文章
461
回复
2934
学术分
4
2009/05/30注册,6年7个月前活动
暂无简介
主体类型:个人
所属领域:无
认证方式:邮箱
IP归属地:未同步
插入公式
评论控制
加载中...
文号:{{pid}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

笔记
{{note.content}}
{{n.user.username}}
{{fromNow(n.toc)}} {{n.status === noteStatus.disabled ? "已屏蔽" : ""}} {{n.status === noteStatus.unknown ? "正在审核" : ""}} {{n.status === noteStatus.deleted ? '已删除' : ''}}
  • 编辑
  • 删除
  • {{n.status === 'disabled' ? "解除屏蔽" : "屏蔽" }}
我也是有底线的