1+ // ******************************************************************************
2+ // ** FileSystemWatcherNative Class
3+ // ******************************************************************************
4+ /* *
5+ * JNI code used to monitor changes made to a directory.
6+ *
7+ ******************************************************************************/
8+
9+ // This file needs to be compiled with _UNICODE flag, since java string uses unicode.
10+
11+ #include " stdafx.h"
12+ #include " FileSystemWatcherNative.h"
13+ #include < sstream>
14+ #include < ctime>
15+ using namespace std ;
16+
17+
18+ BOOL bWatchSubtree; // flag used to set whether to watch subdirectories
19+ DWORD dwNotifyFilter; // notification filter
20+ HANDLE hDirectory; // handle to the directory being monitored
21+ wstring sDirectory ; // a string representing the path to the directory
22+
23+ size_t nBufSize = 32 *1024 ;
24+ FILE_NOTIFY_INFORMATION* pBuffer = (FILE_NOTIFY_INFORMATION*)calloc(1 , nBufSize);
25+ FILE_NOTIFY_INFORMATION* pBufferCurrent;
26+
27+
28+ // **************************************************************************
29+ // ** FindFirstChangeNotification
30+ // **************************************************************************
31+ /* *
32+ * Class: FileSystemWatcherNative
33+ * Method: FindFirstChangeNotification
34+ * Signature: (Ljava/lang/String;ZI)J
35+ */
36+ JNIEXPORT jlong JNICALL Java_javaxt_io_FileSystemWatcherNative_FindFirstChangeNotification
37+ (JNIEnv* env, jclass, jstring filename, jboolean javaWatchSubtree, jint javaNotifyFilter)
38+ {
39+
40+ // Convert jstring to wstring
41+ const jchar *_filename = env->GetStringChars (filename, 0 );
42+ jsize len = env->GetStringLength (filename);
43+ sDirectory .assign (_filename, _filename + len);
44+ env->ReleaseStringChars (filename, _filename);
45+
46+
47+
48+ // Update Inputs
49+ bWatchSubtree = (BOOL)javaWatchSubtree;
50+ dwNotifyFilter = // (DWORD)javaNotifyFilter;
51+ FILE_NOTIFY_CHANGE_LAST_WRITE| // Triggered when a file or directory has been modified
52+ FILE_NOTIFY_CHANGE_DIR_NAME| // Triggered when a directory has been created or deleted
53+ FILE_NOTIFY_CHANGE_FILE_NAME; // Triggered when a file has been created or deleted
54+
55+
56+ // Call FindFirstChangeNotification
57+ HANDLE handle = FindFirstChangeNotificationW (sDirectory .c_str (), bWatchSubtree, dwNotifyFilter);
58+ if (handle == INVALID_HANDLE_VALUE || handle == (HANDLE)ERROR_INVALID_FUNCTION){
59+ DWORD errorCode = GetLastError ();
60+ stringstream ss;
61+ ss << " FindFirstChangeNotification failed. Error Code: " << errorCode;
62+ const char * msg = (const char *)( ss.str ().c_str ());
63+ jclass exceptionClass = env->FindClass (" java/lang/Exception" );
64+ env->ThrowNew (exceptionClass, msg );
65+ }
66+
67+
68+ // Create File Handle
69+ hDirectory = CreateFileW (
70+ sDirectory .c_str (), // pointer to the directory
71+ GENERIC_READ, // access (read/write) mode
72+ FILE_SHARE_READ|
73+ FILE_SHARE_WRITE|
74+ FILE_SHARE_DELETE, // share mode
75+ NULL , // security descriptor
76+ OPEN_EXISTING, // how to create
77+ FILE_FLAG_BACKUP_SEMANTICS, // file attributes
78+ NULL // file with attributes to copy
79+ );
80+
81+
82+ // Return FindFirstChangeNotification Handle
83+ return (jlong)handle;
84+ }
85+
86+
87+ // **************************************************************************
88+ // ** FindNextChangeNotification
89+ // **************************************************************************
90+ /* *
91+ * Class: FileSystemWatcherNative
92+ * Method: FindNextChangeNotification
93+ * Signature: (J)V
94+ */
95+ JNIEXPORT void JNICALL Java_javaxt_io_FileSystemWatcherNative_FindNextChangeNotification
96+ (JNIEnv *javaEnv, jclass, jlong handle)
97+ {
98+ if (!FindNextChangeNotification ((HANDLE) handle)){
99+ DWORD errorCode = GetLastError ();
100+ stringstream ss;
101+ ss << " FindNextChangeNotification failed. Error Code: " << errorCode;
102+ const char * msg = (const char *)( ss.str ().c_str ());
103+ jclass exceptionClass = javaEnv->FindClass (" java/lang/Exception" );
104+ javaEnv->ThrowNew (exceptionClass, msg);
105+ }
106+ }
107+
108+
109+ // **************************************************************************
110+ // ** FindCloseChangeNotification
111+ // **************************************************************************
112+ /* *
113+ * Class: FileSystemWatcherNative
114+ * Method: FindCloseChangeNotification
115+ * Signature: (J)V
116+ */
117+ JNIEXPORT void JNICALL Java_javaxt_io_FileSystemWatcherNative_FindCloseChangeNotification
118+ (JNIEnv *javaEnv, jclass, jlong handle)
119+ {
120+
121+ if (!FindCloseChangeNotification ((HANDLE) handle)){
122+ DWORD errorCode = GetLastError ();
123+ stringstream ss;
124+ ss << " FindCloseChangeNotification failed. Error Code: " << errorCode;
125+ const char * msg = (const char *)( ss.str ().c_str ());
126+ jclass exceptionClass = javaEnv->FindClass (" java/lang/Exception" );
127+ javaEnv->ThrowNew (exceptionClass, msg);
128+ }
129+
130+
131+ // Close File Handle
132+ if (hDirectory!=NULL ){
133+ CloseHandle (hDirectory);
134+ }
135+ }
136+
137+
138+ // **************************************************************************
139+ // ** WaitForSingleObject
140+ // **************************************************************************
141+ /* *
142+ * Class: FileSystemWatcherNative
143+ * Method: WaitForSingleObject
144+ * Signature: (JI)I
145+ */
146+ JNIEXPORT jint JNICALL Java_javaxt_io_FileSystemWatcherNative_WaitForSingleObject
147+ (JNIEnv *javaEnv , jclass, jlong hWaitHandle, jint waitTimeoutMillis)
148+ {
149+ return WaitForSingleObject ((HANDLE) hWaitHandle, (DWORD) waitTimeoutMillis);
150+ }
151+
152+
153+ std::wstring s2ws (const std::string& s){
154+ int len;
155+ int slength = (int )s.length () + 1 ;
156+ len = MultiByteToWideChar (CP_ACP, 0 , s.c_str (), slength, 0 , 0 );
157+ wchar_t * buf = new wchar_t [len];
158+ MultiByteToWideChar (CP_ACP, 0 , s.c_str (), slength, buf, len);
159+ std::wstring r (buf);
160+ delete[] buf;
161+ return r;
162+ }
163+
164+
165+ // **************************************************************************
166+ // ** ReadDirectoryChangesW
167+ // **************************************************************************
168+ /* *
169+ * Class: FileSystemWatcherNative
170+ * Method: ReadDirectoryChangesW
171+ * Signature: (Ljava/lang/String;ZI)J
172+ */
173+ JNIEXPORT jstring JNICALL Java_javaxt_io_FileSystemWatcherNative_ReadDirectoryChangesW
174+ (JNIEnv *env, jclass)
175+ {
176+
177+ wstringstream ss;
178+ DWORD BytesReturned;
179+
180+ if (ReadDirectoryChangesW (
181+ hDirectory, // handle to directory
182+ pBuffer, // read results buffer
183+ nBufSize, // length of buffer
184+ bWatchSubtree, // WatchSubtree
185+ dwNotifyFilter, // notification filter
186+ &BytesReturned, // bytes returned
187+ NULL , // overlapped buffer
188+ NULL // completion routine
189+ )){
190+
191+ // Iterate through the file changes
192+ pBufferCurrent = pBuffer;
193+ while (pBufferCurrent){
194+
195+ // Get current timestamp
196+ time_t d = time (NULL );
197+ string date ( ctime (&d) );
198+ date.erase (date.find_last_not_of (" \t\n " )+1 ); // right trim
199+
200+
201+ // Get action
202+ wstring action = L" " ;
203+ switch (pBufferCurrent->Action ){
204+ case FILE_ACTION_ADDED: action = L" Create" ; break ;
205+ case FILE_ACTION_REMOVED: action = L" Delete" ; break ;
206+ case FILE_ACTION_MODIFIED: action = L" Modify" ; break ;
207+ case FILE_ACTION_RENAMED_OLD_NAME: action = L" Rename" ; break ;
208+ case FILE_ACTION_RENAMED_NEW_NAME: action = L" Renam2" ; break ;
209+ }
210+
211+
212+ // Get filename
213+ WCHAR* _filename = pBufferCurrent->FileName ;
214+ int len = (int )pBufferCurrent->FileNameLength /2 ;
215+ wstring filename;
216+ filename.assign (_filename, _filename + len);
217+
218+
219+ // Join date, action, and filename into 1 event string
220+ ss << L" [" << s2ws (date) << L" ] " << action << L" " << sDirectory << filename << L" \n " ;
221+
222+
223+ // Update pBufferCurrent
224+ if (pBufferCurrent->NextEntryOffset )
225+ pBufferCurrent = (FILE_NOTIFY_INFORMATION*)(((BYTE*)pBufferCurrent) + pBufferCurrent->NextEntryOffset );
226+ else
227+ pBufferCurrent = NULL ;
228+
229+
230+ } // end while pBufferCurrent
231+ }
232+
233+ wstring event = ss.str ();
234+ int len = event.size ();
235+ jchar* raw = new jchar[len];
236+ memcpy (raw, event.c_str (), len*sizeof (wchar_t ));
237+ jstring result = env->NewString (raw, len);
238+ delete[] raw;
239+ return result;
240+ }
0 commit comments