c++ windows下監控目錄變化

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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章