首页 > 代码库 > windows下检测文件改变

windows下检测文件改变

        这个主要是应用在我前一篇博客里提到的脚本热加载功能。主要实现的功能检测文件夹内文件的变化(改变、新增、删除、重命名),当发现改变的时候通知lua重新加载脚本。基本上就是一个windows api的使用。实际应用中会有一些细节需要注意,比如我习惯使用sublime text编辑,而sublime text保存文件不是直接改变文件内容,而是新增一个文件。这些细节情况需要实际使用中微调。

        代码如下:

#include "FileWatcher.h"
#include "cocos2d.h"
#include "CCLuaEngine.h"

using namespace cocos2d;

#ifdef WIN32
//  函数: WatchChanges(LPVOID lpParameter)  
//  
//  目的: 监控目录的程序  
//  
//  注释:主函数创建线程时制定了这个函数的入口  
//       届时该子程序将自动启动执行。  
//  备注:因为代码不全,看下面的代码时,主要参考红色的字体部分  
static int lastChangeTime = 0;

void reloadGame()
{
	int time = GetTickCount();
	if (time - lastChangeTime <= 1000) {
		// 忽略短时间内的重新加载请求
		return;
	}

	Director::getInstance()->getScheduler()->performFunctionInCocosThread([](){
		auto engine = LuaEngine::getInstance();
		ScriptEngineManager::getInstance()->setScriptEngine(engine);
		lua_State* L = engine->getLuaStack()->getLuaState();
		lua_getglobal(L, "reloadGame");
		lua_call(L, 0, 0);
	});
}
DWORD WINAPI WatchChanges(LPVOID lpParameter)//返回版本信息  
{
	wchar_t watchDirectory[512] = {0};
	MultiByteToWideChar(CP_ACP, 0, (char*)lpParameter, strlen((char*)lpParameter), watchDirectory, sizeof(watchDirectory) / sizeof(wchar_t));

	//创建一个目录句柄  
	HANDLE handle_directory=CreateFile(watchDirectory,  
		FILE_LIST_DIRECTORY,  
		FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,  
		NULL,  
		OPEN_EXISTING,  
		FILE_FLAG_BACKUP_SEMANTICS,  
		NULL);

	if(handle_directory==INVALID_HANDLE_VALUE) {  
		DWORD ERROR_DIR=GetLastError();
		CCLOG("Open Directory Error");
	}  

	BOOL watch_state;  
	while (TRUE)  
	{
		char buffer[1024] = {0};
		DWORD bytesReturned = 0;
		FILE_NOTIFY_INFORMATION* notify = (FILE_NOTIFY_INFORMATION*)buffer;
		watch_state=ReadDirectoryChangesW(handle_directory,  (LPVOID)buffer,  
			sizeof(buffer), TRUE,  
			FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_DIR_NAME|FILE_NOTIFY_CHANGE_LAST_WRITE,  
			(LPDWORD)&bytesReturned, NULL, NULL);

		int err = GetLastError();
		if (err == ERROR_INVALID_FUNCTION || err == ERROR_NOTIFY_ENUM_DIR) {
			return -1;
		} 

		if(watch_state != 0) {
			DWORD length=WideCharToMultiByte(0,0,notify->FileName,-1,NULL,0,NULL,NULL);  
			char fileName[256] = {0};
			WideCharToMultiByte(0,0,notify->FileName,-1,fileName,length,NULL,NULL);  

			//这里主要就是检测返回的信息,需要注意FILE_NOTIFY_INFORMATION结构体的定义,以便正确调用返回信息
			if (notify->Action==FILE_ACTION_ADDED) {
				CCLOG("file add: %s", fileName);
				// sublime 修改文件是新增一个临时文件,这个是兼容措施
				reloadGame();
			}  
			if (notify->Action==FILE_ACTION_REMOVED) {
				CCLOG("file delete: %s", fileName);
			}  
			if (notify->Action==FILE_ACTION_MODIFIED) {  
				CCLOG("file changed: %s", fileName);
				reloadGame();
			}  

			//对于下面两种情况,Action本身也是文件名(可能是old_name也可能是new_name)  
			if (notify->Action==FILE_ACTION_RENAMED_OLD_NAME) {
				CCLOG("file rename old name: %s", fileName); 
			}  
			if (notify->Action==FILE_ACTION_RENAMED_NEW_NAME) {
				CCLOG("file rename new name: %s", fileName);

			}
		}  
		Sleep(500);  
	}  
	return 0;  
}
#endif

void startWatch(const char* path)
{
#ifdef WIN32
	static std::string s_path = path;
	//创建一个线程专门用于监控文件变化  
	HANDLE TrheadWatch=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)WatchChanges,(void*)s_path.c_str(),0,NULL);  
	CloseHandle(TrheadWatch);
#endif
}

注册给lua的实现:

#include "lua_cutil.h"
//#include <conio.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdio.h>
#include <string>

#include "FileWatcher.h"

#include <lua.h>
#include <lauxlib.h>

int lua_cutil_watch(lua_State *pL)
{
	std::string path = luaL_checkstring(pL, 1);
	startWatch(path.c_str());
	return 0;
}

int luaopen_cutil(lua_State *pL)
{
	// 注册lua函数
	luaL_Reg reg [] = {
		{"watch",     lua_cutil_watch},
		{NULL, NULL}
	};

	luaL_register(pL, "cutil", reg);
	return 1;
}

lua中调用的方式:

                local mainPath = cc.FileUtils:getInstance():fullPathForFilename('Main.lua');
                mainPath = string.sub(mainPath, 1, string.find(mainPath, 'Main%.lua') - 1);
                print(mainPath);
                cutil.watch(mainPath);


windows下检测文件改变