6

I'm looking for a way to get a unique file id in a Java application, and came across this:

Unique file identifier in windows

Now, I tried the answer supplied by Ashley Henderson himself (the one that asked the question), and that worked fine in C#. But I need to do this in Java, in order to have the app work across platforms.

Is there any way to port this to Java, or get to the same id some other way?

EDIT:

I almost got it working now, using the solution by eee, only I need it to be in a library, and when I compile it as a library I get an error, even though everything is working fine in a test application with everything included. But with a separate library that I try to import (no compiler errors) I get this runtime error:

debug:
Exception in thread "main" java.lang.NoClassDefFoundError: com/sun/jna/Structure
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:791)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:449)
    at java.net.URLClassLoader.access$100(URLClassLoader.java:71)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
    at winfileid.FileId.getFileId(FileId.java:37)
    at testfileid.TestFileId.main(TestFileId.java:19)
Caused by: java.lang.ClassNotFoundException: com.sun.jna.Structure
    at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
    ... 14 more
Java Result: 1
BUILD SUCCESSFUL (total time: 0 seconds)

I've included the jna.jar and platform.jar in the library when I compiled it... Please note again that I'm very new to Java, but what am I doing wrong?

7
  • 1
    I suspect you would have to use JNI to get it. Why would you want this information anyways? Commented Nov 29, 2011 at 10:27
  • whats the use case - why do you need the unique id? Commented Nov 29, 2011 at 10:37
  • The use case is to keep track of files when they are moved or renamed, to preserve links from other files. Commented Nov 29, 2011 at 12:56
  • @AndersSvensson Since your latest edit uses my solution, it shall state like that in your edit. Otherwise, anyone can get confused with it. About the exception error, did you compile it using Eclipse/Netbean IDE or compile it from the command-line? It will help in solving the problem... Commented Nov 30, 2011 at 15:19
  • Right, sorry, edited it to indicate the edit is based on your solution. I compiled with NetBeans. Commented Nov 30, 2011 at 21:32

4 Answers 4

4

Using JNA version 3.3.0:

Kernel32.INSTANCE.GetFileInformationByHandle Test case:

package win.test;

import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.WinBase;
import com.sun.jna.platform.win32.WinBase.FILETIME;
import com.sun.jna.platform.win32.WinNT.HANDLE;

import win.test.Kernel32.BY_HANDLE_FILE_INFORMATION;


public class FileTest {

    /**
     * @param args
     */
    public static void main(String[] args) {
        //http://msdn.microsoft.com/en-us/library/windows/desktop/aa363858%28v=vs.85%29.aspx
        final int FILE_SHARE_READ = (0x00000001);
        //final int FILE_SHARE_WRITE = (0x00000002);
        //final int FILE_SHARE_DELETE = (0x00000004);
        final int OPEN_EXISTING = (3);
        final int GENERIC_READ = (0x80000000);
        //final int GENERIC_WRITE = (0x40000000);
        //final int FILE_FLAG_NO_BUFFERING = (0x20000000);
        //final int FILE_FLAG_WRITE_THROUGH = (0x80000000);
        //final int FILE_READ_ATTRIBUTES = (0x0080);
        //final int FILE_WRITE_ATTRIBUTES = (0x0100);
        //final int ERROR_INSUFFICIENT_BUFFER = (122);
        final int FILE_ATTRIBUTE_ARCHIVE = (0x20);

        WinBase.SECURITY_ATTRIBUTES attr = null;
        BY_HANDLE_FILE_INFORMATION lpFileInformation = new BY_HANDLE_FILE_INFORMATION();
        HANDLE hFile = null;

        hFile = Kernel32.INSTANCE.CreateFile(args[0], GENERIC_READ, FILE_SHARE_READ, attr, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, null);

        System.out.println("CreateFile last error:" + Kernel32.INSTANCE.GetLastError());

        //if (hFile. != -1)
        {

            win.test.Kernel32.INSTANCE.GetFileInformationByHandle(hFile, lpFileInformation);

            System.out.println("CREATION TIME: " + FILETIME.filetimeToDate(lpFileInformation.ftCreationTime.dwHighDateTime, lpFileInformation.ftCreationTime.dwLowDateTime));

            System.out.println("VOLUME SERIAL NO.: "  + Integer.toHexString(lpFileInformation.dwVolumeSerialNumber.intValue()));

            System.out.println("FILE INDEX HIGH: "  + lpFileInformation.nFileIndexHigh);
            System.out.println("FILE INDEX LOW: "  + lpFileInformation.nFileIndexLow);


            System.out.println("GetFileInformationByHandle last error:" + Kernel32.INSTANCE.GetLastError());
        }

        Kernel32.INSTANCE.CloseHandle(hFile);

        System.out.println("CloseHandle last error:" + Kernel32.INSTANCE.GetLastError());

    }

}

Sample output:

CreateFile last error:0
CREATION TIME: Tue Nov 29 22:24:04 SGT 2011
VOLUME SERIAL NO.: 900c0655
FILE INDEX HIGH: 1769472
FILE INDEX LOW: 286306
GetFileInformationByHandle last error:0
CloseHandle last error:0

Kernel32 JNA instance class:

package win.test;

import java.util.HashMap;
import java.util.Map;

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.WString;
import com.sun.jna.platform.win32.WinBase.FILETIME;
import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.platform.win32.WinNT.HANDLE;
import com.sun.jna.win32.StdCallLibrary;
import com.sun.jna.win32.W32APIFunctionMapper;
import com.sun.jna.win32.W32APITypeMapper;

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();

    /**
    typedef struct _BY_HANDLE_FILE_INFORMATION {
          DWORD    dwFileAttributes;
          FILETIME ftCreationTime;
          FILETIME ftLastAccessTime;
          FILETIME ftLastWriteTime;
          DWORD    dwVolumeSerialNumber;
          DWORD    nFileSizeHigh;
          DWORD    nFileSizeLow;
          DWORD    nNumberOfLinks;
          DWORD    nFileIndexHigh;
          DWORD    nFileIndexLow;
        } BY_HANDLE_FILE_INFORMATION, *PBY_HANDLE_FILE_INFORMATION;
     */

    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 static class ByReference extends BY_HANDLE_FILE_INFORMATION implements Structure.ByReference {

        };
        public static class ByValue extends BY_HANDLE_FILE_INFORMATION implements Structure.ByValue {

        };        
    }; 

    /**
    BOOL WINAPI GetFileInformationByHandle(
              __in   HANDLE hFile,
              __out  LPBY_HANDLE_FILE_INFORMATION lpFileInformation
            );
    */
    boolean GetFileInformationByHandle(
              HANDLE hFile,
              BY_HANDLE_FILE_INFORMATION lpFileInformation
            );
}
Sign up to request clarification or add additional context in comments.

9 Comments

Wow, thanks for that detailed answer...! As I mentioned, though, this is a bit above my head, and I'm completely new to Java to boot, having only programmed in C# before. But I have to give this a try now that you provided such a detailed example... But I don't understand where you specify the file to be checked? It's probably in there, but it's hard for me to decode this. Also, what is the FILE INDEX HIGH vs FILE INDEX LOW? In the example I used (from the link) there was only one index value, so which should I use here to identify the file?
BTW, since JNA seems to be for accessing native code in dlls, there isn't a way to do this simply by compiling the code in the example link (which I already have in C# working) into a dll, and then call that with JNA? If it is I would really appreciate a simple example... I was just hoping that might be simpler.
@AndersSvensson yes, you can do that but it is a little bit difficult since you start with C# DLL rather than C/C++ DLL which the latter is the preferred DLL calling convention in JNA. You need to wrap your C# DLL into C/C++ DLL first before you can use it with JNA. That's what I can understand with JNA.
@AndersSvensson Oh, args[0] is from the program command argument which you need to pass into the program; for example: FileTest "test.txt" where "test.txt" will be the value of args[0] of FileTest program
@AndersSvensson To run the program successfully, you need to download JNA platform library (jna.jar and platform.jar), add them as referenced libraries and set the system property -Djna.library.path=<relative folder where the jars can be found> download here
|
1

In Java you would need JNI, native C compilation, both for Windows (using the C# code), and for Unix/Linux (using the inode of a file). Honestly I do not think this is very safe.

2 Comments

+1 The filesystem in use is also important, further making portability more difficult -- good luck on FAT, for instance :)
Yes, I looked briefly into that, and was hoping there would be a simpler way. But I was hoping not to have to compile this and use it as is in Java, but rather if there was a way to rewrite it in Java to get to the same id... Getting a file id in *nix is much simpler, but I can't find a way to do it for Windows except this example in C#. BTW, FAT is not an issue. It's going to be NTFS in Windows, and Mac OS X is the other OS I need to consider.
0

Can't you use the file path as its unique id ?!

Full file path is unique enough ...

2 Comments

Yes, it's unique - but it's not preserved when you move a file to another directory, which seems to be the intention behind the file ID.
Right, the point is to be able to move the files around and rename them freely, so file paths are actually what I need to find out after such moves or renames.
0

There can be multiple paths to the same file through mounted volumes and various types of links/reparse points. The unique file id API is the only way I know of to determine whether the several paths to a seemingly identical file refer to unique files or the same file through different paths.

That being said, I agree that the only java generic way would involve C or C++ code and JNI. Done right though this could probably provide solutions that work on Linux and Windows using different back-end libraries with a common presentation to the hosting java code.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.