我用ReadDirectoryChangesW 检测目录中的文件改变,遇到一个问题,在我的机器上修改一个文件检测到的是FILE_ACTION_MODIFIED,但在同事的机器上修改一个文件检测到的却是FILE_ACTION_REMOVED和FILE_ACTION_ADDED,这让我很纳闷啊.
还有,据我实验(采取同步的方式),检测到的事件都是在完成之前就给通知信号了,如果我要等到比如添加一个文件完成之后再接到通知信号,以方便程序处理,那该如何是好啊

解决方案 »

  1.   

    第一个问题可能和修改文件的应用程序有关吧,人家保存的时候,有时候是修改,有时候是删了新建一个,控制不了的,不行就两个Action都监视好了。第二个问题,等待文件操作结束我当年是用了一个打开文件操作,什么时候成功了,再继续我的程序。// 等到文件复制完毕
    CFile TestWrite;
    BOOL bOpen;
    do {
    bOpen = TestWrite.Open(strFileName,CFile::modeRead);
    } while(!bOpen);
    TestWrite.Close();
      

  2.   

    我使用过这个,文件修改时会得到两次事件吧:第一次是刚修改时,第二次是修改完成时。
    有个开源的程序中使用到了这个,请参考:sumatrapdf(一个开源的PDF阅读器)。
      

  3.   

    通过 未公开API SHChangeNotifyRegister 实现 ================================================================= 
    Windows 内部有两个未公开的函数(注:在最新的MSDN中,已经公开了这两个函数),分别叫做SHChangeNotifyRegister和 SHChangeNotifyDeregister,可以实现以上的功能。这两个函数位于Shell32.dll中,是用序号方式导出的。这就是为什么我们用VC自带的Depends工具察看Shell32.dll时,找不到这两个函数的原因。SHChangeNotifyRegister的导出序号是 2;而SHChangeNotifyDeregister的导出序号是4。 
    SHChangeNotifyRegister可以把指定的窗口添加到系统的消息监视链中,这样窗口就能接收到来自文件系统或者Shell的通知了。而对应的另一个函数,SHChangeNotifyDeregister,则用来取消监视钩挂。SHChangeNotifyRegister的原型和相关参数如下: 
    ULONG SHChangeNotifyRegister 
    (        
    HWND hwnd, 
        int  fSources, 
        LONG fEvents, 
        UINT    wMsg, 
        Int cEntries, 
        SHChangeNotifyEntry *pfsne 
    ); 
    其中: 
    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。 
    ================================================================= 通过 FindFirstChangeNotification 实现 ================================================================= 
    FindFirstChangeNotification函数创建一个更改通知句柄并设置初始更改通知过滤条件. 
    当一个在指定目录或子目录下发生的更改符合过滤条件时,等待通知句柄则成功。 
    该函数原型为: 
    HANDLE FindFirstChangeNotification( 
    LPCTSTR lpPathName, //目录名 
    BOOL bWatchSubtree, // 监视选项 
    DWORD dwNotifyFilter // 过滤条件 
    ); 当下列情况之一发生时,WaitForMultipleObjects函数返回 
    1.一个或者全部指定的对象在信号状态(signaled state) 
    2.到达超时间隔 
    例程如下: 
    DWORD dwWaitStatus; 
    HANDLE dwChangeHandles[2]; //监视C:\Windows目录下的文件创建和删除 dwChangeHandles[0] = FindFirstChangeNotification( 
    "C:\\WINDOWS", // directory to watch 
    FALSE, // do not watch the subtree 
    FILE_NOTIFY_CHANGE_FILE_NAME); // watch file name changes if (dwChangeHandles[0] == INVALID_HANDLE_VALUE) 
    ExitProcess(GetLastError()); //监视C:\下子目录树的文件创建和删除 dwChangeHandles[1] = FindFirstChangeNotification( 
    "C:\\", // directory to watch 
    TRUE, // watch the subtree 
    FILE_NOTIFY_CHANGE_DIR_NAME); // watch dir. name changes if (dwChangeHandles[1] == INVALID_HANDLE_VALUE) 
    ExitProcess(GetLastError()); // Change notification is set. Now wait on both notification 
    // handles and refresh accordingly. while (TRUE) 
    { // Wait for notification. dwWaitStatus = WaitForMultipleObjects(2, dwChangeHandles,FALSE, INFINITE); switch (dwWaitStatus) 

    case WAIT_OBJECT_0: //在C:\WINDOWS目录中创建或删除文件 。 
    //刷新该目录及重启更改通知(change notification). AfxMessageBox("RefreshDirectory"); 
    if ( FindNextChangeNotification(dwChangeHandles[0]) == FALSE ) 
    ExitProcess(GetLastError()); 
    break; case WAIT_OBJECT_0 1: 
    //在C:\WINDOWS目录中创建或删除文件 。 
    //刷新该目录树及重启更改通知(change notification). AfxMessageBox("RefreshTree"); 
    if (FindNextChangeNotification(dwChangeHandles[1]) == FALSE) 
    ExitProcess(GetLastError()); 
    break; default: 
    ExitProcess(GetLastError()); 


    ================================================================= 通过 ReadDirectoryChangesW 实现 ================================================================= bool Monitor() 

        
        
        HANDLE hFile  =  CreateFile( 
            "c:\\", 
            GENERIC_READ|GENERIC_WRITE, 
            FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, 
            NULL, 
            OPEN_EXISTING, 
            FILE_FLAG_BACKUP_SEMANTICS, 
            NULL 
            ); 
        if(  INVALID_HANDLE_VALUE  ==  hFile  )  return  false; 
        
        char  buf[  2*(sizeof(FILE_NOTIFY_INFORMATION)+MAX_PATH)  ]; 
        FILE_NOTIFY_INFORMATION*  pNotify=(FILE_NOTIFY_INFORMATION  *)buf; 
        DWORD  BytesReturned; 
        while(true) 
        { 
            if(  ReadDirectoryChangesW(  hFile, 
                pNotify, 
                sizeof(buf), 
                true, 
                FILE_NOTIFY_CHANGE_FILE_NAME| 
                FILE_NOTIFY_CHANGE_DIR_NAME| 
                FILE_NOTIFY_CHANGE_ATTRIBUTES| 
                FILE_NOTIFY_CHANGE_SIZE| 
                FILE_NOTIFY_CHANGE_LAST_WRITE| 
                FILE_NOTIFY_CHANGE_LAST_ACCESS| 
                FILE_NOTIFY_CHANGE_CREATION| 
                FILE_NOTIFY_CHANGE_SECURITY, 
                &BytesReturned, 
                NULL, 
                NULL  )  ) 
            { 
                char  tmp[MAX_PATH],  str1[MAX_PATH],  str2[MAX_PATH]; 
                memset(  tmp,  0,  sizeof(tmp)  ); 
                WideCharToMultiByte(  CP_ACP,0,pNotify->FileName,pNotify->FileNameLength/2,tmp,99,NULL,NULL  ); 
                strcpy(  str1,  tmp  ); 
                
                if(  0  !=  pNotify->NextEntryOffset  ) 
                { 
                    PFILE_NOTIFY_INFORMATION  p  =  (PFILE_NOTIFY_INFORMATION)((char*)pNotify+pNotify->NextEntryOffset); 
                    memset(  tmp,  0,  sizeof(tmp)  ); 
                    WideCharToMultiByte(  CP_ACP,0,p->FileName,p->FileNameLength/2,tmp,99,NULL,NULL  ); 
                    strcpy(  str2,  tmp  ); 
                } 
                
                // your process 
            } 
            else 
            { 
                break; 
            } 
        }     return true; 
    }