龙仪的家

导航

<2006年5月>
30123456
78910111213
14151617181920
21222324252627
28293031123
45678910

随笔分类

文章分类

收藏夹

随笔档案

文章档案

统计

我的常用网址

代码库之十三 - 目录监控类(0.2 060524)

项目要用到目录监控的功能,但是下载了几个代码都或多或少有丢失文件名的情况,所以研究了一下,希望能给大家节省些时间。
总结如下
1,调试过程中发现在处理ReadDirectoryChangesW的时候,如果后续处理耗时少,那么能够得到大部分甚至全部的内容后续处理耗时的话就会丢失,猜想可能windows本身提供了一个很小的缓冲,如果不持续调用ReadDirectoryChangesW的话,宝贵的数据就会被系统无情的抛弃,这就是所谓丢失现象,
2,根据typedef struct _FILE_NOTIFY_INFORMATION {
    DWORD NextEntryOffset;
    DWORD Action;
    DWORD FileNameLength;
    WCHAR FileName[1];
} FILE_NOTIFY_INFORMATION, *PFILE_NOTIFY_INFORMATION;
我们可以知道,这显然是个类链表的结构也就是这里有可能存储一系列的结果(如果一次只存一个结果显然系统很难设计),那么我们就要根据NextEntryOffset偏移将所有的数据都得到,这样才能防止数据丢失

3,   ReadDirectoryChangesW(//这里的BUFSIZE不要申请的太少,否则会因为系统堆积缓冲太多而不够用
    m_hDir,
    m_pBuf,
    BUFSIZE,
    TRUE,
    FILE_NOTIFY_CHANGE_FILE_NAME |
    FILE_NOTIFY_CHANGE_LAST_WRITE |
    FILE_NOTIFY_CHANGE_CREATION |
    FILE_NOTIFY_CHANGE_SIZE,
    &dwBytesReturned,
    NULL,
    NULL);
根据上述发现,特设计了一个双线程结构,其中用到了代码库中的类,lg_LoopBuffer在这里用并不合适,但用于测试,以及验证结果,暂时就将就用了。代码写的很粗糙,会根据反映逐步修改.
至于完成端口的方法我想原理应该和这个差不多,有兴趣的可以自己试一下.
另外如果对代码有什么想法欢迎讨论,大家的支持是我的动力!
------感谢hpho的精彩设计以下代码是根据他的设计实现的--------
# re: 代码库之十三 - 目录监控类(0.1 060524) 2006-05-24 13:38 hpho
"m_hDir需要在lg_DirMonitor中关掉,所以放在lg_DirMonitor里"
lg_DirMonitor就包含一个lg_DirWatch然而m_hDir除了在lg_DirMonitor里创建和消毁就没用了, 那就是说lg_DirMonitor没有维护这个句柄的责任!
m_hDir的Create()和CloseHandle都应该放在lg_DirWatch里, 当然可以这样写lg_DirWatch::Release(){CloseHandle(m_hDir);}或换一角度看吧,
lg_DirWatch的核心句柄需要lg_DirMonitor传入这意味着lg_DirWatch依赖lg_DirMonitor, 这样无论是单独类调试或分解这个类到其它地方使用都不会是好事.

Watch_tag的设计大概明白了但仍然觉得不太好, 因为它等同于auto_ptr差不多的东西, 这样的类有一定危险性.

class Watch_tag{
     HANDLE m_hDir;
     int size, offset;
     unsigned char* buff;
     FILE_NOTIFY_INFORMATION* fni;

public:
     Watch_tag(HANDLE h, int sz=BUFSIZE)
           :m_hDir(h), size(sz){
           offset=0;
           fni=-1;
           buff=new unsigned char[sz];
           memset(buff, ...);
     }

    ~Watch_tag(){
           delete[] buff;
    }

    int setDirInfo(){
          int ret=ReadDirectoryChanges(....);
          fni=(FILE_NOTIFY_INFORMATION*)buff;
          return ret;
    }

    operator bool(){
         return offset<0||offset>0;
    }

      FILE_NOTIFY_INFORMATION* operator->(){
           return fni;
      }

       void operator ++(){
          offset = fni->NextEntryOffset;
          fni=(PFILE_NOTIFY_INFORMATION)((uint8*)fni + offset);
       }
};

lg_Monitor::run(){
....
    for(int32 i = 0;i < count;i++){
     Watch_tag* pWatch = m_Queue.front();
     while(*pWatch){
          switch(pWatch->Action){...}
          pWatch++;
     }
    }
}

lg_DirWatch::run(){
    Watch_tag pWatch=new Watch_tag(m_hDir);
    if(pWatch->setDirInfo())
        m_pMonitor->SetData(pWatch);
    pWatch=NULL;
}
/////////////////////////H////////////////////////////
#ifndef LGLIB_DIRMONITOR_H
#define LGLIB_DIRMONITOR_H
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include <string>
#include <queue>
#include "Lg_Tread.h"
#include "lg_MultiEvent.h"
#include "lg_LoopBuffer.h"
using namespace std;
//using namespace lglib;
#define BYBUF 1024
#define BUFSIZE 10240
namespace lglib
{
 //缓冲块标志
 class Watch_tag
 {
  
 public:
  Watch_tag(HANDLE h, int sisz=BUFSIZE);
  ~Watch_tag();
  BOOL GetChangeInfo();
  BOOL IsEnd();
  BOOL IsInvoke();
  string GetCurrentString();
  uint32 GetAction();
  Watch_tag& operator++();
 private:
  HANDLE m_hDir;
  FILE_NOTIFY_INFORMATION* fni;
  
  char* m_pstr;
  int32 offset;
  uint32 m_len;
  uint32 m_Action;
  
  BOOL m_bIsInvoke;
  BOOL m_bSucceed;
 };
 class lg_DirMonitor;
 
 class lg_DirWatch : public lg_Thread
 {
 public:
  lg_DirWatch();
  virtual ~lg_DirWatch();
  BOOL Init(string path,lg_DirMonitor* pMo);
  void Release();
  void run();
 private:
  Watch_tag* m_pWatch;
  HANDLE m_hDir;
  string m_path;
  BOOL m_bIsInited;
  lg_DirMonitor* m_pMonitor;
 };
 
 class lg_DirMonitor : public lg_Thread
 {
 public:
  lg_DirMonitor(string strDirName);
  virtual ~lg_DirMonitor();
  BOOL Init();
  void run();
  void Save();
  void SetData(Watch_tag* p);
 private:
  typedef queue<Watch_tag*> QList;
  int CopyToBuffer(const string head,string content,const uint32 SerNum);
  enum Dir_Event
  {
   Exit = 0,
   ProcessData,
   EndPos
  };
  
  string m_strMoniDir;
  BOOL m_bIsInited;
  lg_DirWatch w1;
  lg_Mutex m_Mutex;
  QList m_Queue;
  lg_MultiEvent *m_MultiEvent;
  lg_LoopBuffer * m_LoopBuf;
 };
}
#endif //LGLIB_DIRMONITOR_H
///////////////////////////CPP/////////////////////////
#include "stdafx.h"
#include "DirMonitor.h"
#include "lg_Exception.h"
//使用VC6的时候注意:在winbase.h前面定义#define _WIN32_WINNT 0x0500
//否则无法通过编译
#include <winbase.h>
#include <io.h>
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
namespace lglib
{
 /************************************************************************/
 /*       Watch_tag实现                                                  */
 /************************************************************************/
 Watch_tag::Watch_tag(HANDLE hdl, int size)
  : m_hDir(hdl),m_len(size),offset(-1),fni(NULL),m_bIsInvoke(FALSE),m_Action(0),m_bSucceed(FALSE)
 {
  m_pstr = new char[size];
 }
 Watch_tag::~Watch_tag()
 {
  delete [] m_pstr;
 }
 BOOL Watch_tag::GetChangeInfo()
 {
  if(m_bIsInvoke)
   throw lg_Exception("Watch_tag::GetChangeInfo()","重复调用Watch_tag::GetChangeInfo()",0x00000001);
  
  m_bIsInvoke = TRUE;
  
  m_bSucceed = ReadDirectoryChangesW(
   m_hDir,
   m_pstr,
   BUFSIZE,
   TRUE,
   FILE_NOTIFY_CHANGE_FILE_NAME |
   FILE_NOTIFY_CHANGE_LAST_WRITE |
   FILE_NOTIFY_CHANGE_CREATION |
   FILE_NOTIFY_CHANGE_SIZE,
   &m_len,
   NULL,
   NULL);
  
  if(m_bSucceed){
   fni=(FILE_NOTIFY_INFORMATION*)m_pstr;
  }
  
  return m_bSucceed;
 }
 BOOL Watch_tag::IsEnd()
 {
  if(!m_bIsInvoke)
   throw lg_Exception("Watch_tag::IsEnd()","没有调用Watch_tag::GetChangeInfo()",0x00000002);
  if(!m_bSucceed)
   throw lg_Exception("Watch_tag::IsEnd()","调用Watch_tag::GetChangeInfo()失败",0x00000003);
  
  if(offset == -1)
   return FALSE;
  
  return offset<=0;
 }
 BOOL Watch_tag::IsInvoke()
 {
  return m_bIsInvoke;
 }
 string Watch_tag::GetCurrentString()
 {
  if(!m_bIsInvoke)
   throw lg_Exception("Watch_tag::GetCurrentString()","没有调用Watch_tag::GetChangeInfo()",0x00000004);
  if(!m_bSucceed)
   throw lg_Exception("Watch_tag::GetCurrentString()","调用Watch_tag::GetChangeInfo()失败",0x00000005);
  WCHAR wcFileName[BYBUF] = {0};
  memcpy( wcFileName, fni->FileName, fni->FileNameLength );
  char cFileName[BYBUF];
  WideCharToMultiByte( CP_ACP, 0, wcFileName, -1, cFileName, BYBUF, NULL, NULL );
  string temp = cFileName;
  return temp;
 }
 uint32 Watch_tag::GetAction()
 {
  if(!m_bIsInvoke)
   throw lg_Exception("Watch_tag::GetAction()","没有调用Watch_tag::GetChangeInfo()",0x00000006);
  if(!m_bSucceed)
   throw lg_Exception("Watch_tag::GetAction()","调用Watch_tag::GetChangeInfo()失败",0x00000007);
  return fni->Action;
 }
 Watch_tag& Watch_tag::operator ++()
 {
  if(!m_bIsInvoke)
   throw lg_Exception("Watch_tag::operator ++","没有调用Watch_tag::GetChangeInfo()",0x00000008);
  if(!m_bSucceed)
   throw lg_Exception("Watch_tag::operator ++","调用Watch_tag::GetChangeInfo()失败",0x00000009);
  
  offset = fni->NextEntryOffset;
  fni=(PFILE_NOTIFY_INFORMATION)((uint8*)fni + offset);
  return *this;
 }
 /************************************************************************/
 /*       lg_DirMonitor实现                                              */
 /************************************************************************/
 lg_DirMonitor::lg_DirMonitor(string strDirName)
 : m_strMoniDir(strDirName),m_bIsInited(FALSE),m_MultiEvent(NULL),m_LoopBuf(NULL)
 {
  lg_Thread::setThreadName("lg_DirMonitor");
 }
 lg_DirMonitor::~lg_DirMonitor()
 {
  w1.Release();
  
  if(m_MultiEvent)
   m_MultiEvent->set(Exit);
  
  if(m_bIsInited)
   quit();
  for(int i = 0;i < m_Queue.size();i++)
  {
   delete m_Queue.front();
  }
  m_Queue.empty();
  Sleep(1);
  if(m_MultiEvent)
  {
   delete m_MultiEvent;
   m_MultiEvent = NULL;
  }
  
  if(m_LoopBuf)
  {
   delete m_LoopBuf;
   m_LoopBuf = NULL;
  }
 }
 void lg_DirMonitor::Save()
 {
  CString Filename;
  Filename.Format("d:\\FileRec.txt");
  m_LoopBuf->Save(Filename.GetBuffer(0));
 }
 void lg_DirMonitor::SetData(Watch_tag* p)
 {
  m_Mutex.lock(__FILE__,__LINE__);
  m_Queue.push(p);
  m_Mutex.unlock(__FILE__,__LINE__);
  m_MultiEvent->set(ProcessData);
 }
 BOOL lg_DirMonitor::Init()
 {
  m_MultiEvent = new lg_MultiEvent(EndPos);
  m_LoopBuf = new lg_LoopBuffer;
  m_LoopBuf->InitBuffer(1024000);
  m_bIsInited = TRUE;
  start();
  w1.Init(m_strMoniDir,this);
  return TRUE;
 }
 int lg_DirMonitor::CopyToBuffer(const string head,string content,const uint32 SerNum)
 {
  char Output[1024];
  sprintf(Output,"%s<%s><%ld>\r\n",head.c_str(),content.c_str(),SerNum);
  return m_LoopBuf->WriteBuffer(reinterpret_cast<uint8*>(Output),strlen(Output));
 }
 void lg_DirMonitor::run()
 {
  uint32 dwFileModify = 0;
  uint32 dwFileAdd = 0;
  uint32 dwFileRemove = 0;
  while(isCanLoop())
  {
   //从列表中取出数据
   Dir_Event Event = (Dir_Event)m_MultiEvent->wait();
   switch(Event)
   {
   case Exit:
    LGTRACE(Output_Console,"收到退出信号");
    return;
    break;
   case ProcessData:
    m_MultiEvent->reset(ProcessData);
    uint32 count = m_Queue.size();
    
    for(int32 i = 0;i < count;i++)
    {
     Watch_tag* p = m_Queue.front();
     
     while(!p->IsEnd())
     {
      switch(p->GetAction())
      {
      case FILE_ACTION_MODIFIED:
       {
        string temp = p->GetCurrentString();
        int Ret = CopyToBuffer("ModifyFile:",temp,++dwFileModify);
       }
       break;
      case FILE_ACTION_ADDED:
       {
        string temp = p->GetCurrentString();
        int Ret = CopyToBuffer("AddFile:",temp,++dwFileAdd);
       }
       break;
      case FILE_ACTION_REMOVED:
       {
        string temp = p->GetCurrentString();
        int Ret = CopyToBuffer("RemoveFile:",temp,++dwFileRemove);
       }
       break;
      }
      ++(*p);
     }
     m_Mutex.lock(__FILE__,__LINE__);
     m_Queue.pop();
     m_Mutex.unlock(__FILE__,__LINE__);
     delete p;
    }
   }
   Sleep(1);
  }
  LGTRACE(Output_Console,"正常退出");
 }
 /************************************************************************/
 /*       lg_DirWatch实现                                                */
 /************************************************************************/
 lg_DirWatch::lg_DirWatch() : m_bIsInited(FALSE),m_hDir(INVALID_HANDLE_VALUE),m_pMonitor(NULL)
 {
  lg_Thread::setThreadName("lg_DirWatch");
 }
 lg_DirWatch::~lg_DirWatch()
 {
  if(m_bIsInited)
   quit();
  if(m_pWatch)
  {
   delete m_pWatch;
   m_pWatch = NULL;
  }
 }
 BOOL lg_DirWatch::Init(string path,lg_DirMonitor* pMo)
 {
  if(INVALID_HANDLE_VALUE != m_hDir)
   throw lg_Exception("lg_DirMonitor::Init()","句柄已经有效,重复初始化!",0x0000000A);
  if(path.size() == 0)
   throw lg_Exception("lg_DirMonitor::Init()","所提供的路径为空!",0x0000000B);
  if(access(path.c_str(),0) != 0)
   throw lg_Exception("lg_DirMonitor::Init()","提供的路径不存在!",0x0000000C);
  m_path = path;
  m_pMonitor = pMo;
  m_bIsInited = TRUE;
  m_hDir = CreateFile(
   m_path.c_str(),
   FILE_LIST_DIRECTORY,
   FILE_SHARE_READ|FILE_SHARE_DELETE|FILE_SHARE_WRITE,
   NULL,
   OPEN_EXISTING,
   //如果FILE_FLAG_OVERLAPPED标志不使用,就不能直接CloseHandle,否则调用会挂起
   FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OVERLAPPED,   // file attributes
   NULL);
  if ( INVALID_HANDLE_VALUE == m_hDir )
  {
   throw lg_Exception("lg_DirMonitor::Init()","打开文件失败",0x0000000D);
  }
  start();
  return TRUE;
 }
 void lg_DirWatch::Release()
 {
  if(INVALID_HANDLE_VALUE != m_hDir)
   CloseHandle(m_hDir);
 }
 void lg_DirWatch::run()
 {
  while(isCanLoop())
  {
   m_pWatch = new Watch_tag(m_hDir,BUFSIZE);
   if(m_pWatch->GetChangeInfo())
   {
    m_pMonitor->SetData(m_pWatch);
    m_pWatch = NULL;
   }
   else
   {
    DWORD Err = GetLastError();
    uint32 nTempSize = 2048;
    char* szTemp = new char[ nTempSize ];
    
    // 格式化错误信息
    FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, NULL, Err,
     MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
     szTemp,
     nTempSize,
     NULL);
    TRACE("%s\n",szTemp);
    delete [] szTemp;
    LGTRACE(Output_Console,"lg_DirWatch遇到错误退出");
    return;
   }
  }
  LGTRACE(Output_Console,"lg_DirWatch正常退出");
 }
}

posted on 2006-05-24 23:42 龙仪 阅读(1965) 评论(1)  编辑 收藏

评论

# re: 代码库之十三 - 目录监控类(0.2 060524) 2006-05-25 15:13 oshj

顶一个

标题  
姓名  
主页
验证码 *
内容   
  登录  使用高级评论  Top
[使用Ctrl+Enter键可以直接提交]