JAVA监控目录
1. 编写DLL
编写DLL文件是重点,这个DLL得作用就是用C++去监控某个目录,而具体得接口则暴露给JAVA,JAVA负责调用就OK。
原理如下:
根据JAVA传来得需要监视得目录路径,得到这个目录得句柄,同时新建一个线程去监控这个目录发生得一切,而具体得监视是由库函数ReadDirectoryChangesW完成得,它能得到目录修改的信息,然后将其打印出来!
但是在开发过程中,我遇到了很多很多的问题,主要有两个
1. VC++不识别ReadDirectoryChanges,上网搜索很多的方法都没有成功,直到一位高人指导,解决这个问题,需要两个方面
1. 头部信息
#include "stdafx.h"
#define _WIN32_WINNT 0x0500//必须再windows.h前才有效
#include <windows.h>
2. 设置一些必要的参数
在Project-Setting->C/C++->Preprocessor definitions->加上_WIN32_WINNT=0x0500,WINVER=0x0500那么就OK了
2. JAVA传递给C++会产生乱码,可能是我的String匹配有错,C++得string难道不是用JAVA得String匹配吗?这个问题我还是没有搞清楚,期待解决,最终我为了试验成功,就再DLL得代码中,默认指定了我得测试路径d:/watch
下面是具体得C++监控类源码
#include "stdafx.h"
#define _WIN32_WINNT 0x0500
#include <Winbase.h>
#include <string>
#include <cassert>
#include <conio.h>
#include <iostream>
using namespace std;
enum ACTION { ADDED=1, REMOVED=2, MODIFIED=3, RENAMED=4 };//判断文件操作类型得枚举
/*
*处理操作信息得函数
*/
void __stdcall MyDeal( ACTION act, std::string filename1, std::string filename2 )
{
switch( act )
{
case ADDED:
cout << "Added - " << filename1 << endl;
break;
case REMOVED:
cout << "Removed - " << filename1 << endl;
break;
case MODIFIED:
cout << "Modified - " << filename1 << endl;
break;
case RENAMED:
cout << "Rename - " << filename1 << " " << filename2 << endl;
break;
}
};
class FileSystemWatcher
{
public:
typedef void (__stdcall *LPDEALFUNCTION)( ACTION act, std::string filename1, std::string filename2 );
bool Run( std::string path)
{
WatchedDir = path;
DealFun = MyDeal;
DWORD ThreadId;
cout<<"创建线程"<<endl;
hThread=CreateThread( NULL,0,Routine,this,0,&ThreadId );
cout<<"创建结束"<<endl;
if (NULL!=hThread) {
cout<<"创建线程成功"<<endl;
} else {
cout<<"创建线程失败"<<endl;
}
return NULL!=hThread;
}
//释放资源
void Close()
{
if( NULL != hThread )
{
TerminateThread( hThread, 0 );
hThread = NULL;
}
if( INVALID_HANDLE_VALUE != hDir )
{
CloseHandle( hDir );
hDir = INVALID_HANDLE_VALUE;
}
}
FileSystemWatcher() : DealFun(NULL), hThread(NULL), hDir(INVALID_HANDLE_VALUE)
{
}
~FileSystemWatcher()
{
Close();
}
private:
std::string WatchedDir;
LPDEALFUNCTION DealFun;
HANDLE hThread;
HANDLE hDir;
private:
FileSystemWatcher( const FileSystemWatcher& );
FileSystemWatcher operator=( const FileSystemWatcher );
private:
static DWORD WINAPI Routine( LPVOID lParam )
{
cout<<"开始执行线程"<<endl;
FileSystemWatcher* obj = (FileSystemWatcher*)lParam;
cout<<"要观察得目录"<<obj->WatchedDir<<endl;
cout<<direc<<endl;
obj->hDir = CreateFile(
obj->WatchedDir.c_str(),
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
NULL
);
cout<<"创建文件"<<endl;
if( INVALID_HANDLE_VALUE == obj->hDir ) {
cout<<"无效值"<<endl;
return false;
}
char buf[ 2*(sizeof(FILE_NOTIFY_INFORMATION)+MAX_PATH) ];
FILE_NOTIFY_INFORMATION* pNotify=(FILE_NOTIFY_INFORMATION *)buf;
DWORD BytesReturned;
cout<<"读取信息"<<endl;
while(true)
{
if( ReadDirectoryChangesW( obj->hDir,
pNotify,
sizeof(buf),
true,
FILE_NOTIFY_CHANGE_FILE_NAME|
FILE_NOTIFY_CHANGE_DIR_NAME|
FILE_NOTIFY_CHANGE_ATTRIBUTES|
FILE_NOTIFY_CHANGE_SIZE|
FILE_NOTIFY_CHANGE_LAST_WRITE|
FILE_NOTIFY_CHANGE_LAST_ACCESS|
FILE_NOTIFY_CHANGE_CREATION|
FILE_NOTIFY_CHANGE_SECURITY,
&BytesReturned,
NULL,
NULL ) )
{
cout<<"读取信息成功"<<endl;
char tmp[MAX_PATH], str1[MAX_PATH], str2[MAX_PATH];
memset( tmp, 0, sizeof(tmp) );
WideCharToMultiByte( CP_ACP,0,pNotify->FileName,pNotify->FileNameLength/2,tmp,99,NULL,NULL );
strcpy( str1, tmp );
if( 0 != pNotify->NextEntryOffset )
{
PFILE_NOTIFY_INFORMATION p = (PFILE_NOTIFY_INFORMATION)((char*)pNotify+pNotify->NextEntryOffset);
memset( tmp, 0, sizeof(tmp) );
WideCharToMultiByte( CP_ACP,0,p->FileName,p->FileNameLength/2,tmp,99,NULL,NULL );
strcpy( str2, tmp );
}
obj->DealFun( (ACTION)pNotify->Action, str1, str2 );
}
else
{
cout<<"读取信息失败"<<endl;
break;
}
}
return 0;
}
};
有了这个源码(这个源码需要放到.h文件中,负责无法加载使用)以后,就是暴露接口让JAVA调用了,下面是类代码
// FuckYouToDie.cpp : Defines the entry point for the DLL application.
//
//在setting->C/C++
// 中加上_WIN32_WINNT=0x0500,WINVER=0x0500
//
#include "stdafx.h"
#include "Fuck.h"//是上面得代码得头文件
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}//默认得入口函数,不用管
extern "C" bool __declspec(dllexport) Run() {
FileSystemWatcher b;
cout<<"建立B对象"<<endl;
bool r = b.Run("d:/watch");
char c;
cin>>c;
while(c != 'q'){
cin>>c;
}
return r;
}
然后进行编译链接,就获得了对于得.dll文件,然后接下来就是用JAVA调用了
2. JAVA用JNA框架去调用
用这个框架调用C++真的是很简单,只需要两个类,一个类
是接口
import com.sun.jna.Library;
public interface DLLD extends Library {
boolean Run();
}
继承Library,然后里面得方法要和C++暴露给你得接口一一对应
另外一个就是调用类了
import com.sun.jna.Native;
public class DLLDTest {
public static void main(String[] args) {
DLLD loadLibrary = (DLLD) Native.loadLibrary(
"E:/网络试验/Tt/src/FuckYouToDie.dll", DLLD.class);
System.out.println(loadLibrary.Run());
}
}
注意看代码,JAVA数据类型,要和C++得一一对应才行,然后会找到函数,比如JAVA得int对c++得int,java得char对c++得w_char等,要注意匹配,方法名需要一致