Search code examples
javaeclipsejnacreatefilekernel32

Using JNA to retrieve unique identifier for Files and Folders


Im trying to implement file synchronization in conjunction with java.nio watchevent. Though, as known and discussed in other posts, it doesnt handle renames and moves. I tried out jpathwatch which didnt really seem to cover the scenarios i needed so i try to get unique object identifiers thus being able to track these events myself.

The problem i have is that i tried implement BY_HANDLE_FILE_INFORMATION using JNA and it worked fine for files. But i get null return and error 6 when i try to access directories this way. So what im trying to ascertain is the best way to get a trackable id for both files and folders. If ive made some error, i have som issue locally or with eclipse, or if i perhaps need to look elsewhere for this information.

My testcode is as follows:

@Override
public String getUniqueFileId(Path file) {
    BY_HANDLE_FILE_INFORMATION nfo = new BY_HANDLE_FILE_INFORMATION();
    HANDLE handle = com.sun.jna.platform.win32.Kernel32.INSTANCE.CreateFile(file.toString(), 0x80000000, 0x00000001, null, 3, 0x80, null);
    if(Kernel32.INSTANCE.GetLastError() != 0) {
        LOGGER.error("Error occured for '" + file.toString() + "' (Error " + Kernel32.INSTANCE.GetLastError() + ")");
    }
    Kernel32.INSTANCE.GetFileInformationByHandle(handle, nfo);
    if(Kernel32.INSTANCE.GetLastError() != 0) {
        LOGGER.error("Error occured for '" + file.toString() + "' (Error " + Kernel32.INSTANCE.GetLastError() + ")");
    }
    String identifier = nfo.nFileIndexHigh + nfo.nFileIndexLow.toString() + Integer.toHexString(nfo.dwVolumeSerialNumber.intValue());
    com.sun.jna.platform.win32.Kernel32.INSTANCE.CloseHandle(handle); 
    return identifier;
}

And:

public interface Kernel32 extends StdCallLibrary {
final static Map<String, Object> WIN32API_OPTIONS = new HashMap<String, Object>() {
    private static final long serialVersionUID = 1L;
    {
        put(Library.OPTION_FUNCTION_MAPPER, W32APIFunctionMapper.UNICODE);
        put(Library.OPTION_TYPE_MAPPER, W32APITypeMapper.UNICODE);
    }
};

public Kernel32 INSTANCE = (Kernel32) Native.loadLibrary("Kernel32", Kernel32.class, WIN32API_OPTIONS);

public int GetLastError();

public class BY_HANDLE_FILE_INFORMATION extends Structure {
    public DWORD    dwFileAttributes;
    public FILETIME ftCreationTime;
    public FILETIME ftLastAccessTime;
    public FILETIME ftLastWriteTime;
    public DWORD    dwVolumeSerialNumber;
    public DWORD    nFileSizeHigh;
    public DWORD    nFileSizeLow;
    public DWORD    nNumberOfLinks;
    public DWORD    nFileIndexHigh;
    public DWORD    nFileIndexLow;

    public List<String> getFieldOrder() {
        return Arrays.asList(new String[] { 
            "dwFileAttributes", 
            "ftCreationTime", 
            "ftLastAccessTime", 
            "ftLastWriteTime",
            "dwVolumeSerialNumber",
            "nFileSizeHigh",
            "nFileSizeLow",
            "nNumberOfLinks",
            "nFileIndexHigh",
            "nFileIndexLow"
        });
    };  
}; 

boolean GetFileInformationByHandle(HANDLE hFile, BY_HANDLE_FILE_INFORMATION lpFileInformation);

}

If someone spots any problem or could give me a push in the right direction i would be greatful.

Im using Wondows 10 / Eclipse but had same issue on Windows 8.

UPDATE: realized i called the getlasterror to late, but updated the code and it gives med first error 5, then error 6. Error 5 indicate permission issue, but full permission for all users and running eclipse in administrator mode unfortunately didnt help. All files work fine, no folder work.


Solution

  • Found the problem, which was with the dwFlagsAndAttributes in createFile.

    Using these parameters worked for retrieveing identifiers for both files and folders.

    private final int GENERIC_READ = 0x80000000;
    private final int FILE_SHARE_READ = 0x00000001;
    private WinBase.SECURITY_ATTRIBUTES SECURITY_ATTRIBUTES = null;
    private final int OPEN_EXISTING = 3;
    private final int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
    

    And when calling createFile

    HANDLE handle = com.sun.jna.platform.win32.Kernel32.INSTANCE.CreateFile(file.toString(), GENERIC_READ, FILE_SHARE_READ, SECURITY_ATTRIBUTES, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, null);