监控文件变化的三种方法

=================================================================

通过 未公开API SHChangeNotifyRegister 实现

=================================================================


一、原理

Windows 内部有两个未公开的函数(注:在最新的MSDN中,已经公开了这两个函数),分别叫做SHChangeNotifyRegister和 SHChangeNotifyDeregister,可以实现以上的功能。这两个函数位于Shell32.dll中,是用序号方式导出的。这就是为什么我们用VC自带的Depends工具察看Shell32.dll时,找不到这两个函数的原因。SHChangeNotifyRegister的导出序号是 2;而SHChangeNotifyDeregister的导出序号是4。
SHChangeNotifyRegister可以把指定的窗口添加到系统的消息监视链中,这样窗口就能接收到来自文件系统或者Shell的通知了。而对应的另一个函数,SHChangeNotifyDeregister,则用来取消监视钩挂。SHChangeNotifyRegister的原型和相关参数如下:
  1. ULONG SHChangeNotifyRegister  
  2. (          
  3.     HWND hwnd,  
  4.     int   fSources,  
  5.     LONG fEvents,  
  6.     UINT    wMsg,  
  7.     Int cEntries,  
  8.     SHChangeNotifyEntry *pfsne  
  9. );  
其中:
hwnd
将要接收改变或通知消息的窗口的句柄。
fSource
指示接收消息的事件类型,将是下列值的一个或多个(注:这些标志没有被包括在任何头文件中,使用者须在自己的程序中加以定义或者直接使用其对应的数值)
SHCNRF_InterruptLevel
0x0001。接收来自文件系统的中断级别通知消息。
SHCNRF_ShellLevel
0x0002。接收来自Shell的Shell级别通知消息。
SHCNRF_RecursiveInterrupt
0x1000。接收目录下所有子目录的中断事件。此标志必须和SHCNRF_InterruptLevel 标志合在一起使用。当使用该标志时,必须同时设置对应的SHChangeNotifyEntry结构体中的fRecursive成员为TRUE(此结构体由函数的最后一个参数pfsne指向),这样通知消息在目录树上是递归的。
SHCNRF_NewDelivery
0x8000。接收到的消息使用共享内存。必须先调用SHChangeNotification_Lock,然后才能存取实际的数据,完成后调用SHChangeNotification_Unlock函数释放内存。
fEvents
要捕捉的事件,其所有可能的值请参见MSDN中关于SHChangeNotify函数的注解。
wMsg
产生对应的事件后,发往窗口的消息。
cEntries
pfsne指向的数组的成员的个数。
pfsne
SHChangeNotifyEntry 结构体数组的起始指针。此结构体承载通知消息,其成员个数必须设置成1,否则SHChangeNotifyRegister或者 SHChangeNotifyDeregister将不能正常工作(但是据我试验,如果cEntries设为大于1的值,依然可以注册成功,不知何故)。
如果函数调用成功,则返回一个整型注册标志号,否则将返回0。同时系统就会将hwnd指定的窗口加入到操作监视链中,当有文件操作发生时,系统会向hwnd标识的窗口发送wMsg指定的消息,我们只要在程序中加入对该消息的处理函数就可以实现对系统操作的监视了。
如果要退出程序监视,就要调用另外一个未公开得函数SHChangeNotifyDeregister来取消程序监视。该函数的原型如下:
BOOL SHChangeNotifyDeregister(ULONG ulID);

其中ulID指定了要注销的监视注册标志号,如果卸载成功,返回TRUE,否则返回FALSE。


二、实例


在使用这两个函数之前,必须要先声明它们的原型,同时还要添加一些宏和结构定义。我们在原工程中添加一个ShellDef.h头文件,然后加入如下声明:
  1. #define SHCNRF_InterruptLevel 0x0001 //Interrupt level notifications from the file system  
  2. #define SHCNRF_ShellLevel   0x0002 //Shell-level notifications from the shell  
  3. #define SHCNRF_RecursiveInterrupt 0x1000 //Interrupt events on the whole subtree  
  4. #define SHCNRF_NewDelivery   0x8000 //Messages received use shared memory  
  5.   
  6. typedef struct  
  7. {  
  8.     LPCITEMIDLIST pidl; //Pointer to an item identifier list (PIDL) for which to receive notifications  
  9.     BOOL fRecursive; //Flag indicating whether to post notifications for children of this PIDL  
  10. }SHChangeNotifyEntry;  
  11.   
  12. typedef struct  
  13. {  
  14.     DWORD dwItem1; // dwItem1 contains the previous PIDL or name of the folder.  
  15.     DWORD dwItem2; // dwItem2 contains the new PIDL or name of the folder.  
  16. }SHNotifyInfo;  
  17.   
  18. typedef ULONG (WINAPI* pfnSHChangeNotifyRegister)  
  19. (  
  20.   HWND hWnd,  
  21.   int fSource,  
  22.   LONG fEvents,  
  23.   UINT wMsg,  
  24.   int cEntries,  
  25.   SHChangeNotifyEntry* pfsne  
  26. );  
  27.   
  28. typedef BOOL (WINAPI* pfnSHChangeNotifyDeregister)(ULONG ulID);  
这些宏和函数的声明,以及参数含义,如前所述。下面我们要在CListCtrlEx体内添加两个函数指针和一个ULONG型的成员变量,以保存函数地址和返回的注册号。
接下来实现一个函数Initialize,在其中,我们首先进行加载Shell32.dll以及初始化函数指针动作,接着调用注册函数向Shell注册。
  1. BOOL Initialize()  
  2. {  
  3.   …………  
  4.   //加载Shell32.dll  
  5.   m_hShell32 = LoadLibrary("Shell32.dll");  
  6.   if(m_hShell32 == NULL)  
  7.   {  
  8.     return FALSE;  
  9.   }  
  10.   
  11.   //取函数地址  
  12.   m_pfnDeregister = NULL;  
  13.   m_pfnRegister = NULL;  
  14.   m_pfnRegister = (pfnSHChangeNotifyRegister)GetProcAddress(m_hShell32,MAKEINTRESOURCE(2));  
  15.   m_pfnDeregister = (pfnSHChangeNotifyDeregister)GetProcAddress(m_hShell32,MAKEINTRESOURCE(4));  
  16.   if(m_pfnRegister==NULL || m_pfnDeregister==NULL)  
  17.   {  
  18.     return FALSE;  
  19.   }  
  20.   
  21.   SHChangeNotifyEntry shEntry = {0};  
  22.   shEntry.fRecursive = TRUE;  
  23.   shEntry.pidl = 0;  
  24.   m_ulNotifyId = 0;  
  25.   
  26.   //注册Shell监视函数  
  27.   m_ulNotifyId = m_pfnRegister(  
  28.         GetSafeHwnd(),  
  29.         SHCNRF_InterruptLevel|SHCNRF_ShellLevel,  
  30.         SHCNE_ALLEVENTS,  
  31.         WM_USERDEF_FILECHANGED, //自定义消息  
  32.         1,  
  33.         &shEntry  
  34.        );  
  35.   if(m_ulNotifyId == 0)  
  36.   {  
  37.     MessageBox("Register failed!","ERROR",MB_OK|MB_ICONERROR);  
  38.     return FALSE;  
  39.   }  
  40.   return TRUE;  
  41. }  

=================================================================

通过 FindFirstChangeNotification 实现

=================================================================


FindFirstChangeNotification函数创建一个更改通知句柄并设置初始更改通知过滤条件。
当一个在指定目录或子目录下发生的更改符合过滤条件时,等待通知句柄则成功。
该函数原型为:
  1. HANDLE FindFirstChangeNotification(  
  2. LPCTSTR lpPathName, //目录名  
  3. BOOL bWatchSubtree, // 监视选项  
  4. DWORD dwNotifyFilter // 过滤条件  
  5. );  
当下列情况之一发生时,WaitForMultipleObjects函数返回
1.一个或者全部指定的对象在信号状态(signaled state)
2.到达超时间隔

例程如下:
  1. DWORD dwWaitStatus;  
  2. HANDLE dwChangeHandles[2];  
  3.   
  4. //监视C:\Windows目录下的文件创建和删除  
  5.   
  6. dwChangeHandles[0] = FindFirstChangeNotification(  
  7. "C:\\WINDOWS"// directory to watch  
  8. FALSE, // do not watch the subtree  
  9. FILE_NOTIFY_CHANGE_FILE_NAME); // watch file name changes  
  10.   
  11. if (dwChangeHandles[0] == INVALID_HANDLE_VALUE)  
  12. ExitProcess(GetLastError());  
  13.   
  14. //监视C:\下子目录树的文件创建和删除  
  15.   
  16. dwChangeHandles[1] = FindFirstChangeNotification(  
  17. "C:\\"// directory to watch  
  18. TRUE, // watch the subtree  
  19. FILE_NOTIFY_CHANGE_DIR_NAME); // watch dir. name changes  
  20.   
  21. if (dwChangeHandles[1] == INVALID_HANDLE_VALUE)  
  22.   ExitProcess(GetLastError());  
  23.   
  24. // Change notification is set. Now wait on both notification  
  25. // handles and refresh accordingly.  
  26.   
  27. while (TRUE)  
  28. {  
  29.   
  30.   // Wait for notification.  
  31.   
  32.   dwWaitStatus = WaitForMultipleObjects(2, dwChangeHandles,FALSE, INFINITE);  
  33.   
  34.   switch (dwWaitStatus)  
  35.   {  
  36.    case WAIT_OBJECT_0:  
  37.   
  38.     //在C:\WINDOWS目录中创建或删除文件 。  
  39.    //刷新该目录及重启更改通知(change notification).  
  40.   
  41.    AfxMessageBox("RefreshDirectory");  
  42.    if ( FindNextChangeNotification(dwChangeHandles[0]) == FALSE )  
  43.    ExitProcess(GetLastError());  
  44.    break;  
  45.   
  46.   case WAIT_OBJECT_0 1:  
  47.    //在C:\WINDOWS目录中创建或删除文件 。  
  48.    //刷新该目录树及重启更改通知(change notification).  
  49.   
  50.    AfxMessageBox("RefreshTree");  
  51.    if (FindNextChangeNotification(dwChangeHandles[1]) == FALSE)  
  52.    ExitProcess(GetLastError());  
  53.    break;  
  54.   
  55.   default:  
  56.    ExitProcess(GetLastError());  
  57.   }  
  58. }  

=================================================================

通过 ReadDirectoryChangesW 实现

=================================================================


  1. bool Monitor()  
  2. {  
  3.      
  4.      
  5.     HANDLE hFile   =   CreateFile(  
  6.         "c:\\",  
  7.         GENERIC_READ|GENERIC_WRITE,  
  8.         FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,  
  9.         NULL,  
  10.         OPEN_EXISTING,  
  11.         FILE_FLAG_BACKUP_SEMANTICS,  
  12.         NULL  
  13.         );  
  14.     if(   INVALID_HANDLE_VALUE   ==   hFile   )   return   false;  
  15.      
  16.     char   buf[   2*(sizeof(FILE_NOTIFY_INFORMATION)+MAX_PATH)   ];  
  17.     FILE_NOTIFY_INFORMATION*   pNotify=(FILE_NOTIFY_INFORMATION   *)buf;  
  18.     DWORD   BytesReturned;  
  19.     while(true)  
  20.     {  
  21.         if(   ReadDirectoryChangesW(   hFile,  
  22.             pNotify,  
  23.             sizeof(buf),  
  24.             true,  
  25.             FILE_NOTIFY_CHANGE_FILE_NAME|  
  26.             FILE_NOTIFY_CHANGE_DIR_NAME|  
  27.             FILE_NOTIFY_CHANGE_ATTRIBUTES|  
  28.             FILE_NOTIFY_CHANGE_SIZE|  
  29.             FILE_NOTIFY_CHANGE_LAST_WRITE|  
  30.             FILE_NOTIFY_CHANGE_LAST_ACCESS|  
  31.             FILE_NOTIFY_CHANGE_CREATION|  
  32.             FILE_NOTIFY_CHANGE_SECURITY,  
  33.             &BytesReturned,  
  34.             NULL,  
  35.             NULL   )   )  
  36.         {  
  37.             char   tmp[MAX_PATH],   str1[MAX_PATH],   str2[MAX_PATH];  
  38.             memset(   tmp,   0,   sizeof(tmp)   );  
  39.             WideCharToMultiByte(   CP_ACP,0,pNotify->FileName,pNotify->FileNameLength/2,tmp,99,NULL,NULL   );  
  40.             strcpy(   str1,   tmp   );  
  41.              
  42.             if(   0   !=   pNotify->NextEntryOffset   )  
  43.             {  
  44.                 PFILE_NOTIFY_INFORMATION   p   =   (PFILE_NOTIFY_INFORMATION)((char*)pNotify+pNotify->NextEntryOffset);  
  45.                 memset(   tmp,   0,   sizeof(tmp)   );  
  46.                 WideCharToMultiByte(   CP_ACP,0,p->FileName,p->FileNameLength/2,tmp,99,NULL,NULL   );  
  47.                 strcpy(   str2,   tmp   );  
  48.             }  
  49.              
  50.             // your process  
  51.         }  
  52.         else  
  53.         {  
  54.             break;  
  55.         }  
  56.     }  
  57.   
  58.     return true;  
  59. }  
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。
### 回答1: Linux实时监控文件变化可以使用inotify工具。inotify是Linux内核提供的一种文件系统监控机制,可以监控文件或目录的变化,包括文件的创建、删除、修改、移动等操作。使用inotify可以实时监控文件变化,并在文件发生变化时进行相应的处理。可以通过编写脚本或使用现成的工具来实现对文件变化监控。 ### 回答2: Linux是一种非常常见且广泛使用的操作系统,但如果你需要实时监控文件变化,该怎么做呢?以下是一些常见的方法和工具: 1. inotify(内核提供的文件系统监控机制):inotify可以监视文件系统事件,如打开、关闭、创建和删除文件,这使得它成为一种非常流行的文件系统监控工具。通过使用inotify工具,您可以设置一个监控事件,并在文件被修改时立即接收通知。 2. auditd(Linux内置的审计机制):auditd是Linux内置的一个安全审计工具,它可以记录系统和应用程序的操作记录,并生成安全审计日志。通过配置auditd,您可以设置检测文件变化的规则,并在文件被修改时立即接收警报。 3. incron(inotify机制的增强版):incron可以使用inotify机制来监控文件变化,并在发生指定事件时执行相应操作。与inotify不同的是,incron可以执行更复杂的任务,如运行脚本、备份文件等,这使得它成为一种非常灵活和功能强大的文件监控工具。 4. lsof(列出打开文件):lsof可以列出当前打开的文件和进程,并显示文件的详细信息,如文件描述符、文件类型、文件大小等。通过使用lsof,您可以实时查看文件是否被打开、修改或关闭,这可以帮助您更好地监控文件变化。 总之,无论您是需要监控本地文件系统的变化,还是需要远程监控系统的文件变化,都可以使用上述工具和方法来实现实时监控文件变化。这样可以帮助您更好地掌握文件的状态和行为,从而更好地保护您的系统和数据。 ### 回答3: Linux系统中,可以使用inotify机制实现对文件的实时监控。 inotify是Linux系统中一种文件事件通知机制,可以监控文件系统中指定目录或文件变化情况,并及时通知相关程序,实现文件实时监控功能。通过inotify可以监控文件系统中的各种事件,包括文件的创建、修改、删除、移动等操作。 使用inotify需要使用头文件<sys/inotify.h>,并调用inotify_init()函数来进行初始化。可以使用inotify_add_watch()函数来添加需要监控文件或目录,并指定需要监控的事件类型。当文件发生指定的事件时,inotify机制会生成相应的事件通知,并通过一个文件描述符返回给用户程序,以便进一步处理。 例如,以下代码可以实现监控当前目录下所有文件的修改事件: ``` #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <sys/types.h> #include <sys/inotify.h> #define MAX_EVENTS 1024 #define EVENT_SIZE (sizeof (struct inotify_event)) #define EVENT_BUF_LEN (MAX_EVENTS * (EVENT_SIZE + 16)) int main() { int length, i = 0; int fd; int wd; char buffer[EVENT_BUF_LEN]; fd = inotify_init(); if (fd < 0) { perror("inotify_init"); } wd = inotify_add_watch(fd, ".", IN_MODIFY); while (1) { i = 0; length = read(fd, buffer, EVENT_BUF_LEN); if (length < 0) { perror("read"); } while (i < length) { struct inotify_event *event = (struct inotify_event *) &buffer[i]; if (event->len) { if (event->mask & IN_MODIFY) { printf("The file %s was modified.\n", event->name); } } i += EVENT_SIZE + event->len; } } (void) inotify_rm_watch(fd, wd); (void) close(fd); return 0; } ``` 该代码使用了inotify机制,监控当前目录下的所有文件,当文件被修改时,程序会输出相应的提示信息。在代码中,inotify_add_watch()函数用于添加需要监控的目录或文件,通过设置第三个参数可以指定需要监控的事件类型。while循环中的read()函数用于读取inotify机制返回的事件通知信息,程序根据读取到的信息进行相应的处理。 在Linux系统中,inotify机制可实现对文件的实时监控和处理,可用于很多实际应用场景,如动态监控文件变化、自动同步文件等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值