main.cpp
#include <stdio.h>
#include "IPcdnSdk.h"
#include "common/Utils.h"
#include <random>
#include <stdlib.h>
#include <iostream>
#include <sstream>
#include <io.h>
#include <fcntl.h>
#include "ReadDirectoryChanges.h"
#include <vector>
#include <iostream>
#include <windows.h>
#include <process.h>
#include <synchapi.h>
#include <winnt.h>
#include <signal.h>
using namespace std;
int main(int argc, char* argv[])
{
//设置监听目录下文件变化的事件
const DWORD dwNotificationFlags =
FILE_NOTIFY_CHANGE_LAST_WRITE
| FILE_NOTIFY_CHANGE_CREATION
| FILE_NOTIFY_CHANGE_FILE_NAME
| FILE_NOTIFY_CHANGE_DIR_NAME;
ReadDirectoryChanges changes;
string dir = "D:/data";
bool isSucc = changes.AddDirectory(dir, false, dwNotificationFlags);
vector<TDirectoryChangeNotification> notifications;
struct _stat s;
_stat(dir.c_str(), &s);
if (!(_S_IFDIR & s.st_mode))
{
printf("%s is not a dir\n", dir.c_str());
}
while (true)
{
cout << "Deal with other things..." << endl;
cout << changes.getReadChangesServer()->getReadChangeRequestSum() << endl;
Sleep(5000);
_stat(dir.c_str(), &s);
if (!(_S_IFDIR & s.st_mode))
{
changes.Terminate();
break;
}
//获取文件变化事件
changes.GetNotifications(notifications);
for (vector<TDirectoryChangeNotification>::iterator iter = notifications.begin(); iter != notifications.end(); iter++) {
DWORD dwAction = iter->first;
string filename = iter->second;
switch (dwAction)
{
case FILE_ACTION_ADDED:
cout << filename << " is Added!" << endl;
break;
case FILE_ACTION_REMOVED:
cout << filename << " is Deleted!" << endl;
break;
case FILE_ACTION_MODIFIED:
cout << filename << " is Modified!" << endl;
break;
case FILE_ACTION_RENAMED_OLD_NAME:
cout << filename << " is Renamed From!" << endl;
break;
case FILE_ACTION_RENAMED_NEW_NAME:
cout << filename << " is Renamed To!" << endl;
break;
default:
cout << filename << " unkonw action!" << endl;
break;
}
}
}
return 0;
}
ReadDirectoryChanges.h
#pragma once
#include <Windows.h>
#include <string>
#include "ReadChangesServer.h"
using namespace std;
typedef pair<DWORD, string> TDirectoryChangeNotification;
/**
此类作用:
1、启动新线程,运行ReadChangesServer的ThreadStartProc函数,监控目录变化
2、添加需要监控的目录
*/
class ReadDirectoryChanges
{
public:
ReadDirectoryChanges(int nMaxChanges = 1000);
virtual ~ReadDirectoryChanges();
void Init();
void Terminate(); //停止监听线程,停止目录监听
/// <summary>
/// Add a new directory to be monitored.
/// </summary>
/// <param name="wszDirectory">Directory to monitor.</param>
/// <param name="bWatchSubtree">True to also monitor subdirectories.</param>
/// <param name="dwNotifyFilter">The types of file system events to monitor, such as FILE_NOTIFY_CHANGE_ATTRIBUTES.</param>
/// <param name="dwBufferSize">The size of the buffer used for overlapped I/O.</param>
/// <remarks>
/// <para>
/// This function will make an APC call to the worker thread to issue a new
/// ReadDirectoryChangesW call for the given directory with the given flags.
/// </para>
/// </remarks>
bool AddDirectory(string wszDirectory, BOOL bWatchSubtree, DWORD dwNotifyFilter, DWORD dwBufferSize = 16384);
// "Push" is for usage by ReadChangesRequest. Not intended for external usage.
//当有文件事件消息时,由ReadChangesRequest调用
void Push(DWORD dwAction, string& wstrFilename);
void GetNotifications(vector<TDirectoryChangeNotification>& v); //获取文件变化事件消息
unsigned int GetThreadId() { return m_dwThreadId; }
ReadChangesServer* getReadChangesServer() { return m_pReadChangesServer; }
private:
ReadChangesServer* m_pReadChangesServer;
HANDLE m_hThread;
unsigned int m_dwThreadId;
HANDLE m_hMutex; // 互斥量,用于同步
vector<TDirectoryChangeNotification> m_Notifications; //存储事件消息
};
ReadDirectoryChanges.cpp
#include "ReadDirectoryChanges.h"
#include <process.h>
#include <iostream>
ReadDirectoryChanges::ReadDirectoryChanges(int nMaxCount)
{
m_hThread = NULL;
m_dwThreadId = 0;
m_pReadChangesServer = new ReadChangesServer(this);
m_hMutex = CreateMutex(NULL, FALSE, NULL);
m_Notifications.reserve(nMaxCount);
}
ReadDirectoryChanges::~ReadDirectoryChanges()
{
Terminate();
delete m_pReadChangesServer;
m_pReadChangesServer = NULL;
::CloseHandle(m_hMutex);
}
void ReadDirectoryChanges::Init()
{
//
// Kick off the worker thread, which will be
// managed by CReadChangesServer.
//
m_hThread = (HANDLE)_beginthreadex(NULL,
0,
ReadChangesServer::ThreadStartProc,
m_pReadChangesServer,
0,
&m_dwThreadId
);
Sleep(1000);
}
void ReadDirectoryChanges::Terminate()
{
if (m_hThread)
{
QueueUserAPC(ReadChangesServer::TerminateProc, m_hThread, (ULONG_PTR)m_pReadChangesServer);
::WaitForSingleObjectEx(m_hThread, 10000, true);
::CloseHandle(m_hThread);
m_hThread = NULL;
m_dwThreadId = 0;
}
}
bool ReadDirectoryChanges::AddDirectory(string szDirectory, BOOL bWatchSubtree, DWORD dwNotifyFilter, DWORD dwBufferSize)
{
m_pReadChangesServer->setTerminate(false); //若调用过Terminate, m_pReadChangesServer的m_bTerminate为true,需要重设为false
if (!m_hThread)
Init();
if (!m_hThread)
return false;
ReadChangesRequest* pRequest = new ReadChangesRequest(m_pReadChangesServer, szDirectory, bWatchSubtree, dwNotifyFilter, dwBufferSize);
//APC就是异步调用过程。线程都有个APC队列,QueueUserAPC是向线程的APC队列中插入一个APC过程
QueueUserAPC(ReadChangesServer::AddDirectoryProc, m_hThread, (ULONG_PTR)pRequest);
return true;
}
void ReadDirectoryChanges::Push(DWORD dwAction, string& wstrFilename)
{
WaitForSingleObject(m_hMutex, INFINITE);
m_Notifications.push_back(TDirectoryChangeNotification(dwAction, wstrFilename));
ReleaseMutex(m_hMutex);
}
void ReadDirectoryChanges::GetNotifications(vector<TDirectoryChangeNotification>& v)
{
WaitForSingleObject(m_hMutex, INFINITE);
v.assign(m_Notifications.begin(), m_Notifications.end());
m_Notifications.clear();
ReleaseMutex(m_hMutex);
}
ReadChangesServer.h
#pragma once
#include <windows.h>
#include <string>
#include <vector>
#include "ReadChangesRequest.h"
class ReadDirectoryChanges;
/**
此类作用:
处理新的目录监听,每个监听目录对应一个ReadChangesRequest对象
*/
class ReadChangesServer
{
public:
ReadChangesServer(ReadDirectoryChanges* pReadDirectoryChanges);
virtual ~ReadChangesServer();
// 线程运行入口函数
static unsigned int WINAPI ThreadStartProc(LPVOID arg);
// Called by QueueUserAPC to start orderly shutdown. 停止函数
static void CALLBACK TerminateProc(__in ULONG_PTR arg);
// Called by QueueUserAPC to add another directory. 有新目录添加时,会调用些函数
static void CALLBACK AddDirectoryProc(__in ULONG_PTR arg);
ReadDirectoryChanges* getReadDirectoryChanges() { return m_pReadDirectoryChanges; }
int getReadChangeRequestSum() { return m_pReadChangesRequests.size(); } //获取已经监听的目录总数,一个ReadChangesRequest表示监听一个目录
void setTerminate(bool b) { m_bTerminate = b; }
private:
void Run();
void AddDirectory(ReadChangesRequest* pReadChangesRequest);
void RequestTermination();
vector<ReadChangesRequest*> m_pReadChangesRequests;
volatile bool m_bTerminate;
ReadDirectoryChanges* m_pReadDirectoryChanges;
};
ReadChangesServer.cpp
#include "ReadChangesServer.h"
#include <iostream>
ReadChangesServer::ReadChangesServer(ReadDirectoryChanges* pReadDirectoryChanges)
{
m_bTerminate = false;
m_pReadDirectoryChanges = pReadDirectoryChanges;
}
ReadChangesServer::~ReadChangesServer()
{
if (!m_pReadChangesRequests.empty())
{
RequestTermination();
}
}
unsigned int WINAPI ReadChangesServer::ThreadStartProc(LPVOID arg)
{
ReadChangesServer* pReadChangesServer = (ReadChangesServer*)arg;
pReadChangesServer->Run();
return 0;
}
// Called by QueueUserAPC to start orderly shutdown.
void CALLBACK ReadChangesServer::TerminateProc(__in ULONG_PTR arg)
{
ReadChangesServer* pReadChangesServer = (ReadChangesServer*)arg;
pReadChangesServer->RequestTermination();
}
// Called by QueueUserAPC to add another directory.
void CALLBACK ReadChangesServer::AddDirectoryProc(__in ULONG_PTR arg)
{
ReadChangesRequest* pReadChangesRequest = (ReadChangesRequest*)arg;
pReadChangesRequest->getReadChangesServer()->AddDirectory(pReadChangesRequest);
}
void ReadChangesServer::Run()
{
while (!m_bTerminate)
{
DWORD rc = ::SleepEx(INFINITE, true); //线程处于alterable状态,若有消息进行apc队列,或者有其他消息时,则被唤醒
}
}
void ReadChangesServer::AddDirectory(ReadChangesRequest* pReadChangesRequest)
{
if (pReadChangesRequest->OpenDirectory())
{
m_pReadChangesRequests.push_back(pReadChangesRequest);
pReadChangesRequest->BeginRead();
}
else
{
delete pReadChangesRequest;
pReadChangesRequest = NULL;
}
}
void ReadChangesServer::RequestTermination()
{
for (DWORD i = 0; i < m_pReadChangesRequests.size(); ++i)
{
// Each Request object will delete itself.
m_pReadChangesRequests[i]->RequestTermination();
delete m_pReadChangesRequests[i];
m_pReadChangesRequests[i] = NULL;
}
m_pReadChangesRequests.clear();
m_bTerminate = true;
}
ReadChangesRequest.h
#pragma once
#include <windows.h>
#include <string>
#include <vector>
using namespace std;
class ReadChangesServer;
class ReadChangesRequest
{
public:
ReadChangesRequest(ReadChangesServer* pReadChangesServer, string directory, BOOL includeChildren, DWORD dwFilterFlags, DWORD size);
virtual ~ReadChangesRequest();
bool OpenDirectory(); //获取目录句柄
void BeginRead(); //监听目录事件
// The dwSize is the actual number of bytes sent to the APC.
void BackupBuffer(DWORD dwSize)
{
// We could just swap back and forth between the two
// buffers, but this code is easier to understand and debug.
memcpy(&m_backupBuffer[0], &m_buffer[0], dwSize);
}
void ProcessNotification(); //处理事件
void RequestTermination() //终止函数
{
::CancelIo(m_hDirectory);
::CloseHandle(m_hDirectory);
m_hDirectory = NULL;
}
ReadChangesServer* getReadChangesServer() { return m_pReadChangesServer; }
private:
//监听目录的回调函数
static VOID CALLBACK NotificationCompletion(
DWORD dwErrorCode, // completion code
DWORD dwNumberOfBytesTransfered, // number of bytes transferred
LPOVERLAPPED lpOverlapped); // I/O information buffer
std::string WString2String(const std::wstring& ws); //宽字符转成多字节
std::wstring String2WString(const std::string& s); //多字节转成宽字符
// Parameters from the caller for ReadDirectoryChangesW().
DWORD m_dwFilterFlags;
BOOL m_includeChildren;
string m_directory;
// Result of calling CreateFile().
HANDLE m_hDirectory;
// Required parameter for ReadDirectoryChangesW().
OVERLAPPED m_overlapped;
// Data buffer for the request.
// Since the memory is allocated by malloc, it will always
// be aligned as required by ReadDirectoryChangesW().
vector<BYTE> m_buffer;
// Double buffer strategy so that we can issue a new read
// request before we process the current buffer.
vector<BYTE> m_backupBuffer;
ReadChangesServer* m_pReadChangesServer;
};
ReadChangesRequest.cpp
#include "ReadChangesRequest.h"
#include "ReadChangesServer.h"
#include "ReadDirectoryChanges.h"
#include <string>
#include <locale>
#include <iostream>
ReadChangesRequest::ReadChangesRequest(ReadChangesServer* pReadChangesServer, string directory, BOOL includeChildren, DWORD dwFilterFlags, DWORD size)
{
m_pReadChangesServer = pReadChangesServer;
m_directory = directory;
m_includeChildren = includeChildren;
m_dwFilterFlags = dwFilterFlags;
m_hDirectory = 0;
::ZeroMemory(&m_overlapped, sizeof(OVERLAPPED));
// The hEvent member is not used when there is a completion
// function, so it's ok to use it to point to the object.
m_overlapped.hEvent = this;
m_buffer.resize(size);
m_backupBuffer.resize(size);
}
ReadChangesRequest::~ReadChangesRequest()
{
if (m_hDirectory)
RequestTermination();
// RequestTermination() must have been called successfully.
//_ASSERTE(m_hDirectory == NULL);
}
bool ReadChangesRequest::OpenDirectory()
{
// Allow this routine to be called redundantly.
if (m_hDirectory)
return true;
wstring dir = String2WString(m_directory);
m_hDirectory = ::CreateFileW(
(LPCWSTR)dir.c_str(), // pointer to the file name
FILE_LIST_DIRECTORY, // access (read/write) mode
FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, // share mode
NULL, // security descriptor
OPEN_EXISTING, // how to create
FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OVERLAPPED, // file attributes
NULL); // file with attributes to copy
if (m_hDirectory == INVALID_HANDLE_VALUE)
{
return false;
}
return true;
}
void ReadChangesRequest::BeginRead()
{
DWORD dwBytes = 0;
// This call needs to be reissued after every APC.
BOOL success = ::ReadDirectoryChangesW(
m_hDirectory, // handle to directory
&m_buffer[0], // read results buffer
m_buffer.size(), // length of buffer
m_includeChildren, // monitoring option
m_dwFilterFlags, // filter conditions
&dwBytes, // bytes returned
&m_overlapped, // overlapped buffer
&NotificationCompletion); // completion routine
}
//static
VOID CALLBACK ReadChangesRequest::NotificationCompletion(
DWORD dwErrorCode, // completion code
DWORD dwNumberOfBytesTransfered, // number of bytes transferred
LPOVERLAPPED lpOverlapped) // I/O information buffer
{
ReadChangesRequest* pReadChangesRequest = (ReadChangesRequest*)lpOverlapped->hEvent;
if (dwErrorCode == ERROR_OPERATION_ABORTED) //调用CancelIo函数时会进入
{
return;
}
// Can't use sizeof(FILE_NOTIFY_INFORMATION) because
// the structure is padded to 16 bytes.
_ASSERTE(dwNumberOfBytesTransfered >= offsetof(FILE_NOTIFY_INFORMATION, FileName) + sizeof(WCHAR));
if (!dwNumberOfBytesTransfered) //调用CancelIo函数或者CloseHandle函数时会进入
{
return;
}
pReadChangesRequest->BackupBuffer(dwNumberOfBytesTransfered);
// Get the new read issued as fast as possible. The documentation
// says that the original OVERLAPPED structure will not be used
// again once the completion routine is called.
pReadChangesRequest->BeginRead();
pReadChangesRequest->ProcessNotification();
}
void ReadChangesRequest::ProcessNotification()
{
BYTE* pBase = m_backupBuffer.data();
for (;;)
{
FILE_NOTIFY_INFORMATION& fni = (FILE_NOTIFY_INFORMATION&)*pBase;
wstring wstrFilename(fni.FileName, fni.FileNameLength / sizeof(WCHAR));
string filename = WString2String(wstrFilename);
m_pReadChangesServer->getReadDirectoryChanges()->Push(fni.Action, filename);
if (!fni.NextEntryOffset)
break;
pBase += fni.NextEntryOffset;
};
}
std::string ReadChangesRequest::WString2String(const std::wstring& ws) //宽字符转成多字节
{
std::string strLocale = setlocale(LC_ALL, "");
const wchar_t* wchSrc = ws.c_str();
size_t nDestSize = wcstombs(NULL, wchSrc, 0) + 1; //wide charactor string to multi bytes string
char* chDest = new char[nDestSize];
memset(chDest, 0, nDestSize);
wcstombs(chDest, wchSrc, nDestSize);
std::string strResult = chDest;
delete[]chDest;
setlocale(LC_ALL, strLocale.c_str());
return strResult;
}
std::wstring ReadChangesRequest::String2WString(const std::string& s) //多字节转成宽字符
{
std::string strLocale = setlocale(LC_ALL, "");
const char* chSrc = s.c_str();
size_t nDestSize = mbstowcs(NULL, chSrc, 0) + 1;
wchar_t* wchDest = new wchar_t[nDestSize];
wmemset(wchDest, 0, nDestSize);
mbstowcs(wchDest, chSrc, nDestSize);
std::wstring wstrResult = wchDest;
delete[]wchDest;
setlocale(LC_ALL, strLocale.c_str());
return wstrResult;
}