views:

729

answers:

3

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).

+1  A: 

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.

Matthew Flaschen
And don't forget the GetLastError() to get a meaningful explanation for failed moves unlike File.renameTo()
kd304
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
Did you try out my example?
jitter
@jitter, I'm just checking it out
Jonik
Jonik, I've fixed the links to the latest version, so they should be in sync. Note that DEFAULT_OPTIONS is defined in com.sun.jna.examples.win32.W32API (https://jna.dev.java.net/javadoc/com/sun/jna/examples/win32/W32API.html#DEFAULT_OPTIONS), which Kernel32API inherits from.
Matthew Flaschen
+1  A: 

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.

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
As you don't like the NativeCall variant I did a JNA POC app too. Implementing both MoveFileA and MoveFileW
jitter
+3  A: 

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.

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
Did you check out my working example?
jitter
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
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
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