What is the easiest way to call a Windows kernel function from Java?

Go To StackoverFlow.com

7

While searching for how to do this, I found some vague discussion about different options, like JNI vs JNA, but not much in the way of concrete examples.

Context: if Java's File.renameTo() cannot do it's job (for whatever reason; it is a little problematic), I'd like to fall back to directly using this native Windows function, which is defined in kernel32.dll (from this answer):

BOOL WINAPI MoveFile(
  __in  LPCTSTR lpExistingFileName,
  __in  LPCTSTR lpNewFileName
);

So, using whatever approach, how exactly would you call that function from within Java code? I'm looking for the simplest way, with the minimum amount of non-Java code or extra steps (e.g. in compilation or deployment).

2009-06-16 10:40
by Jonik


7

If you go with JNA, consider invoking MoveFileW directly - it saves having to provide configuration info to choose between Unicode and ANSI calls.

import java.io.*;
import com.sun.jna.*;

public class Ren {

  static interface Kernel32 extends Library {
    public static Kernel32 INSTANCE = (Kernel32) Native
        .loadLibrary("Kernel32", Kernel32.class);

    public static int FORMAT_MESSAGE_FROM_SYSTEM = 4096;
    public static int FORMAT_MESSAGE_IGNORE_INSERTS = 512;

    public boolean MoveFileW(WString lpExistingFileName,
        WString lpNewFileName);

    public int GetLastError();

    public int FormatMessageW(int dwFlags,
        Pointer lpSource, int dwMessageId,
        int dwLanguageId, char[] lpBuffer, int nSize,
        Pointer Arguments);
  }

  public static String getLastError() {
    int dwMessageId = Kernel32.INSTANCE.GetLastError();
    char[] lpBuffer = new char[1024];
    int lenW = Kernel32.INSTANCE.FormatMessageW(
        Kernel32.FORMAT_MESSAGE_FROM_SYSTEM
            | Kernel32.FORMAT_MESSAGE_IGNORE_INSERTS, null,
        dwMessageId, 0, lpBuffer, lpBuffer.length, null);
    return new String(lpBuffer, 0, lenW);
  }

  public static void main(String[] args) throws IOException {
    String from = ".\\from.txt";
    String to = ".\\to.txt";
    new FileOutputStream(from).close();
    if (!Kernel32.INSTANCE.MoveFileW(new WString(from),
        new WString(to))) {
      throw new IOException(getLastError());
    }
  }
}

EDIT: I've edited my answer after checking the code - I was mistaken about using char[] in the signature - it is better to use WString.

2009-06-16 11:50
by McDowell
Ok, but how to call MoveFileW directly then? Like Matthew Flaschen suggests, or what? A few lines of example code (like how to use Native.loadLibrary() etc) would be appreciated - Jonik 2009-06-16 12:08
Did you check out my working example - jitter 2009-06-16 12:16
Thanks for the JNA example! Btw, how would the interface look for MoveFileA (mentioned in jitter's answer)? Simply using "MoveFileA" instead of "MoveFileW" was no good. I'm really trying to move/rename a directory, and couldn't get that working with this - though I'm not sure if that is because of MoveFileW or something else - Jonik 2009-06-16 14:28
The function could be defined as MoveFileA(String,String), but doing that restricts the filenames you can work with. You might not be able to use it on all files on your system, esp. if you're using NTFS. I tested the above code on a directory, so your problem is probably something else - McDowell 2009-06-16 15:09
I'm accepting this one as I think the solution, with example code, should be right here on SO. (I'll change it, of course, if a better/simpler way is posted. : - Jonik 2009-06-17 12:47


1

If this is really necessary (renameTo doesn't work and you're sure MoveFile will), I would use JNA. It looks like most of the work is already done in com.mucommander.file.util.Kernel32.java/Kernel32API.java.

2009-06-16 10:47
by Matthew Flaschen
And don't forget the GetLastError() to get a meaningful explanation for failed moves unlike File.renameTo( - akarnokd 2009-06-16 10:55
I'm not /sure/ MoveFile will work, but I could try, as renameTo sure doesn't. Btw, the two files you link to don't fit together perfectly; Kernel32API.DEFAULT_OPTIONS is missing. What should you pass as 3rd parameter to Native.loadLibrary? I'm trying with empty map and can't get it to work; "ClassCastException: $Proxy0 cannot be cast to com.sun.jna.Library" occurs - Jonik 2009-06-16 12:02
Did you try out my example - jitter 2009-06-16 12:16
@jitter, I'm just checking it ou - Jonik 2009-06-16 12:18
Jonik, I've fixed the links to the latest version, so they should be in sync. Note that DEFAULTOPTIONS is defined in com.sun.jna.examples.win32.W32API (https://jna.dev.java.net/javadoc/com/sun/jna/examples/win32/W32API.html#DEFAULTOPTIONS), which Kernel32API inherits from - Matthew Flaschen 2009-06-16 15:06


1

Based on the NativeCall library I did the following POC Application.

It uses the MoveFileA function from kernel32.dll

It comes as complete working sample with a run.bat and all jar and dlls in place.

It moves the included test.txt to test2.txt


If you don't like the NativeCall library version I did another POC Application based on/resuing on the Java Native Access (JNA) library. This time MoveFileA and MoveFileW are implemented and demonstrated.

2009-06-16 11:37
by jitter
Thanks! Using NativeCall I was able to do quick move for large directories from Java (although I haven't tried it yet in the situation where renameTo often failed), and the code for doing the call indeed is simple. But I have some doubts now about the NativeCall.dll file - it seems clumsy if you need to always have it in the working dir (if it was inside one of the jars, used transparently to the user of the API, embedding the lib would be easier - Jonik 2009-06-16 13:23
As you don't like the NativeCall variant I did a JNA POC app too. Implementing both MoveFileA and MoveFile - jitter 2009-06-16 16:12
Ads