C/C++文件监控

#ifndef __FILE_GUARD_H__
#define __FILE_GUARD_H__

#include <functional>
#include <algorithm>
#include <thread>
#include <string>
#include <vector>
#include <map>

class FileGuard
{
public:
	//文件动作
	enum Action
	{
		//添加动作
		ADDED = 0x00000001,

		//删除动作
		REMOVED,

		//修改动作
		MODIFIED,

		//重命名旧名称动作
		RENAMED_OLD_NAME,

		//重命名新名称动作
		RENAMED_NEW_NAME,
	};

	/*
	* @brief 构造
	*/
	FileGuard();

	/*
	* @brief 析构
	*/
	~FileGuard();

	/*
	* @brief 存在路径
	* @param[in] path 路径
	* @retval true 存在
	* @retval false 不存在
	*/
	bool existPath(const std::string& path) const;

	/*
	* @brief 添加路径
	* @param[in] path 路径(*代表监控所有磁盘)(&代表监控除系统盘以外的磁盘)
	* @param[in] subpath 是否监控子路径
	* @retval true 成功
	* @retval false 失败
	*/
	bool addPath(const std::string& path, bool subpath = true);

	/*
	* @brief 删除路径
	* @param[in] path 路径
	* @return void
	*/
	void removePath(const std::string& path);

	/*
	* @brief 清空路径
	* @return void
	*/
	void clearPaths();

	/*
	* @brief 获取路径
	* @return (路径,是否监控子路径)
	*/
	std::map<std::string, bool> getPaths() const;

	/*
	* @brief 启动
	* @return void
	*/
	void start();

	/*
	* @brief 停止
	* @return void
	*/
	void stop();

	/*
	* @brief 重新开始监控
	* @retval true 成功
	* @retval false 失败
	*/
	bool restart();

	/*
	* @brief 是否启动
	* @retval true 已启动
	* @retval false 未启动
	*/
	bool isStart() const;

	/*
	* @brief 获取最终错误
	* @return 最终错误
	*/
	const char* getLastError() const;

	/*
	* @brief 添加后缀
	* @param[in] suffix 后缀名(.*或*代表所有)
	* @return void
	*/
	void addSuffix(const std::string& suffix);

	/*
	* @brief 添加后缀
	* @param[in] suffixes 后缀名
	* @return void
	*/
	void addSuffixes(const std::vector<std::string>& suffixes);

	/*
	* @brief 删除后缀
	* @param[in] suffix 后缀名
	* @return void
	*/
	void removeSuffix(const std::string& suffix);

	/*
	* @brief 删除后缀
	* @param[in] suffixes 后缀名
	* @return void
	*/
	void removeSuffixes(const std::vector<std::string>& suffixes);

	/*
	* @brief 清空后缀
	* @return void
	*/
	void clearSuffixes();

	/*
	* @brief 获取后缀
	* @return void
	*/
	std::vector<std::string> getSuffixes() const;

	//改变回调
	std::function<void(uint32_t action, const char* file)> onChanged = nullptr;

	//错误回调
	std::function<void(uint32_t error, const char* path)> onError = nullptr;

	//启动回调
	std::function<void(uint32_t thread, const char* path)> onStarted = nullptr;

	//停止回调
	std::function<void(uint32_t thread, const char* path)> onStopped = nullptr;

	//所有磁盘的路径
	static const char* const ALL_DISK_PATHS;

	//除系统以外的磁盘路径
	static const char* const EXCEPT_SYSTEM_DISK_PATHS;

	//所有后缀名
	static const char* const ALL_SUFFIXES;
protected:
	/*
	* @brief 设置最终错误
	* @param[in] fmt 格式化字符串
	* @param[in] ... 可变参数
	* @return void
	*/
	void setLastError(const char* fmt, ...);

private:
	
	//参数
	struct Arg
	{
		std::string path;
		bool subpath;
		char* buffer;
		static const size_t size = 1048576;
		void* file;
		void* wevent;
		void* revent;
		void* lapped;
		bool quit;
		unsigned long thread;
		unsigned long ecode;
		char error[256];

		Arg();

		~Arg();

		Arg(const Arg& o);

		Arg& operator=(const Arg& o);

		//创建
		bool create(const std::string& path, bool subpath);

		//释放
		void release();

		//等待
		void wait(size_t timeout = (size_t)-1) const;

		//退出
		void exit() const;
	};

	//参数
	std::vector<Arg> m_args;

	//后缀
	std::vector<std::string> m_suffixes;

	//错误信息
	std::string m_error = "未知错误";

	//是否启动
	bool m_start = false;
};

#define FILE_GUARD_C_API
#if defined(FILE_GUARD_C_API)

//#define FILE_GUARD_BUILD_DLL
#if defined(FILE_GUARD_BUILD_DLL)
#define FILE_GUARD_DLL_EXPORT __declspec(dllexport)
#else
#define FILE_GUARD_DLL_EXPORT
#endif

#include <stdbool.h>
#include <stdint.h>

enum file_guard_action
{
	//添加动作
	added_action = 0x00000001,

	//删除动作
	removed_action,

	//修改动作
	modified_action,

	//重命名旧名称动作
	renamed_old_name_action,

	//重命名新名称动作
	renamed_new_name_action
};

struct file_guard_path
{
	char path[512];
	bool subpath;
};

#if defined(__cplusplus)
extern "C" {
#endif // !__cplusplus

	FILE_GUARD_DLL_EXPORT void* file_guard_initialize();

	FILE_GUARD_DLL_EXPORT void file_guard_uninitialize(void* guard);

	FILE_GUARD_DLL_EXPORT bool file_guard_exist_path(void* guard, const char* path);

	FILE_GUARD_DLL_EXPORT bool file_guard_add_path(void* guard, const char* path, bool subpath);

	FILE_GUARD_DLL_EXPORT void file_guard_remove_path(void* guard, const char* path);

	FILE_GUARD_DLL_EXPORT void file_guard_clear_paths(void* guard);

	FILE_GUARD_DLL_EXPORT int file_guard_get_paths(void* guard, struct file_guard_path* path, int size);

	FILE_GUARD_DLL_EXPORT void file_guard_set_on_changed_callback(void* guard,
		void (*callback)(uint32_t action, const char* file, void* user), void* user);

	FILE_GUARD_DLL_EXPORT void file_guard_set_on_started_callback(void* guard,
		void (*callback)(uint32_t thread, const char* path, void* user), void* user);

	FILE_GUARD_DLL_EXPORT void file_guard_set_on_stopped_callback(void* guard, 
		void (*callback)(uint32_t thread, const char* path, void* user), void* user);

	FILE_GUARD_DLL_EXPORT void file_guard_set_on_error_callback(void* guard,
		void (*callback)(uint32_t error, const char* path, void* user), void* user);

	FILE_GUARD_DLL_EXPORT void file_guard_start(void* guard);

	FILE_GUARD_DLL_EXPORT void file_guard_start_ex(void* guard, void* user,
		void (*on_changed)(uint32_t action, const char* file, void* user),
		void (*on_stared)(uint32_t thread, const char* path, void* user),
		void (*on_stopped)(uint32_t thread, const char* path, void* user),
		void (*on_error)(uint32_t error, const char* path, void* user));

	FILE_GUARD_DLL_EXPORT void file_guard_stop(void* guard);

	FILE_GUARD_DLL_EXPORT bool file_guard_restart(void* guard);

	FILE_GUARD_DLL_EXPORT bool file_guard_is_start(void* guard);

	FILE_GUARD_DLL_EXPORT void file_guard_get_error(void* guard, char* error, int size);

	FILE_GUARD_DLL_EXPORT void file_guard_add_suffix(void* guard, const char* suffix);

	FILE_GUARD_DLL_EXPORT void file_guard_remove_suffix(void* guard, const char* suffix);

	FILE_GUARD_DLL_EXPORT void file_guard_clear_suffixes(void* gurad);

	FILE_GUARD_DLL_EXPORT int file_guard_get_suffixes(void* guard, char (*suffixes)[256], int size);

#if defined(__cplusplus)
}
#endif // !__cplusplus
#endif // !FILE_GUARD_C_API
#endif // !__FILE_GUARD_H__
#include "FileGuard.h"
#include <Windows.h>
#include <io.h>

#if defined(IDEBUG)
#define print(fmt, ...)\
do { \
	char buffer[512] = { 0 };\
	snprintf(buffer, sizeof(buffer), fmt, ##__VA_ARGS__);\
	printf(fmt, ##__VA_ARGS__);\
	OutputDebugString(buffer);\
}while(0);
#else
#define print(fmt, ...)
#endif

static std::string unicode2ascii(const std::wstring& str)
{
	int size = WideCharToMultiByte(CP_ACP, 0, str.c_str(), (int)str.size(), 0, 0, 0, 0) + 1;
	std::unique_ptr<char> buffer(new char[size]);
	WideCharToMultiByte(CP_ACP, 0, str.c_str(), (int)str.size(), buffer.get(), size, 0, 0);
	buffer.get()[size - 1] = '\0';
	return std::string(buffer.get());
}

const char* const FileGuard::ALL_DISK_PATHS = "*";

const char* const FileGuard::EXCEPT_SYSTEM_DISK_PATHS = "&";

const char* const FileGuard::ALL_SUFFIXES = ".*";

FileGuard::FileGuard()
{

}

FileGuard::~FileGuard()
{
	stop();
	clearPaths();
}

bool FileGuard::existPath(const std::string& path) const
{
	bool exist = false;
	for (const auto& x : m_args)
	{
		if (x.path == path)
		{
			exist = true;
			break;
		}
	}
	return exist;
}

bool FileGuard::addPath(const std::string& path, bool subpath)
{
	bool result = false, success = true;
	do
	{
		if (_access(path.c_str(), 0) == -1 && path != ALL_DISK_PATHS && path != EXCEPT_SYSTEM_DISK_PATHS)
		{
			setLastError("%s路径不存在", path.c_str());
			break;
		}

		std::vector<std::string> paths;
		if (path == ALL_DISK_PATHS || path == EXCEPT_SYSTEM_DISK_PATHS)
		{
			auto drives = GetLogicalDrives();
			char volume = 'A';
			while (drives)
			{
				if (drives & 1)
				{
					paths.push_back(std::string(1, volume) + ":\\");
				}
				volume++;
				drives >>= 1;
			}

			//排除无法监控的盘符
			std::vector<std::string> temp;
			for (const auto& x : paths)
			{
				uint32_t type = GetDriveType(x.c_str());
				if (type == DRIVE_UNKNOWN ||
					type == DRIVE_NO_ROOT_DIR ||
					type == DRIVE_REMOTE ||
					type == DRIVE_CDROM)
				{
					continue;
				}
				temp.push_back(x);
			}

			paths = temp;

			if (path == EXCEPT_SYSTEM_DISK_PATHS)
			{
				char dir[512] = { 0 };
				if (!GetWindowsDirectoryA(dir, sizeof(dir)))
				{
					setLastError("获取系统目录失败,错误代码:%lu", GetLastError());
					break;
				}

				const std::string win(dir, 3);
				for (auto iter = paths.begin(); iter != paths.end(); ++iter)
				{
					if (*iter == win)
					{
						paths.erase(iter);
						break;
					}
				}
			}
		}
		else
		{
			std::string str = path;
			char c = str.at(str.length() - 1);
			if (c != '\\' && c != '/')
			{
				str.append("\\");
			}

			for (auto& x : str)
			{
				if (x == '/')
				{
					x = '\\';
				}
			}
			paths.push_back(str);
		}

		for (const auto& x : paths)
		{
			if (!existPath(x))
			{
				Arg arg;
				if (!arg.create(x, subpath))
				{
					success = false;
					setLastError(arg.error);
					if (path == ALL_DISK_PATHS || path == EXCEPT_SYSTEM_DISK_PATHS)
					{
						continue;
					}
					break;
				}
				m_args.push_back(arg);
			}
		}

		if (!success)
		{
			for (const auto& x : paths)
			{
				removePath(x);
			}
			break;
		}

		result = true;
	} while (false);
	return result;
}

void FileGuard::removePath(const std::string& path)
{
	for (auto iter = m_args.begin(); iter != m_args.end(); ++iter)
	{
		if (iter->path == path)
		{
			iter->release();
			m_args.erase(iter);
			break;
		}
	}
}

void FileGuard::clearPaths()
{
	for (auto iter = m_args.begin(); iter != m_args.end(); ++iter)
	{
		iter->release();
	}
	m_args.clear();
}

std::map<std::string, bool> FileGuard::getPaths() const
{
	std::map<std::string, bool> map;
	for (const auto& x : m_args)
	{
		map.insert(std::make_pair(x.path, x.subpath));
	}
	return map;
}

void FileGuard::start()
{
	if (m_suffixes.size() == 1 && m_suffixes[0] == ".*")
	{
		m_suffixes.clear();
	}

	for (auto& x : m_args)
	{
		if (!x.quit)
		{
			continue;
		}

		std::thread([&](Arg* arg)->void
			{
				arg->quit = false;
				arg->thread = GetCurrentThreadId();
				if (onStarted)
				{
					onStarted(arg->thread, arg->path.c_str());
				}

				bool success = true;
				DWORD bytes = 0, offset = 0, error = 0;
				OVERLAPPED lapped = { 0 };
				arg->lapped = static_cast<void*>(&lapped);
				lapped.hEvent = arg->revent;
				do
				{
					memset(arg->buffer, 0, arg->size);
					print("thread %lu,path %s,start ReadDirectoryChangesW\n", arg->thread, arg->path.c_str());
					if (ReadDirectoryChangesW(arg->file,
						arg->buffer,
						arg->size,
						arg->subpath,
						FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE,
						&bytes,
						&lapped,
						nullptr))
					{
						print("thread %lu,path %s,start GetOverlappedResult\n", arg->thread, arg->path.c_str());
						if (GetOverlappedResult(arg->file, &lapped, &bytes, TRUE))
						{
							FILE_NOTIFY_INFORMATION* info = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(arg->buffer);
							do
							{
								if (onChanged)
								{
									std::wstring ws(info->FileName, info->FileNameLength / sizeof(wchar_t));
									std::string s(arg->path + unicode2ascii(ws));

									if (!m_suffixes.empty())
									{
										for (size_t i = 0; i < m_suffixes.size(); ++i)
										{
											size_t npos = s.find_last_of('.');
											if (npos != std::string::npos)
											{
												std::string suffix = s.substr(npos);
												std::transform(suffix.begin(), suffix.end(), suffix.begin(), std::tolower);
												if (suffix == m_suffixes[i])
												{
													onChanged(info->Action, s.c_str());
													break;
												}
											}
										}
									}
									else
									{
										onChanged(info->Action, s.c_str());
									}
								}
								offset = info->NextEntryOffset;
								info = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(reinterpret_cast<uint8_t*>(info) + offset);
							} while (offset);
						}
						else
						{
							arg->ecode = GetLastError();
							print("thread %lu,path %s,GetOverlappedResult false,error %lu\n",
								arg->thread, arg->path.c_str(), arg->ecode);
							if (arg->ecode == ERROR_OPERATION_ABORTED)
							{
								arg->ecode = 0;
								break;
							}
							success = false;
						}

						print("thread %lu,path %s,start ResetEvent\n", arg->thread, arg->path.c_str());
						if (!ResetEvent(lapped.hEvent))
						{
							arg->ecode = GetLastError();
							print("thread %lu,path %s,ResetEvent false,error %lu\n",
								arg->thread, arg->path.c_str(), arg->ecode);
							success = false;
							break;
						}
					}
					else
					{
						arg->ecode = GetLastError();
						print("thread %lu,path %s,ReadDirectoryChangeW false,error %lu\n",
							arg->thread, arg->path.c_str(), arg->ecode);
						success = false;
						break;
					}
					print("thread %lu,path %s,start WaitForSingleObject\n", arg->thread, arg->path.c_str());
					error = WaitForSingleObject(arg->wevent, 0);
				} while (error == WAIT_TIMEOUT);

				if (onError && !success)
				{
					onError(arg->ecode, arg->path.c_str());
				}

				if (onStopped)
				{
					onStopped(arg->thread, arg->path.c_str());
				}
				print("thread %lu,path %s,thread exit\n", arg->thread, arg->path.c_str());
				arg->quit = true;
			}, &x).detach();
	}
	m_start = true;
}

void FileGuard::stop()
{
	m_start = false;
	for (auto& x : m_args)
	{
		x.exit();
		x.wait();
	}
}

bool FileGuard::restart()
{
	stop();
	std::vector<Arg> args = m_args;
	clearPaths();
	for (const auto& x : args)
	{
		if (!addPath(x.path, x.subpath))
		{
			return false;
		}
	}
	start();
	return true;
}

bool FileGuard::isStart() const
{
	return m_start;
}

const char* FileGuard::getLastError() const
{
	return m_error.c_str();
}

void FileGuard::addSuffix(const std::string& suffix)
{
	std::string data(suffix);
	std::transform(data.begin(), data.end(), data.begin(), std::tolower);

	bool find = false, add = false;
	
	for (const auto& x : m_suffixes)
	{
		if (x == data)
		{
			find = true;
			break;
		}
	}

	if (!find)
	{
		if (data.find_last_of('.') == std::string::npos)
		{
			add = true;
		}
		m_suffixes.push_back(add ? "." + data : data);
	}

	for (const auto& x : m_suffixes)
	{
		if (x == ALL_SUFFIXES || std::string(".") + x == ALL_SUFFIXES)
		{
			m_suffixes.clear();
			m_suffixes.push_back(ALL_SUFFIXES);
			break;
		}
	}
}

void FileGuard::addSuffixes(const std::vector<std::string>& suffixes)
{
	m_suffixes = suffixes;
}

void FileGuard::removeSuffix(const std::string& suffix)
{
	std::string data(suffix);
	std::transform(data.begin(), data.end(), data.begin(), std::tolower);

	for (auto iter = m_suffixes.begin(); iter != m_suffixes.end(); ++iter)
	{
		if (*iter == data)
		{
			m_suffixes.erase(iter);
			break;
		}
	}
}

void FileGuard::removeSuffixes(const std::vector<std::string>& suffixes)
{
	for (auto iter = suffixes.begin(); iter != suffixes.end(); ++iter)
	{
		std::string data(*iter);
		std::transform(data.begin(), data.end(), data.begin(), std::tolower);
		m_suffixes.erase(std::remove(m_suffixes.begin(), m_suffixes.end(), data), m_suffixes.end());
	}
}

void FileGuard::clearSuffixes()
{
	m_suffixes.clear();
}

std::vector<std::string> FileGuard::getSuffixes() const
{
	return m_suffixes;
}

void FileGuard::setLastError(const char* fmt, ...)
{
	char buff[512] = { 0 };
	va_list ap;
	va_start(ap, fmt);
	vsnprintf(buff, sizeof(buff), fmt, ap);
	va_end(ap);
	m_error = buff;
}

FileGuard::Arg::Arg()
	:subpath(false),
	buffer(nullptr),
	file(INVALID_HANDLE_VALUE),
	wevent(nullptr),
	revent(nullptr),
	lapped(nullptr),
	quit(true),
	thread(0),
	ecode(0),
	error{ 0 }
{

}

FileGuard::Arg::~Arg()
{
	if (buffer)
	{
		delete[] buffer;
		buffer = nullptr;
	}
}

FileGuard::Arg::Arg(const Arg& o)
{
	path = o.path;
	subpath = o.subpath;
	if (o.buffer)
	{
		buffer = new char[o.size];
		memcpy(buffer, o.buffer, o.size);
	}
	file = o.file;
	wevent = o.wevent;
	revent = o.revent;
	lapped = o.lapped;
	quit = o.quit;
	thread = o.thread;
	ecode = o.ecode;
	memcpy(error, o.error, sizeof(error));
}

FileGuard::Arg& FileGuard::Arg::operator=(const Arg& o)
{
	if (!memcmp(this, &o, sizeof(Arg)))
	{
		return *this;
	}

	path = o.path;
	subpath = o.subpath;
	if (o.buffer)
	{
		buffer = new char[o.size];
		memcpy(buffer, o.buffer, o.size);
	}
	file = o.file;
	wevent = o.wevent;
	revent = o.revent;
	lapped = o.lapped;
	quit = o.quit;
	thread = o.thread;
	ecode = o.ecode;
	memcpy(error, o.error, sizeof(error));
	return *this;
}

bool FileGuard::Arg::create(const std::string& path, bool subpath)
{
	bool result = false;
	do
	{
		const char c = path.at(path.length() - 1);
		this->path = (c != '\\' && c != '/') ? (path + "\\") : (path);

		for (auto& x : this->path)
		{
			if (x == '/')
				x = '\\';
		}

		this->subpath = subpath;

		file = CreateFileA(path.c_str(),
			GENERIC_READ | GENERIC_WRITE | FILE_LIST_DIRECTORY,
			FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
			nullptr,
			OPEN_EXISTING,
			FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
			nullptr);
		if (file == INVALID_HANDLE_VALUE)
		{
			sprintf_s(error, "获取%s路径句柄失败,错误代码:%lu", path.c_str(), ::GetLastError());
			break;
		}

		wevent = CreateEventA(nullptr, true, false, nullptr);
		if (wevent == nullptr)
		{
			CloseHandle(file);
			file = INVALID_HANDLE_VALUE;
			sprintf_s(error, "创建%s路径事件失败,错误代码:%lu", path.c_str(), ::GetLastError());
			break;
		}

		revent = CreateEventA(nullptr, true, false, nullptr);
		if (revent == nullptr)
		{
			CloseHandle(file);
			file = INVALID_HANDLE_VALUE;
			CloseHandle(wevent);
			wevent = nullptr;
			sprintf_s(error, "创建%s路径折叠失败,错误代码:%lu", path.c_str(), ::GetLastError());
			break;
		}

		buffer = new char[size];
		if (buffer == nullptr)
		{
			CloseHandle(file);
			file = INVALID_HANDLE_VALUE;
			CloseHandle(wevent);
			wevent = nullptr;
			CloseHandle(revent);
			revent = nullptr;
			sprintf_s(error, "创建%s路径缓冲区失败,内存不足", path.c_str());
			break;
		}
		memset(buffer, 0, size);
		result = true;
	} while (false);
	return result;
}

void FileGuard::Arg::release()
{
	if (file != INVALID_HANDLE_VALUE)
	{
		CloseHandle(file);
		file = INVALID_HANDLE_VALUE;
	}

	if (wevent)
	{
		CloseHandle(wevent);
		wevent = nullptr;
	}

	if (revent)
	{
		CloseHandle(revent);
		revent = nullptr;
	}

	if (lapped)
	{
		lapped = nullptr;
	}

	if (buffer)
	{
		delete[] buffer;
		buffer = nullptr;
	}
}

void FileGuard::Arg::wait(size_t timeout) const
{
	auto tick = GetTickCount64();
	while (!quit)
	{
		if (GetTickCount64() - tick > timeout)
		{
			break;
		}
	}
}

void FileGuard::Arg::exit() const
{
	if (file != INVALID_HANDLE_VALUE)
	{
		CancelIoEx(file, static_cast<LPOVERLAPPED>(lapped));
	}
}

#if defined(FILE_GUARD_C_API)

#define get_guard(x) ((FileGuard*)(x))

void* file_guard_initialize()
{
	return new FileGuard;
}

void file_guard_uninitialize(void* guard)
{
	if (guard)
	{
		delete static_cast<FileGuard*>(guard);
	}
}

bool file_guard_exist_path(void* guard, const char* path)
{
	return get_guard(guard)->existPath(path);
}

bool file_guard_add_path(void* guard, const char* path, bool subpath)
{
	return get_guard(guard)->addPath(path, subpath);
}

void file_guard_remove_path(void* guard, const char* path)
{
	get_guard(guard)->removePath(path);
}

void file_guard_clear_paths(void* guard)
{
	get_guard(guard)->clearPaths();
}

int file_guard_get_paths(void* guard, file_guard_path* path, int size)
{
	auto map = get_guard(guard)->getPaths();
	int index = 0;
	for (const auto& x : map)
	{
		strcpy_s(path[index].path, x.first.c_str());
		path[index].subpath = x.second;
		if (index + 1 == size)
		{
			break;
		}
	}
	return index;
}

void file_guard_set_on_changed_callback(void* guard, void(*callback)(uint32_t action, const char* file, void* user), void* user)
{
	get_guard(guard)->onChanged = [user, callback](uint32_t action, const char* file) {
		callback(action, file, user);
	};
}

void file_guard_set_on_started_callback(void* guard, void(*callback)(uint32_t thread, const char* path, void* user), void* user)
{
	get_guard(guard)->onStarted = [user, callback](uint32_t thread, const char* path) {
		callback(thread, path, user);
	};
}

void file_guard_set_on_stopped_callback(void* guard, void(*callback)(uint32_t thread, const char* path, void* user), void* user)
{
	get_guard(guard)->onStopped = [user, callback](uint32_t thread, const char* path) {
		callback(thread, path, user);
	};
}

void file_guard_set_on_error_callback(void* guard, void(*callback)(uint32_t error, const char* path, void* user), void* user)
{
	get_guard(guard)->onError = [user, callback](uint32_t error, const char* path) {
		callback(error, path, user);
	};
}

void file_guard_start(void* guard)
{
	get_guard(guard)->start();
}

void file_guard_start_ex(void* guard, void* user,
	void(*on_changed)(uint32_t action, const char* file, void* user),
	void(*on_started)(uint32_t thread, const char* path, void* user),
	void(*on_stopped)(uint32_t thread, const char* path, void* user),
	void(*on_error)(uint32_t error, const char* path, void* user))
{
	get_guard(guard)->onChanged = [user, on_changed](uint32_t action, const char* file) {
		on_changed(action, file, user);
	};

	get_guard(guard)->onStarted = [user, on_started](uint32_t thread, const char* path) {
		on_started(thread, path, user);
	};

	get_guard(guard)->onStopped = [user, on_stopped](uint32_t thread, const char* path) {
		on_stopped(thread, path, user);
	};

	get_guard(guard)->onError = [user, on_error](uint32_t error, const char* path) {
		on_error(error, path, user);
	};

	get_guard(guard)->start();
}

void file_guard_stop(void* guard)
{
	get_guard(guard)->stop();
}

bool file_guard_restart(void* guard)
{
	return get_guard(guard)->restart();
}

bool file_guard_is_start(void* guard)
{
	return get_guard(guard)->isStart();
}

void file_guard_get_error(void* guard, char* error, int size)
{
	strncpy_s(error, size, get_guard(guard)->getLastError(), _TRUNCATE);
}

void file_guard_add_suffix(void* guard, const char* suffix)
{
	get_guard(guard)->addSuffix(suffix);
}

void file_guard_remove_suffix(void* guard, const char* suffix)
{
	get_guard(guard)->removeSuffix(suffix);
}

void file_guard_clear_suffixes(void* guard)
{
	get_guard(guard)->clearSuffixes();
}

int file_guard_get_suffixes(void* guard, char(*suffixes)[256], int size)
{
	auto datas = get_guard(guard)->getSuffixes();
	size_t i = 0;
	for (; i < datas.size(); ++i)
	{
		strcpy_s(suffixes[i], datas[i].c_str());
		if (i == size - 1)
		{
			i++;
			break;
		}
	}
	return (int)i;
}

#endif // !FILE_GUARD_BUILD_DLL

//测试代码
#include "FileGuard.h"
#include <stdio.h>

//C++风格
#if defined(__cplusplus)
int main()
{
	FileGuard guard;
	guard.addPath("*");//*代表监控当前计算机中,所有的磁盘
	guard.onChanged = [](uint32_t action, const char* file) {
		switch (action)
		{
			case FileGuard::Action::ADDED:
				printf("onChanged-> 文件[%s]添加动作\n", file);
				break;
			case FileGuard::Action::MODIFIED:
				printf("onChanged-> 文件[%s]修改动作\n", file);
				break;;
			case FileGuard::Action::REMOVED:
				printf("onChanged-> 文件[%s]移除动作\n", file);
				break;
			case FileGuard::Action::RENAMED_OLD_NAME:
				printf("onChanged-> 文件[%s]改名动作(旧)\n", file);
				break;
			case FileGuard::Action::RENAMED_NEW_NAME:
				printf("onChanged-> 文件[%s]改名动作(新)\n", file);
				break;
		default:
			break;
		}
	};

	guard.onError = [](uint32_t error, const char* path) {
		printf("onError-> 错误代码:%lu,路径:%s\n", error, path);
	};

	guard.onExit = [](uint32_t thread, const char* path) {
		printf("onExit-> 线程:%lu,路径:%s\n", thread, path);
	};

	guard.start();
	getchar();
	guard.stop();

	return 0;
}
#else
//C风格,如果使用C语言进行操作,需要将FileGuard编译为静态库或动态库,
//头文件中,删除C++的声明.因为是用C++写的,外面套了一层C语言的壳进行封装.
#pragma comment(lib, "FileGuard.lib")
void file_guard_on_changed(uint32_t action, const char* file, void* user)
{
	switch (action)
	{
		case FileGuard::Action::ADDED:
			printf("on_changed-> 文件[%s]添加动作\n", file);
			break;
		case FileGuard::Action::MODIFIED:
			printf("on_changed-> 文件[%s]修改动作\n", file);
			break;;
		case FileGuard::Action::REMOVED:
			printf("on_changed-> 文件[%s]移除动作\n", file);
			break;
		case FileGuard::Action::RENAMED_OLD_NAME:
			printf("on_changed-> 文件[%s]改名动作(旧)\n", file);
			break;
		case FileGuard::Action::RENAMED_NEW_NAME:
			printf("on_changed-> 文件[%s]改名动作(新)\n", file);
			break;
		default:
		break;
	}
}

void file_guard_on_exit(uint32_t thread, const char* path, void* user)
{
	printf("on_exit-> 路径%s,线程%lu\n", path, thread);
}

void file_guard_on_error(uint32_t error, const char* path, void* user)
{
	printf("on_error-> 路径%s,错误代码%lu\n", path, error);
}

int main()
{
    void* guard = file_guard_initialize();
	
	file_guard_add_path(guard, "*", true);//*代表监控当前计算机中,所有的磁盘
	
	file_guard_start(guard, NULL,
                    file_guard_on_changed,
                    file_guard_on_exit,
                    file_guard_on_error);
	
	getchar();
	file_guard_stop(guard);
	file_guard_uninitialize(guard);

    return 0;
}
#endif // !__cplusplus

项目展示:

通用DLL下载链接地址:

C/C++文件监控通用DLL-C++文档类资源-CSDN文库

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值