项目要用到目录监控的功能,但是下载了几个代码都或多或少有丢失文件名的情况,所以研究了一下,希望能给大家节省些时间。
总结如下
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的建议--------------------------------------------------------
m_hDir需要在lg_DirMonitor中关掉,所以放在lg_DirMonitor里
Watch_tag以后有可能不是固定的,而且内存是在lg_DirWatch分配的,所以觉得动态比较好
“或者跟本上把Watch_tag直接用FILE_NOTIFY_INFORMATION 就算了“
Watch_tag中的缓冲要保存多个FILE_NOTIFY_INFORMATION 所以不能直接定义
////////////////////////////////////////////////////////////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 lg_DirMonitor;
class lg_DirWatch : public lg_Thread
{
public:
lg_DirWatch();
virtual ~lg_DirWatch();
BOOL Init(HANDLE dir,string name,lg_DirMonitor* pMo);
void run();
private:
char *m_pBuf;
HANDLE m_hDir;
BOOL m_bIsInited;
string m_name;
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(cpstr str,uint32 len);
private:
//缓冲块标志
class Watch_tag
{
public:
Watch_tag() : m_pstr(NULL),m_len(0)
{
}
~Watch_tag()
{
if(m_pstr){
delete [] m_pstr;
m_pstr = NULL;
}
}
void SetData(cpstr str,uint32 len)
{
m_pstr = const_cast<char*>(str);
m_len = len;
}
cpstr GetData(uint32& len)
{
len = m_len;
return m_pstr;
}
private:
char* m_pstr;
uint32 m_len;
};
typedef queue<Watch_tag*> QList;
int CopyToBuffer(const string head,const FILE_NOTIFY_INFORMATION * pfiNotify,const uint32 SerNum);
enum Dir_Event
{
Exit = 0,
ProcessData
};
HANDLE m_hDir;
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 "FileMon.h"
#include "DirMonitor.h"
#include "lg_Exception.h"
//使用VC6的时候注意:在winbase.h前面定义#define _WIN32_WINNT 0x0500
//否则无法通过编译
#include <winbase.h>
#include <io.h>
using namespace lglib;
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
namespace lglib
{
lg_DirMonitor::lg_DirMonitor(string strDirName)
: m_strMoniDir(strDirName),m_bIsInited(FALSE),m_MultiEvent(NULL),
m_LoopBuf(NULL),m_hDir(INVALID_HANDLE_VALUE)
{
lg_Thread::setThreadName("lg_DirMonitor");
}
lg_DirMonitor::~lg_DirMonitor()
{
if(INVALID_HANDLE_VALUE != m_hDir)
CloseHandle(m_hDir);
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(cpstr str,uint32 len)
{
Watch_tag* p = new Watch_tag;
p->SetData(str,len);
m_Mutex.lock(__FILE__,__LINE__);
m_Queue.push(p);
m_Mutex.unlock(__FILE__,__LINE__);
m_MultiEvent->set(ProcessData);
}
BOOL lg_DirMonitor::Init()
{
if(INVALID_HANDLE_VALUE != m_hDir)
throw lg_Exception("lg_DirMonitor::Init()","句柄已经有效,重复初始化!",0x00000001);
if(m_strMoniDir.size() == 0)
throw lg_Exception("lg_DirMonitor::Init()","所提供的路径为空!",0x00000002);
if(access(m_strMoniDir.c_str(),0) != 0)
throw lg_Exception("lg_DirMonitor::Init()","提供的路径不存在!",0x00000003);
m_hDir = CreateFile(
m_strMoniDir.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()","打开文件失败",0x00000004);
}
m_MultiEvent = new lg_MultiEvent(2);
m_LoopBuf = new lg_LoopBuffer;
m_LoopBuf->InitBuffer(1024000);
m_bIsInited = TRUE;
start();
w1.Init(m_hDir,"-1-",this);
return TRUE;
}
int lg_DirMonitor::CopyToBuffer(const string head,const FILE_NOTIFY_INFORMATION * pfiNotify,const uint32 SerNum)
{
char Output[1024];
WCHAR wcFileName[BYBUF] = {0};
memcpy( wcFileName, pfiNotify->FileName, pfiNotify->FileNameLength );
char cFileName[BYBUF];
WideCharToMultiByte( CP_ACP, 0, wcFileName, -1,
cFileName, BYBUF, NULL, NULL );
sprintf(Output,"%s<%s><%ld>\r\n",head.c_str(),cFileName,SerNum);
return m_LoopBuf->WriteBuffer(reinterpret_cast<uint8*>(Output),strlen(Output));
}
void lg_DirMonitor::run()
{
uint32 LenTemp;
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();
uint32 offset;
//实际上系统利用这个缓冲区保存一系列结果,如果只取一个值就会丢失结果
FILE_NOTIFY_INFORMATION * pfiNotifyInfo = (FILE_NOTIFY_INFORMATION *)p->GetData(LenTemp);
do{
offset = pfiNotifyInfo->NextEntryOffset;
switch(pfiNotifyInfo->Action)
{
case FILE_ACTION_MODIFIED:
{
int Ret = CopyToBuffer("ModifyFile:",pfiNotifyInfo,++dwFileModify);
if(Ret < 0)
LGTRACE(Output_Console,"FILE_ACTION_MODIFIED-缓冲满!");
break;
}
case FILE_ACTION_ADDED:
{
int Ret = CopyToBuffer("AddFile:",pfiNotifyInfo,++dwFileAdd);
if(Ret < 0)
LGTRACE(Output_Console,"FILE_ACTION_ADDED-缓冲满!");
break;
}
case FILE_ACTION_REMOVED:
{
int Ret = CopyToBuffer("RemoveFile:",pfiNotifyInfo,++dwFileRemove);
if(Ret < 0)
LGTRACE(Output_Console,"FILE_ACTION_REMOVED-缓冲满!");
break;
}
}
pfiNotifyInfo = (PFILE_NOTIFY_INFORMATION)((uint8*)pfiNotifyInfo + offset);
}while(offset > 0 );
m_Mutex.lock(__FILE__,__LINE__);
m_Queue.pop();
m_Mutex.unlock(__FILE__,__LINE__);
delete p;
}
}
Sleep(1);
}
LGTRACE(Output_Console,"正常退出");
}
lg_DirWatch::lg_DirWatch() : m_bIsInited(FALSE),m_hDir(INVALID_HANDLE_VALUE),m_pBuf(NULL),m_pMonitor(NULL)
{
lg_Thread::setThreadName("lg_DirWatch");
}
lg_DirWatch::~lg_DirWatch()
{
if(m_bIsInited)
quit();
if(m_pBuf)
{
delete [] m_pBuf;
m_pBuf = NULL;
}
}
BOOL lg_DirWatch::Init(HANDLE dir,string name,lg_DirMonitor* pMo)
{
m_hDir = dir;
m_name = name;
m_pMonitor = pMo;
m_bIsInited = TRUE;
start();
return TRUE;
}
void lg_DirWatch::run()
{
BOOL bSucceed;
uint32 dwBytesReturned = 0;
while(isCanLoop())
{
m_pBuf = new char[BUFSIZE];
dwBytesReturned = 0;
bSucceed = ReadDirectoryChangesW(
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);
if (bSucceed)
{
//这里尽快处理避免丢失结果
m_pMonitor->SetData(m_pBuf,dwBytesReturned);
m_pBuf = 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正常退出");
}
}