Native Windows-FileChooser mit JNative

Swing-FileChooser

Der Swing-FileChooser hat selbst in der Version 6 von Java immer noch nicht das Niveau des nativen FileChoosers erreicht. So steht bespielsweise die wichtige Miniaturansicht für Bilder bei Swing nicht zur Verfügung. Auch das Auswahl “Lasso” bei mehrfach Auswahl fehlt bei der Java-Variante.

JNative

Mit JNative gibt es eine einfache Möglichkeit native Code in Java einzubinden, ohne dafür gleich JNI oder den C/C++ Compiler bemühen zu müssen. Der native Code in der DLL (oder dem SO-Modul) ist generisch und ein Call an eine Betriebsystemfunktion wie “GetOpenFile” wird sozusagen in Java “zusammengebaut”.


Calls mit JNative bauen

Mit drei Klassen werden Strukturen für die Übergabe an die Funktion, das Auslesen der Rückgabewerte und der Call selbst zusammen gebaut. Damit der native Call klappt, muss selbstverständlich die JNativeCpp.dll im Java-Librarypath gefunden werden können. Hierzu lädt man die DLL bei JNative herunter und kopiert sie ins Bin Verzeichnis des JRE (dort wo auch java.exe zu finden ist).

Folgende drei Klassen liefern dann schon das gewünschte Ergebnis:

Main-Klasse mit MainEntry-Point und dem FileChooser an sich:

package rinke.solutions.jnative;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFileChooser;
import javax.swing.UIManager;

import org.xvolks.jnative.JNative;
import org.xvolks.jnative.Type;
import org.xvolks.jnative.exceptions.NativeException;
import org.xvolks.jnative.misc.basicStructures.DWORD;
import org.xvolks.jnative.misc.basicStructures.HWND;
import org.xvolks.jnative.util.ComDlg32;

public class MyComDlg32 extends ComDlg32 {

    private static final String CHARSET = "ISO-8859-1";
 
//Cache the JNative object between calls.
   
private static JNative nGetOpenFileName;

    // small class wrapping the OFN results
   
public static class OpenFilenameResult {
       
public OpenFilenameResult(boolean i) {
           
success = i;
       
}

        public OpenFilenameResult() {
        }

        public boolean success;
       
public String dir;
       
public List<String> files;
   
}

    public static OpenFilenameResult getOpenFileName(HWND lOwner, HWND hInstance,
            DWORD Flags, String title, String initDir
) throws NativeException, IllegalAccessException {
       
if( nGetOpenFileName == null ) {
           
nGetOpenFileName = new JNative(DLL_NAME, "GetOpenFileNameA");
            nGetOpenFileName.setRetVal
(Type.INT);
       
}

        OpenFileName param = new OpenFileName();
        param.setOwner
(lOwner);
        param.setFlag
(Flags);
        param.setTitle
(title);
        param.setInitialDir
(initDir);

        nGetOpenFileName.setParameter(0, param.getPointer());
        nGetOpenFileName.invoke
();

        if("0".equals(nGetOpenFileName.getRetVal())) {
           
return new OpenFilenameResult(false);
       
} else {
           
// extract result
           
byte[] result = JNative.getMemory(param.lpstrFile.getPointer(),param.nMaxFile.getValue());
            ByteArrayInputStream bis =
new ByteArrayInputStream(result);
            InputStreamReader reader;
            ArrayList<String> files =
new ArrayList<String>();
     
try {
       
reader = new InputStreamReader(bis,CHARSET);
              StringBuilder res = 
new StringBuilder(100);
             
             
int ch = reader.read();
             
int lastch = 0;
             
while( ch != -1 ) {
                 
if( ch == 0 ) {
                   
if( lastch == 0 ) break;
                      files.add
( res.toString() );
                      res = 
new StringBuilder(100);
                 
} else {          
                   
res.append((char)ch);
                 
}
                 
lastch = ch;
                  ch = reader.read
();
             
}
      }
catch (UnsupportedEncodingException e) {
       
e.printStackTrace();
     
} catch (IOException e) {
       
e.printStackTrace();
     
}

            OpenFilenameResult r = new OpenFilenameResult();
            r.success =
true;
            r.dir = files.get
(0);
            files.remove
(0);
            r.files = files;
           
return r;
       
}
    }
   
   
public static void main(String[] args) {
       
String initDir = System.getProperty("user.home");
       
try {
         
HWND foregroundWindow = MyUser32.getForegroundWindow();
          OpenFilenameResult res = MyComDlg32.getOpenFileName
(foregroundWindow,
                 
new HWND(0),
                 
new DWORD(OpenFileName.OFN_ALLOWMULTISELECT +
                          OpenFileName.OFN_EXPLORER
),
                 
"Bitte wählen ...",
                  initDir
);
          System.out.println
(res.success);
 
         
if( res.success ) {
           
File[] files;
           
if( res.files.size() > 0 ) {
               
files = new File[res.files.size()];
               
for (int i = 0; i < files.length; i++) {
           
files[i] = new File( res.dir + File.separator + res.files.get(i));
         
}
            }
else {
             
files = new File[1];
              files
[0] = new File(res.dir);
           
}
          }
         
         
try {
       
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
     
} catch (Exception e) {
       
e.printStackTrace();
     
}
         
         
// --- swing
     
JFileChooser browse = new JFileChooser();
           
// Allows multiple selection.
           
browse.setMultiSelectionEnabled(true);
            browse.setDialogType
(JFileChooser.OPEN_DIALOG);
            browse.setFileSelectionMode
(JFileChooser.FILES_ONLY);
            browse.setEnabled
(true);
           
           
int returnVal = browse.showOpenDialog(null);
         
if (returnVal == JFileChooser.APPROVE_OPTION)
          {
             
File[] files = browse.getSelectedFiles();
         
}
 
        }
catch( IllegalAccessException e) {
     
e.printStackTrace();
       
} catch (NativeException e) {
     
e.printStackTrace();
   
}
    }

}


Helper-Klasse zum Ermitteln des ForegroundWindows:

package rinke.solutions.jnative;

import org.xvolks.jnative.JNative;
import org.xvolks.jnative.Type;
import org.xvolks.jnative.exceptions.NativeException;
import org.xvolks.jnative.misc.basicStructures.HWND;
import org.xvolks.jnative.util.User32;

public class MyUser32 extends User32 {

    public static HWND getForegroundWindow() throws NativeException, IllegalAccessException {
       
JNative GetForegroundWindow = new JNative(DLL_NAME, "GetForegroundWindow");
        GetForegroundWindow.setRetVal
(Type.INT);
        GetForegroundWindow.invoke
();
        HWND handle =
new HWND(GetForegroundWindow.getRetValAsInt());
        GetForegroundWindow.dispose
();
       
return handle;
   
}

}


Helper-Klasse zum Verwalten der Ein- / Ausgabe-Struktur für GetOpenFilename:

package rinke.solutions.jnative;

import org.xvolks.jnative.exceptions.NativeException;
import org.xvolks.jnative.misc.basicStructures.AbstractBasicData;
import org.xvolks.jnative.misc.basicStructures.DWORD;
import org.xvolks.jnative.misc.basicStructures.HWND;
import org.xvolks.jnative.misc.basicStructures.LPARAM;
import org.xvolks.jnative.pointers.NullPointer;
import org.xvolks.jnative.pointers.Pointer;
import org.xvolks.jnative.pointers.memory.GlobalMemoryBlock;
import org.xvolks.jnative.pointers.memory.MemoryBlock;
import org.xvolks.jnative.pointers.memory.MemoryBlockFactory;
import org.xvolks.jnative.util.Callback;

public class OpenFileName extends AbstractBasicData<OpenFileName> {

    public static final int OFN_ALLOWMULTISELECT = 512;
   
public static final int OFN_CREATEPROMPT = 0x2000;
   
public static final int OFN_ENABLEHOOK = 32;
   
public static final int OFN_ENABLESIZING = 0x800000;
   
public static final int OFN_ENABLETEMPLATE = 64;
   
public static final int OFN_ENABLETEMPLATEHANDLE = 128;
   
public static final int OFN_EXPLORER = 0x80000;
   
public static final int OFN_EXTENSIONDIFFERENT = 0x400;
   
public static final int OFN_FILEMUSTEXIST = 0x1000;
   
public static final int OFN_HIDEREADONLY = 4;
   
public static final int OFN_LONGNAMES = 0x200000;
   
public static final int OFN_NOCHANGEDIR = 8;
   
public static final int OFN_NODEREFERENCELINKS = 0x100000;
   
public static final int OFN_NOLONGNAMES = 0x40000;
   
public static final int OFN_NONETWORKBUTTON = 0x20000;
   
public static final int OFN_NOREADONLYRETURN = 0x8000;
   
public static final int OFN_NOTESTFILECREATE = 0x10000;
   
public static final int OFN_NOVALIDATE = 256;
   
public static final int OFN_OVERWRITEPROMPT = 2;
   
public static final int OFN_PATHMUSTEXIST = 0x800;
   
public static final int OFN_READONLY = 1;
   
public static final int OFN_SHAREAWARE = 0x4000;
   
public static final int OFN_SHOWHELP = 16;
   
public static final int OFN_SHAREFALLTHROUGH = 2;
   
public static final int OFN_SHARENOWARN = 1;
   
public static final int OFN_SHAREWARN = 0;

    protected OpenFileName(OpenFileName lValue) {
       
super(lValue);
   
}

    public OpenFileName() throws NativeException {
       
super(null);
        mValue =
this;
        createPointer
();
   
}

    DWORD       lStructSize;
    HWND        hwndOwner;
    HWND        hInstance;
    Pointer     lpstrFilter;
    Pointer     lpstrCustomFilter;
    DWORD       nMaxCustFilter;
    DWORD       nFilterIndex;
    Pointer     lpstrFile;
    DWORD       nMaxFile;
    Pointer     lpstrFileTitle;
    DWORD       nMaxFileTitle;
    Pointer     lpstrInitialDir;
    Pointer     lpstrTitle;
    DWORD       Flags;
   
short       nFileOffset;
   
short       nFileExtension;
    Pointer     lpstrDefExt;
    LPARAM      lCustData;
    Callback    lpfnHook;
    Pointer     lpTemplateName;
   
private MemoryBlock block;

    public OpenFileName getValueFromPointer() throws NativeException {
       
offset = 0;
        lStructSize =
new DWORD(getNextInt());   // 0
       
hwndOwner = new HWND(getNextInt());     // 4
       
hInstance = new HWND(getNextInt());     // 8
        // lpstrFilter 12
       
offset += 4;
       
// lpstrCustomFilter 16
       
offset += 4;
       
// nMaxCustFilter 20
       
offset += 4;
       
// nFilterIndex 24
       
offset += 4;
       
// lpstrFile 28
       
offset += 4;
       
// nMaxFile 32
       
nMaxFile = new DWORD(getNextInt()); // offset += 4;
        // lpstrFileTitle 36
       
offset += 4;
       
// nMaxFileTitle 40
       
offset += 4;
       
// lpstrInitialDir 44
       
offset += 4;
       
// lpstrTitle 48
       
offset += 4;
        Flags =
new DWORD(getNextInt());         // 52
        // nFileOffset 56
       
offset += 2;
       
// nFileExtension 68
       
offset += 2;
       
// lpstrDefExt 60
       
offset += 4;
       
// lCustData 64
       
lCustData = new LPARAM(getNextInt());
       
// lpfnHook 68
       
offset += 4;
       
// lpTemplateName 72
       
offset += 4;

        return getValue();
   
}

    public int getSizeOf() {
       
return 76;
   
}

    public Pointer createPointer() throws NativeException {
       
pointer = new Pointer(new GlobalMemoryBlock(getSizeOf()));
        pointer.setIntAt
(0, getSizeOf());

        nMaxFile = new DWORD(512);
        lpstrFile =
new Pointer(MemoryBlockFactory.createMemoryBlock(nMaxFile.getValue()));

        pointer.setIntAt(28, lpstrFile.getPointer());
        pointer.setIntAt
(32, nMaxFile.getValue()-1);

        return pointer;
   
}

    public void setOwner(HWND owner) throws NativeException {
       
hwndOwner = owner;
        pointer.setIntAt
(4, hwndOwner.getValue());
   
}

    public void setFlag(DWORD f) throws NativeException {
       
Flags = f;
        pointer.setIntAt
(52,Flags.getValue());

    }

    public void setTitle(String title) throws NativeException {
       
if(title == null) {
           
lpstrTitle = new NullPointer();
       
} else {
           
lpstrTitle = new Pointer(MemoryBlockFactory.createMemoryBlock(title.length() + 1));
            lpstrTitle.setMemory
(title);
       
}
       
pointer.setIntAt(48, lpstrTitle.getPointer());
   
}

    public void setInitialDir(String initDir) throws NativeException {
       
if(initDir == null) {
           
lpstrInitialDir = new NullPointer();
       
} else {
           
lpstrInitialDir = new Pointer(MemoryBlockFactory.createMemoryBlock(initDir.length() + 1));
            lpstrInitialDir.setMemory
(initDir);
       
}
       
pointer.setIntAt(44, lpstrInitialDir.getPointer());

    }

}

Dieser Beitrag wurde unter Java veröffentlicht. Setze ein Lesezeichen auf den Permalink.

Hinterlasse eine Antwort

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *

Du kannst folgende HTML-Tags benutzen: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>