JNA to access ReadDirectoryChangesW to notify for file sytem changes

12 views
Skip to first unread message

ram sharma

unread,
Apr 2, 2024, 3:35:55 AMApr 2
to Java Native Access
this is my JNA code here:
import com.sun.jna.*;
import com.sun.jna.platform.win32.*;
import com.sun.jna.ptr.*;

import java.util.Arrays;
import java.util.List;

public class DirectoryMonitor {

public interface Kernel32 extends com.sun.jna.platform.win32.Kernel32 {
Kernel32 INSTANCE = Native.load("kernel32", Kernel32.class);

WinNT.HANDLE CreateFileW(String lpFileName, int dwDesiredAccess, int dwShareMode, WinBase.SECURITY_ATTRIBUTES lpSecurityAttributes,
int dwCreationDisposition, int dwFlagsAndAttributes, WinNT.HANDLE hTemplateFile);

boolean ReadDirectoryChangesW(WinNT.HANDLE hDirectory, FILE_NOTIFY_INFORMATION lpBuffer, int nBufferLength, boolean bWatchSubtree,
int dwNotifyFilter, IntByReference lpBytesReturned, WinBase.OVERLAPPED lpOverlapped,
WinNT.OVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);

boolean CloseHandle(WinNT.HANDLE hObject);
}

public static class FILE_NOTIFY_INFORMATION extends Structure {
public int NextEntryOffset;
public int Action;
public int FileNameLength;
public char[] FileName;

@Override
protected List<String> getFieldOrder() {
return Arrays.asList("NextEntryOffset", "Action", "FileNameLength", "FileName");
}
}

public static void main(String[] args) {
String directory = "C:\\temp\\"; // Directory to monitor
int bufferSize = 4096; // Size of the buffer for directory changes

WinNT.HANDLE dirHandle = Kernel32.INSTANCE.CreateFileW(
directory,
WinNT.FILE_LIST_DIRECTORY,
WinNT.FILE_SHARE_READ | WinNT.FILE_SHARE_WRITE | WinNT.FILE_SHARE_DELETE,
null,
WinNT.OPEN_EXISTING,
WinNT.FILE_FLAG_BACKUP_SEMANTICS,
null
);

if (dirHandle == WinNT.INVALID_HANDLE_VALUE) {
System.err.println("Failed to open directory: Error code " + Kernel32.INSTANCE.GetLastError());
return;
}

FILE_NOTIFY_INFORMATION notification = new FILE_NOTIFY_INFORMATION();
IntByReference bytesReturned = new IntByReference();
WinBase.OVERLAPPED overlapped = new WinBase.OVERLAPPED();

boolean result = Kernel32.INSTANCE.ReadDirectoryChangesW(
dirHandle,
notification,
notification.size(),
true,
WinNT.FILE_NOTIFY_CHANGE_LAST_WRITE,
bytesReturned,
overlapped,
null,
null // Pass null for lpCompletionRoutine
);

if (!result) {
System.err.println("ReadDirectoryChangesW failed: Error code " + Kernel32.INSTANCE.GetLastError());
Kernel32.INSTANCE.CloseHandle(dirHandle);
return;
}

try {
while (true) {
if (Kernel32.INSTANCE.ReadDirectoryChangesW(
dirHandle,
notification,
notification.size(),
true,
WinNT.FILE_NOTIFY_CHANGE_LAST_WRITE,
bytesReturned,
overlapped,
null,
null // Pass null for lpCompletionRoutine
)) {
int offset = 0;
while (offset < bytesReturned.getValue()) {
notification.read();
String fileName = new String(notification.FileName, 0, notification.FileNameLength / 2);
switch (notification.Action) {
case WinNT.FILE_ACTION_ADDED:
System.out.println("File added: " + fileName);
break;
case WinNT.FILE_ACTION_REMOVED:
System.out.println("File removed: " + fileName);
break;
case WinNT.FILE_ACTION_MODIFIED:
System.out.println("File modified: " + fileName);
break;
case WinNT.FILE_ACTION_RENAMED_OLD_NAME:
System.out.println("File renamed (old name): " + fileName);
break;
case WinNT.FILE_ACTION_RENAMED_NEW_NAME:
System.out.println("File renamed (new name): " + fileName);
break;
default:
System.out.println("Unknown action for file: " + fileName);
break;
}
offset += notification.NextEntryOffset;
}
} else {
System.err.println("ReadDirectoryChangesW failed: Error code " + Kernel32.INSTANCE.GetLastError());
}

try {
Thread.sleep(1000); // Sleep for 1 second before checking for changes again
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} finally {
Kernel32.INSTANCE.CloseHandle(dirHandle);
}
}
}



and this is my c++ code to do the same
#include <windows.h>
#include <iostream>
#include<string>

int main() {
    HANDLE dirHandle;
    DWORD dwBytes;
    CHAR buffer[1024];
    BOOL result;

    // Directory to monitor
    LPCWSTR directory = L"D:\\";

    // Open handle to the directory
    dirHandle = CreateFileW(
        directory,
        FILE_LIST_DIRECTORY,
        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
        NULL,
        OPEN_EXISTING,
        FILE_FLAG_BACKUP_SEMANTICS,
        NULL
    );

    if (dirHandle == INVALID_HANDLE_VALUE) {
        std::cerr << "Failed to open directory" << std::endl;
        return 1;
    }

    // Monitor changes in the directory
    while (true) {
        result = ReadDirectoryChangesW(
            dirHandle,
            buffer,
            sizeof(buffer),
            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_CREATION |
            FILE_NOTIFY_CHANGE_SECURITY,
            &dwBytes,
            NULL,
            NULL
        );

        if (!result) {
            std::cerr << "ReadDirectoryChangesW failed" << std::endl;
            break;
        }

        // Parse the buffer to get file change information
        FILE_NOTIFY_INFORMATION* fileInfo = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(buffer);
        while (fileInfo) {
            std::wstring fileName(fileInfo->FileName, fileInfo->FileNameLength / sizeof(WCHAR));
            fileName.erase(std::remove(fileName.begin(), fileName.end(), L'\n'), fileName.end());
            std::wstring filePath = std::wstring(directory) + L"\\" + fileName;
            DWORD fileSize=0;
            FILETIME fileModifiedTime;
            WIN32_FILE_ATTRIBUTE_DATA fileAttributes;
            if (GetFileAttributesEx(filePath.c_str(), GetFileExInfoStandard, &fileAttributes))
            {
                 fileSize = fileAttributes.nFileSizeLow;
                 fileModifiedTime = fileAttributes.ftLastWriteTime;                
            }
            else
            {
                std::wcout << L"Error retrieving file attributes for " << fileName << std::endl;
            }
            FILETIME localFileTime;
            FileTimeToLocalFileTime(&fileModifiedTime, &localFileTime);
            SYSTEMTIME systemTime;
            FileTimeToSystemTime(&localFileTime, &systemTime);
            std::wstring fileModificationTimeString = std::to_wstring(systemTime.wYear) + L"/" +
                std::to_wstring(systemTime.wMonth) + L"/" +
                std::to_wstring(systemTime.wDay) + L" " +
                std::to_wstring(systemTime.wHour) + L":" +
                std::to_wstring(systemTime.wMinute) + L":" +
                std::to_wstring(systemTime.wSecond);

            size_t lastBackslashIndex = fileName.find_last_of(L'\\');
            if (lastBackslashIndex != std::wstring::npos) {
                fileName = fileName.substr(lastBackslashIndex + 1);
            }
            switch (fileInfo->Action) {
            case FILE_ACTION_ADDED:
                std::wcout << L"File " << fileName<< L" added. " <<"  filepath: "<<filePath<<L"  fileSize: "<<fileSize<<L"  fileModificationTime: "<< fileModificationTimeString <<std::endl;

                break;
            case FILE_ACTION_REMOVED:
                std::wcout << L"File " << fileName<< L" removed." << "   filepath: " << filePath << L"  fileSize: " << fileSize << L"  fileModificationTime: " << fileModificationTimeString << std::endl;
                break;
           /* case FILE_ACTION_MODIFIED:
                std::wcout << L"File " << fileName<< L" modified." << "   filepath: " << filePath << L"  fileSize: " << fileSize << L"  fileModificationTime: " << fileModificationTimeString << std::endl;
                break;*/
            case FILE_ACTION_RENAMED_OLD_NAME:
                std::wcout << L"File " << fileName<< L" renamed (old name)." << "   filepath: " << filePath << L"  fileSize: " << fileSize << L" fileModificationTime: " << fileModificationTimeString << std::endl;
                break;
            case FILE_ACTION_RENAMED_NEW_NAME:
                std::wcout << L"File " << fileName<< L" renamed (new name)." << "   filepath: " << filePath << L"  fileSize: " << fileSize << L"  fileModificationTime: " << fileModificationTimeString << std::endl;
                break;
            default:
                std::wcout << L"Unknown action for file "<< fileName<< L"." << "   filepath: " << filePath << L"  fileSize: " << fileSize << L"  fileModificationTime: " << fileModificationTimeString << std::endl;
                break;
            }
            if (fileInfo->NextEntryOffset == 0)
                break;
            fileInfo = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(reinterpret_cast<LPBYTE>(fileInfo) + fileInfo->NextEntryOffset);
        }
    }

    // Close the directory handle
    CloseHandle(dirHandle);

    return 0;
}



the c++ code works fine to print when a file is deleted, moved, renamed... just want to do the same using JNA, the JNA code don't seem to work, some errors here
errors are:
'ReadDirectoryChangesW(com.sun.jna.platform.win32.WinNT.HANDLE, com.sun.jna.platform.win32.WinNT.FILE_NOTIFY_INFORMATION, int, boolean, int, com.sun.jna.ptr.IntByReference, com.sun.jna.platform.win32.WinBase.OVERLAPPED, ...)' in 'DirectoryMonitor.Kernel32' cannot be applied to '(com.sun.jna.platform.win32.WinNT.HANDLE, DirectoryMonitor.FILE_NOTIFY_INFORMATION, int, boolean, int, com.sun.jna.ptr.IntByReference, com.sun.jna.platform.win32.WinBase.OVERLAPPED, null, null)'

'ReadDirectoryChangesW(com.sun.jna.platform.win32.WinNT.HANDLE, com.sun.jna.platform.win32.WinNT.FILE_NOTIFY_INFORMATION, int, boolean, int, com.sun.jna.ptr.IntByReference, com.sun.jna.platform.win32.WinBase.OVERLAPPED, ...)' in 'DirectoryMonitor.Kernel32' cannot be applied to '(com.sun.jna.platform.win32.WinNT.HANDLE, DirectoryMonitor.FILE_NOTIFY_INFORMATION, int, boolean, int, com.sun.jna.ptr.IntByReference, com.sun.jna.platform.win32.WinBase.OVERLAPPED, null, null)'






Matthias Bläsing

unread,
Apr 2, 2024, 2:25:34 PMApr 2
to jna-...@googlegroups.com
Hi,

actually the errors are self explaining (I just add some wrapping):

Am Dienstag, dem 02.04.2024 um 00:35 -0700 schrieb ram sharma:
> 'ReadDirectoryChangesW(com.sun.jna.platform.win32.WinNT.HANDLE, com.sun.jna.platform.win32.WinNT.FILE_NOTIFY_INFORMATION, int, boolean, int, com.sun.jna.ptr.IntByReference, com.sun.jna.platform.win32.WinBase.OVERLAPPED, ...)' in 'DirectoryMonitor.Kernel32' cannot be applied to 
> '(com.sun.jna.platform.win32.WinNT.HANDLE, DirectoryMonitor.FILE_NOTIFY_INFORMATION, int, boolean, int, com.sun.jna.ptr.IntByReference, com.sun.jna.platform.win32.WinBase.OVERLAPPED, null, null)'
>

and looking at your code I have serious problems to understand why you
did what you did.

You are using jna-platform. That library has bindings for
ReadDirectoryChangesW and CreateFileW (accessible via
com.sun.jna.platform.win32.Kernel32.INSTANCE) your new definitions
inside DirectoryMonitor are bogus. Remove your definitions and have a
look here:

https://github.com/java-native-access/jna/blob/master/contrib/platform/src/com/sun/jna/platform/win32/W32FileMonitor.java

Greetings

Matthias
Reply all
Reply to author
Forward
0 new messages