JNA 教程
JNA 教程
----------
1. JNA介绍
JNA提供JAVA程序访问本地共享库(DLLs), 且不需要编写JNI或本地代码.
JNA允许使用JAVA方法调用方式来直接调用本地函数.它提供了一个动态的C语言编写的转发器,自动实现JAVA类型和C类型的映射.
JNA使用一个小型本地库存根来动态调用本地代码.开发者需要定义一个JAVA接口来描述本地库存的函数,结构.
JNA包括一个平台库. 它提供已描述好本地函数类型的一组工具接口来简化本地访问.
JNA与JNI比较, 它不需要生成DLL, JNA基于JNI技术, 简化了JAVA本地访问的过程, 但性能不如JNI.
官网
http://jna.java.net/
2. 包说明
JNA只有一个组件:jna.jar. 它包括本地库jnidispatch. JNA自己负责抽取与加载本地库支持文件jnidispatch, 如果没有找到, 可以通过System.loadLibrary(java.lang.String)来加载.若还没有找到, 你需要自己来安装本地共享库支持文件.
platform-specific jar: 它也包括本地库jnidispatch, 主要用于Web Start.
3. 示例1
调用 C标准库函数printf.
3.1 配置CLASSPATH, 加入jna.jar
3.2 编写JAVA接口描述C标准库函数printf,并调用
3.2.1 加载本地库并映射. 加载本地库C, 定义接口CLibrary 映射本地库C.
3.2.2 类型映射
3.2.3 调用映射本地函数的JAVA方法
package com.sun.jna.examples;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;
/** JNA interface 映射与用法. */
public class HelloWorld {
public interface CLibrary extends Library {
CLibrary INSTANCE = (CLibrary)
Native.loadLibrary((Platform.isWindows() ? "msvcrt" : "c"),
CLibrary.class);
void printf(String format, Object... args);
}
public static void main(String[] args) {
CLibrary.INSTANCE.printf("Hello, World\n");
}
}
4. 不需要自己一个一个来映射本地库,JNA提供platform.jar, 它映射了大多数的标准库.
5. 映射名规则:
结构:根据头文件名映射
ShlObj.h
ShlObj
函数:根据DLL文件名映射.
Advapi32.dll
Advapi32
工具类:DLL文件名+Util.
Advapi32.dll
Advapi32Util
6. 同步调用与异步调用
JNA本地调用默认是异步的.要同步调用的话:
Kernel32 INSTANCE = (Kernel32)
Native.loadLibrary("kernel32", Kernel32.class);
Kernel32 SYNC_INSTANCE = (Kernel32)
Native.synchronizedLibrary(INSTANCE); // 同步
7. 最好将SYNC_INSTANCE 的作用域定义成局部的,方便JAVA垃圾回收.
8. 结构映射
8.1 在接口内部编写一个静态类继承Structure
9. 类型映射表
http://jna.java.net/javadoc/overview-summary.html#marshalling
10. JNAerator 自动本地库映射JAVA代码工具
JNAeratorStudio
http://code.google.com/p/jnaerator/
11. 传引用
// C声明
void allocate_buffer(char **bufp, int* lenp);
// JNA 映射
void allocate_buffer(PointerByReference bufp, IntByReference lenp);
// 用法
PointerByReference pref = new PointerByReference();
IntByReference iref = new IntByReference();
lib.allocate_buffer(pref, iref);
Pointer p = pref.getValue();
byte[] buffer = p.getByteArray(0, iref.getValue());
// 为什么这样就是传引用呢?
因为它传过去的不是对象本身,而是它的地址值.
12. 数组映射
// C声明
void fill_buffer(int *buf, int len);
void fill_buffer(int buf[], int len); // same thing with array syntax
// 等价 JNA 映射
void fill_buffer(int[] buf, int len);
13. Structures/Unions 映射
结构:
映射时需要与结构字段定义的顺序一致.
联合:
与结构类似,但必须设置使用setType表示激活的字段 .
Structures.ByReference
Structures.ByValue
标记型接口.表示结构体是引用或结构体本身.
14. 官方例子
http://java.net/projects/jna/sources/svn/show/trunk/jnalib/contrib
15. 记录用户输入
package cn.bisoft.java.jna;import com.sun.jna.Structure;import com.sun.jna.platform.win32.BaseTSD;import com.sun.jna.platform.win32.Kernel32;import com.sun.jna.platform.win32.User32;import com.sun.jna.platform.win32.WinDef;import com.sun.jna.platform.win32.WinUser;import com.sun.jna.platform.win32.WinDef.HMODULE;import com.sun.jna.platform.win32.WinDef.LRESULT;import com.sun.jna.platform.win32.WinDef.WPARAM;import com.sun.jna.platform.win32.WinUser.HHOOK;import com.sun.jna.platform.win32.WinUser.HOOKPROC;import com.sun.jna.platform.win32.WinUser.KBDLLHOOKSTRUCT;import com.sun.jna.platform.win32.WinUser.LowLevelKeyboardProc;import com.sun.jna.platform.win32.WinUser.MSG;/** * 低级键盘鼠标事件监听 * 此程序使用JNA实现类似键盘记录器程序或网页游戏傻瓜点击录制 */public class TrackUserInput {private static volatile boolean quit;private static HHOOK hhkKeyBoard;private static HHOOK hhkMouse;private static LowLevelKeyboardProc keyboardHook;private static HOOKPROC mouseHook;public static void main(String[] args) {final User32 lib = User32.INSTANCE;HMODULE hMod = Kernel32.INSTANCE.GetModuleHandle(null);keyboardHook = new LowLevelKeyboardProc() {public LRESULT callback(int nCode, WPARAM wParam,KBDLLHOOKSTRUCT info) {if (nCode >= 0) {switch (wParam.intValue()) {case WinUser.WM_KEYUP:case WinUser.WM_KEYDOWN:case WinUser.WM_SYSKEYUP:case WinUser.WM_SYSKEYDOWN:int keyCode = info.vkCode;System.err.println(keyCode);if (info.vkCode == 123) {quit = true;}break;}}return lib.CallNextHookEx(hhkKeyBoard, nCode, wParam,info.getPointer());}};mouseHook = new LowLevelMouseProc() {public LRESULT callback(int nCode, WPARAM wParam,MOUSEHOOKSTRUCT info) {if (nCode >= 0) {switch (wParam.intValue()) {case MouseHook.WM_LBUTTONDOWN:System.err.println("mouse left key down");break;case MouseHook.WM_LBUTTONUP:System.err.println("mouse left key up");break;case MouseHook.WM_MBUTTONDOWN:System.err.println("mouse middle key down");break;case MouseHook.WM_MBUTTONUP:System.err.println("mouse middle key up");break;case MouseHook.WM_RBUTTONDOWN:System.err.println("mouse right key down");break;case MouseHook.WM_RBUTTONUP:System.err.println("mouse right key up");break;case MouseHook.WM_MOUSEMOVE:// System.err.println("mouse mouse");break;}}return lib.CallNextHookEx(hhkMouse, nCode, wParam, info.getPointer());}};hhkKeyBoard = lib.SetWindowsHookEx(WinUser.WH_KEYBOARD_LL,keyboardHook, hMod, 0);hhkMouse = lib.SetWindowsHookEx(WinUser.WH_MOUSE_LL, mouseHook, hMod, 0);System.out.println("Enter '<F12>' to quit");new Thread() {public void run() {while (!quit) {try {Thread.sleep(10);} catch (Exception e) {}}System.err.println("unhook and exit");lib.UnhookWindowsHookEx(hhkKeyBoard);lib.UnhookWindowsHookEx(hhkMouse);System.exit(0);}}.start();// 处理消息int result;MSG msg = new MSG();while ((result = lib.GetMessage(msg, null, 0, 0)) != 0) {if (result == -1) {System.err.println("error in get message");break;} else {lib.TranslateMessage(msg);lib.DispatchMessage(msg);}}lib.UnhookWindowsHookEx(hhkKeyBoard);lib.UnhookWindowsHookEx(hhkMouse);}public interface LowLevelMouseProc extends HOOKPROC {LRESULT callback(int nCode, WPARAM wParam, MOUSEHOOKSTRUCT lParam);}public static class MOUSEHOOKSTRUCT extends Structure {public static class ByReference extends MOUSEHOOKSTRUCT implementsStructure.ByReference {};public WinUser.POINT pt;public WinDef hwnd;public int wHitTestCode;public BaseTSD.ULONG_PTR dwExtraInfo;}}