JNative Howto
Saturday 3 June 2006.Requirements
First you need to have [1]:
- a running Java environnement Sun’s JDK,
- an IDE, like Eclipse [2] or any IDE you are comfortable with,
- JNative itself,
Unzip the jnative archive you’ve just downloaded and you obtain at least 2 or 3 files :
- JNative.jar : this is the java side suitable for all platforms with Java5,
- JNative.dll : this is the Win32 native side for W2K/XP/2003,
- libjnative.so : this is the Linux native side.
You have to add JNative.jar in your classpath, see in Eclipse the menu Project|Properties|Java Build Path|Libraries, click onAdd External JARs... and choose JNative.jar.
For Windows users :JNative.dll must be in the path, so put it in thecurrent directory or in C:\Windows orC:\Windows\System32 (assuming that your Windows is installed in C:\Windows, can also be in C:\WINNT)
For linux users :libjnative.so can be in your /usr/lib directory, in the i386 directory of your jre/lib, or you can add to the shell launching your application :
export LD_LIBRARY_PATH=/path/to/your/directory
You need to have the documentation of the library so you know what are the parameters and return value of a function.
I, now describe an example, you’ll need the documentation, get ithere this sample will demonstrate the use of the Color dialog (you can get the white paperhere)
We will implement the ChooseColor function we will also need the documentation of theCHOOSECOLOR structure
Structure handling
First we need to implement the structure. Basically you have 2 ways to do this :
- Dirty : you need one call and do not use all the members of the struct,
- Clean : you create a library, your code must be reusable, you have 5 minutes to do it,I’ll elaborate theclean way as it is the recommanded one.
Let’s consider this structure :
DWORD lStructSize;
HWND hwndOwner;
HWND hInstance;
COLORREF rgbResult;
COLORREF *lpCustColors;
DWORD Flags;
LPARAM lCustData;
LPCCHOOKPROC lpfnHook;
LPCTSTR lpTemplateName;
} CHOOSECOLOR, *LPCHOOSECOLOR;
We need to know the size of each elements, some of them are already known by JNative [3], for the others you’ll have to find them by yourself (remember that a pointer has a size of 4 bytes).
Type | Length [4] | JNative class |
DWORD | 4 | org.xvolks.jnative.misc.basicStructures.LONG |
HWND | 4 | org.xvolks.jnative.misc.basicStructures.HWND |
COLORREF [MSDN1] | 4 | org.xvolks.jnative.misc.basicStructures.LONG |
COLORREF* [MSDN1] | 4 | org.xvolks.jnative.pointers.Pointer |
LPARAM | 4 | org.xvolks.jnative.misc.basicStructures.LPARAM |
LPCCHOOKPROC [MSDN2] | 4 | org.xvolks.jnative.util.Callback |
LPCTSTR | 4 | org.xvolks.jnative.pointers.Pointer |
So, now we know the size of CHOOSECOLOR structure :
2 * DWORD + 2 * HWND + 2 * COLORDEF + LPARAM + LPCCHOOKPROC + LPCTSTR = 2*4 + 2*4 + 2*4 + 4 + 4 + 4 = 36 bytes.
we have to implement org.xvolks.jnative.pointers.AbstractBasicData so our new class will be easy to use.
AbstractBasicData is a Generic class that return an instance of your data, we are now creating aChooseColor structure, with your IDE create a new class extending AbstractBasicData called ChooseColor:
package org.xvolks.jnative.misc;
import org.xvolks.jnative.exceptions.NativeException;
import org.xvolks.jnative.misc.basicStructures.AbstractBasicData;
import org.xvolks.jnative.pointers.Pointer;
public class ChooseColor extends AbstractBasicData<ChooseColor> {
protected ChooseColor(ChooseColor lValue) {
super(null);
mValue = this;
}
public ChooseColor getValueFromPointer() throws NativeException {
// TODO Auto-generated method stub
return null;
}
public int getSizeOf() {
// TODO Auto-generated method stub
return 0;
}
public Pointer createPointer() throws NativeException {
// TODO Auto-generated method stub
return null;
}
}
Modify this file to suit your needs:
Add the data members:
import org.xvolks.jnative.misc.basicStructures.*;
....
LONG lStructSize;
HWND hwndOwner;
HWND hInstance;
//This one is public because it's the return value; I should have created a getter
public LONG rgbResult;
static Pointer lpCustColors;
LONG Flags;
LPARAM lCustData;
int lpfnHook;
Callback fnHook;
String lpTemplateName;
static {
try {
lpCustColors = new Pointer(MemoryBlockFactory.createMemoryBlock(16));
} catch (NativeException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
The getSizoOf() method :
/**
* This method returns 36 always (it's the size of CHOOSECOLOR struct)
* @returns 36
*/
@Override
public int getSizeOf() {
return 36;
}
The createPointer() method :
public Pointer createPointer() throws NativeException {
pointer = new Pointer(new GlobalMemoryBlock(getSizeOf()));
pointer.setIntAt(0, getSizeOf());
pointer.setIntAt(16, lpCustColors.getPointer());
return pointer;
}
The getValueFromPointer() method :
public ChooseColor getValueFromPointer() throws NativeException {
offset = 0;
lStructSize = new LONG(getNextInt());
hwndOwner = new HWND(getNextInt());
hInstance = new HWND(getNextInt());
rgbResult = new LONG(getNextInt());
//Skip lpCustColor
offset += 4;
Flags = new LONG(getNextInt());
lCustData = new LPARAM(getNextInt());
//Skip callback
return getValue();
}
Create helper methods to manipulate callbacks :
public void removeCallback() throws NativeException {
if(fnHook != null) {
JNative.releaseCallback(fnHook);
fnHook = null;
}
}
public void addCallback(Callback lCallback) throws NativeException {
removeCallback();
fnHook = lCallback;
//The callback object must handle 4 parameters from
//CCHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
lpfnHook = JNative.createCallback(4, lCallback);
pointer.setIntAt(28, lpfnHook);
}
Create setters to manipulate data, they will fill the structure :
...
public void setOwner(HWND owner) throws NativeException {
hwndOwner = owner;
pointer.setIntAt(4, hwndOwner.getValue());
}
public void setInstance(HWND instance) throws NativeException {
hInstance = instance;
pointer.setIntAt(8, hInstance.getValue());
}
...
Function call
As we start to implement comdlg32.dll we have to create a class named ComDlg32 to handle its functions.
So create ComDlg32 class in org.xvolks.jnative.util package.
package org.xvolks.jnative.util;
import org.xvolks.jnative.JNative;
import org.xvolks.jnative.Type;
import org.xvolks.jnative.exceptions.NativeException;
import org.xvolks.jnative.misc.ChooseColor;
import org.xvolks.jnative.misc.basicStructures.HWND;
import org.xvolks.jnative.misc.basicStructures.LONG;
import org.xvolks.jnative.misc.basicStructures.LPARAM;
public class ComDlg32 {
public static final String DLL_NAME = "COMDLG32.DLL";
//Cache the JNative object between calls.
private static JNative nChooseColor;
/**
*
* @param lOwner
* @param hInstance
* @param Flags
* @param lCustData
* @param lCallback
* @param lTemplateName
* @return -1 if the use does not click on OK button, the RGB value else.
* @throws NativeException
* @throws IllegalAccessException
*/
public static int ChooseColor(HWND lOwner, HWND hInstance, LONG Flags, LPARAM lCustData, Callback lCallback, String lTemplateName) throws NativeException, IllegalAccessException {
if(nChooseColor == null) {
//JNative uses ANSI version ChooseColorA vs ChooseColorW
nChooseColor = new JNative(DLL_NAME, "ChooseColorA");
//BOOL is in fact an INT
nChooseColor.setRetVal(Type.INT);
}
ChooseColor nChooseColorStruct = new ChooseColor();
nChooseColorStruct.setOwner(lOwner);
nChooseColorStruct.setInstance(hInstance);
nChooseColorStruct.setFlags(Flags);
nChooseColorStruct.setCustData(lCustData);
nChooseColorStruct.addCallback(lCallback);
nChooseColorStruct.setTemplateName(lTemplateName);
nChooseColor.setParameter(0, nChooseColorStruct.getPointer());
nChooseColor.invoke();
if("0".equals(nChooseColor.getRetVal())) {
return -1;
} else {
return nChooseColorStruct.getValueFromPointer().rgbResult.getValue();
}
}
}
Conclusion
The callback implementation is missing you’ll have to create it yourself, see the EnumWindows sample in the sources.
The full source of ChooseColor class
package org.xvolks.jnative.misc;
import org.xvolks.jnative.JNative;
import org.xvolks.jnative.exceptions.NativeException;
import org.xvolks.jnative.misc.basicStructures.AbstractBasicData;
import org.xvolks.jnative.misc.basicStructures.HWND;
import org.xvolks.jnative.misc.basicStructures.LONG;
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.MemoryBlockFactory;
import org.xvolks.jnative.util.Callback;
public class ChooseColor extends AbstractBasicData<ChooseColor> {
LONG lStructSize;
HWND hwndOwner;
HWND hInstance;
public LONG rgbResult;
static Pointer lpCustColors;
LONG Flags;
LPARAM lCustData;
int lpfnHook;
Callback fnHook;
Pointer lpTemplateName;
static {
try {
lpCustColors = new Pointer(MemoryBlockFactory.createMemoryBlock(16));
} catch (NativeException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
public ChooseColor() throws NativeException {
super(null);
mValue = this;
createPointer();
}
public void removeCallback() throws NativeException {
if(fnHook != null) {
JNative.releaseCallback(fnHook);
fnHook = null;
}
}
public void addCallback(Callback lCallback) throws NativeException {
removeCallback();
fnHook = lCallback;
//The callback object must handle 4 parameters from
//CCHookProc(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
lpfnHook = JNative.createCallback(4, lCallback);
pointer.setIntAt(28, lpfnHook);
}
public ChooseColor getValueFromPointer() throws NativeException {
offset = 0;
lStructSize = new LONG(getNextInt()); // 0
hwndOwner = new HWND(getNextInt()); // 4
hInstance = new HWND(getNextInt()); // 8
rgbResult = new LONG(getNextInt()); //12
//Skip lpCustColor
offset += 4; //16
Flags = new LONG(getNextInt()); //20
lCustData = new LPARAM(getNextInt()); //24
//Skip callback //28
//Skip TemplateName //32
//36 = size
return getValue();
}
public int getSizeOf() {
return 36;
}
public Pointer createPointer() throws NativeException {
pointer = new Pointer(new GlobalMemoryBlock(getSizeOf()));
pointer.setIntAt(0, getSizeOf());
pointer.setIntAt(16, lpCustColors.getPointer());
return pointer;
}
public void setOwner(HWND owner) throws NativeException {
hwndOwner = owner;
pointer.setIntAt(4, hwndOwner.getValue());
}
public void setInstance(HWND instance) throws NativeException {
hInstance = instance;
pointer.setIntAt(8, hInstance.getValue());
}
public void setFlags(LONG flags) throws NativeException {
Flags = flags;
pointer.setIntAt(20, Flags.getValue());
}
public void setCustData(LPARAM custData) throws NativeException {
lCustData = custData;
pointer.setIntAt(24, lCustData.getValue());
}
public void setTemplateName(String templateName) throws NativeException {
if(templateName == null) {
lpTemplateName = new NullPointer();
} else {
lpTemplateName = new Pointer(MemoryBlockFactory.createMemoryBlock(templateName.length() + 1));
lpTemplateName.setMemory(templateName);
}
pointer.setIntAt(32, lpTemplateName.getPointer());
}
}
This tutorial was not tested and therefore is not guaranted to work, post comments if you think you need more help.
— Marc