2009-06-16 16 views
7

Mentre cercavo come fare, ho trovato una vaga discussione su diverse opzioni, come JNI vs JNA, ma non molto in termini di esempi concreti.Qual è il modo più semplice per chiamare una funzione del kernel di Windows da Java?

Contesto: se Java di File.renameTo() non può fare il suo lavoro (per qualsiasi motivo; it is a little problematic), vorrei ricadere direttamente utilizzando questa funzione nativa di Windows, che è definita in kernel32.dll (from this answer):

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

Quindi, usando qualsiasi approccio, come si chiamerebbe esattamente quella funzione all'interno del codice Java? Sto cercando il modo più semplice, con la quantità minima di codice non Java o passaggi aggiuntivi (ad esempio in compilazione o distribuzione).

risposta

7

Se si utilizza JNA, è consigliabile richiamare direttamente MoveFileW - salva la necessità di fornire informazioni di configurazione per scegliere tra chiamate Unicode e ANSI.

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: Ho modificato la mia risposta dopo aver controllato il codice - mi sbagliavo sull'utilizzo char [] nella firma - è meglio utilizzare WString.

+0

Ok, ma * come * chiamare direttamente MoveFileW? Come suggerisce Matthew Flaschen, o cosa? Alcune righe di codice di esempio (come l'uso di Native.loadLibrary(), ecc.) Sarebbero apprezzate. – Jonik

+0

Hai controllato il mio esempio di lavoro? – jitter

+0

Grazie per l'esempio JNA! A proposito, come sarebbe l'interfaccia per MoveFileA (menzionato nella risposta di jitter)? Semplicemente usare "MoveFileA" invece di "MoveFileW" non andava bene. Sto davvero cercando di spostare/rinominare una directory, e non ho potuto farlo funzionare con questo - anche se non sono sicuro che sia a causa di MoveFileW o qualcos'altro. – Jonik

1

Se questo è veramente necessario (renameTo non funziona e sei sicuro che MoveFile lo farà), vorrei usare JNA. Sembra che la maggior parte del lavoro sia già stata eseguita in com.mucommander.file.util. Kernel32.java/Kernel32API.java.

+0

E non dimenticare l'oggetto GetLastError() per ottenere una spiegazione significativa per le mosse non riuscite a differenza di File.renameTo() – akarnokd

+0

I'm not/sure/MoveFile funzionerà, ma potrei provare, come rinominarePer certo non. A proposito, i due file che si collegano non si adattano perfettamente; Kernel32API.DEFAULT_OPTIONS mancante. Cosa dovresti passare come 3 ° parametro a Native.loadLibrary? Sto provando con la mappa vuota e non riesco a farlo funzionare; "ClassCastException: $ Proxy0 non può essere lanciato su com.sun.jna.Library". – Jonik

+0

Hai provato il mio esempio? – jitter

1

In base allo NativeCall library ho eseguito il seguente numero POC Application.

Si utilizza la funzione MoveFileA da kernel32.dll

Viene esempio funzionante come completo di run.bat e tutto vaso e DLL in luogo.

sposta il test.txt incluso per test2.txt


Se non ti piace la versione della libreria NativeCall ho fatto un altro POC Application in base a/resuing sulla libreria Java Native Access (JNA). Questa volta MoveFileA e MoveFileW sono implementati e dimostrati.

+0

Grazie! Usando NativeCall sono stato in grado di fare mosse veloci per grandi directory da Java (anche se non l'ho ancora provato nella situazione in cui rename spesso fallisce), e il codice per fare la chiamata è davvero semplice. Ma ora ho dei dubbi riguardo a NativeCall.file dll - sembra maldestro se hai bisogno di averlo sempre nella cartella di lavoro (se fosse all'interno di uno dei vasi, usato in modo trasparente per l'utente dell'API, incorporare la lib sarebbe più semplice) – Jonik

+0

Come non ti piace la variante NativeCall ho fatto anche un'app JNA POC. Implementazione di MoveFileA e MoveFileW – jitter

Problemi correlati