Java调用Native API之JNA

Java调用C/C++的Native API一般采用JNI(Java Native Interface),但是需要编写特殊的代码,方法、数据的映射也很复杂。JNA(Java Native Access)能够是Java代码很简单的调用到Native API(.dll、.so)。类似于.NET的P/Invoke(Platform Invoke)、Python的ctypes。最初是1999年SUN公司为开发NetBeans IDE而开发的,2006年开源。

[url=https://github.com/twall/jna]https://github.com/twall/jna[/url]

目前版本: 4.1.0

先看一个简单的例子:调用Windows API的printf输出“Hello, World”。

JNA代码:
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;

public class HelloWorld1 {
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");
}
}

第6行定义CLibrary映射到Native(继承JNA的Library)
第7,8行加载目标Native Lib(Windows:msvcrt.dll,也可以是自己的so或dll)
第10行映射Native方法
第14行作为Java的普通方法调用printf

这是一种静态的映射方法,以下也可以动态映射:
import com.sun.jna.Function;
import com.sun.jna.Platform;

public class HelloWorld2 {
public static void main(String[] args) {
String libraryName = Platform.isWindows() ? "msvcrt" : "c";
String functionName = "printf";

// Function
Function function = Function.getFunction(
libraryName,
functionName,
Function.C_CONVENTION);

// Invoke
function.invoke(new Object[]{"Hello, world\n"});
}
}


[b]JNI来实现调用C写的方法:[/b]
1)编写HelloWorld.java
/* HelloWorld.java */
public class HelloWorld {
public native void hello(); /* (1) */
static {
System.loadLibrary("helloworld"); /* (2) */
}
public static void main (String args[]) {
HelloWorld h = new HelloWorld();
h.hello(); /* (3) */
}
}

2)编译java文件
[quote]/usr/java/jdk1.7.0_45/bin/javac HelloWorld.java[/quote]
3)生成JNI的HelloWorld.h
[quote]/usr/java/jdk1.7.0_45/bin/javah -jni HelloWorld[/quote]
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */

#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: HelloWorld
* Method: hello
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_HelloWorld_hello
(JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

4)编写HelloWorld.c
#include "HelloWorld.h"

JNIEXPORT void JNICALL Java_HelloWorld_hello
(JNIEnv *env, jobject me)
{
printf("Hello World\n");
}

5)编译HelloWorld.c
[quote]gcc -fPIC -shared HelloWorld.c -I/usr/java/jdk1.7.0_45/include/ -I/usr/java/jdk1.7.0_45/include/linux/ -o libhelloworld.so[/quote]
6)执行HelloWorld
[quote]java -Djava.library.path=. HelloWorld[/quote]

[b]JNA来实现调用C写的方法:[/b]

1)编写hello.c
#include <stdio.h>

void hello ()
{
printf("Hello World!\n");
}


2)编译hello.c
[quote]gcc -fPIC -shared -o libhello.so hello.c[/quote]

3)编写HelloJNA.java
import com.sun.jna.Library;
import com.sun.jna.Native;

interface HelloLib extends Library {
HelloLib INSTANCE = (HelloLib) Native.loadLibrary("hello", HelloLib.class);

void hello();
}

public class HelloJNA {
public static void main(String[] args){
HelloLib hello = HelloLib.INSTANCE;
hello.hello();
}
}


4)编译执行
[quote]javac -cp jna-4.1.0.jar HelloJNA.java
java -cp .:jna-4.1.0.jar HelloJNA[/quote]

参考:
http://en.wikipedia.org/wiki/Java_Native_Access
http://en.wikipedia.org/wiki/Java_Native_Interface
http://en.wikipedia.org/wiki/SWIG
http://www.swig.org/
http://en.wikipedia.org/wiki/Platform_Invocation_Services
http://www.atmarkit.co.jp/fjava/special/jna/jna_1.html
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用 JNA 调用 Windows API 可以实现模拟键盘事件。以下是一个示例代码,使用 `SendInput` 函数发送键盘事件: ```java import com.sun.jna.Native; import com.sun.jna.Pointer; import com.sun.jna.platform.win32.User32; import com.sun.jna.platform.win32.WinDef; import com.sun.jna.platform.win32.WinUser; public class Main { public static void main(String[] args) { User32 user32 = User32.INSTANCE; // 模拟按下键盘按键 int VK_A = 0x41; // A键的虚拟键码 WinUser.INPUT input = new WinUser.INPUT(); input.type = new WinDef.DWORD(WinUser.INPUT.INPUT_KEYBOARD); input.input.setType("ki"); input.input.ki.wScan = 0; input.input.ki.time = 0; input.input.ki.dwExtraInfo = new BaseTSD.ULONG_PTR(0); input.input.ki.wVk = new WinDef.WORD(VK_A); input.input.ki.dwFlags = new WinDef.DWORD(0); // 0 表示按下键盘按键 user32.SendInput(new WinDef.DWORD(1), (WinUser.INPUT[]) input.toArray(1), input.size()); // 模拟释放键盘按键 input.input.ki.dwFlags = new WinDef.DWORD(WinUser.KEYEVENTF_KEYUP); // KEYEVENTF_KEYUP 表示释放键盘按键 user32.SendInput(new WinDef.DWORD(1), (WinUser.INPUT[]) input.toArray(1), input.size()); } } ``` 在上面的示例代码中,我们使用 `User32.INSTANCE` 获取 JNA 接口,然后定义了一个 `WinUser.INPUT` 结构体来存储键盘事件的信息。`SendInput` 函数用于发送输入事件,第一个参数是发送事件的个数,第二个参数是一个 `WinUser.INPUT` 数组,第三个参数是数组元素的大小。在发送键盘事件时,我们需要设置对应的虚拟键码和键盘事件类型,具体可以参考 Windows API 文档。 另外需要注意的是,为了发送键盘事件,程序需要获取 `SE_DEBUG_NAME` 权限,否则会报错。可以在程序启动时使用 `Advapi32.INSTANCE.OpenProcessToken` 函数获取当前进程的访问令牌,然后使用 `Advapi32.INSTANCE.LookupPrivilegeValue` 函数获取 `SE_DEBUG_NAME` 权限的 LUID,最后使用 `Advapi32.INSTANCE.AdjustTokenPrivileges` 函数开启 `SE_DEBUG_NAME` 权限。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值