我正在使用Windows 7x64上的JNI,Java版本是1.7.0_40和MinGW / GCC / G ++ 4.7.2。
尝试从Java关闭显示器电源。所以,我创建了一个类:
public class MonitorTrigger {
static {
try {
System.load(new ClassPathResource("MonitorTrigger.dll").getFile().getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
}
public native void on();
public native void off();
}
然后,从它生成h
文件(使用Eclipse,但我相信它使用javah
):
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class by_dev_madhead_bubikopf_desktop_monitortrigger_util_MonitorTrigger */
#ifndef _Included_by_dev_madhead_bubikopf_desktop_monitortrigger_util_MonitorTrigger
#define _Included_by_dev_madhead_bubikopf_desktop_monitortrigger_util_MonitorTrigger
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: by_dev_madhead_bubikopf_desktop_monitortrigger_util_MonitorTrigger
* Method: on
* Signature: ()V
*/
JNIEXPORT void
JNICALL Java_by_dev_madhead_bubikopf_desktop_monitortrigger_util_MonitorTrigger_on(
JNIEnv *, jobject);
/*
* Class: by_dev_madhead_bubikopf_desktop_monitortrigger_util_MonitorTrigger
* Method: off
* Signature: ()V
*/
JNIEXPORT void
JNICALL Java_by_dev_madhead_bubikopf_desktop_monitortrigger_util_MonitorTrigger_off(
JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
并实施了它:
#include "by_dev_madhead_bubikopf_desktop_monitortrigger_util_MonitorTrigger.h"
#include <iostream>
#include <windows.h>
JNIEXPORT void JNICALL Java_by_dev_madhead_bubikopf_desktop_monitortrigger_util_MonitorTrigger_on
(JNIEnv * env, jobject object) {
SendMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, (LPARAM) -1);
}
JNIEXPORT void JNICALL Java_by_dev_madhead_bubikopf_desktop_monitortrigger_util_MonitorTrigger_off
(JNIEnv * env, jobject object) {
SendMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, (LPARAM) 2);
}
它不起作用!我得到例外:
Exception in thread "main" java.lang.UnsatisfiedLinkError: by.dev.madhead.bubikopf.desktop.monitortrigger.util.MonitorTrigger.off()V
at by.dev.madhead.bubikopf.desktop.monitortrigger.util.MonitorTrigger.off(Native Method)
at by.dev.madhead.bubikopf.desktop.monitortrigger.App.main(App.java:7)
我被困了一段时间,但谷歌搜索后,我找到了一个解决方案 - 使用__cdecl
代替__stdcall
约定导出函数。所以,我做了一个非常肮脏的黑客:
#define JNICALL __cdecl
它有效!
之前在jni_md.h
定义为__stdcall。我意识到我做了一件非常糟糕的事情,这对小猫有害,但我缺乏C / C ++的经验,我无法弄清楚,为什么我要重新定义这个?为什么标准标题(jni_md.h
)错误地定义了这个?
答案 0 :(得分:4)
对于jni使用的32位DLL,代码必须在没有通常的__stdcall
装饰的情况下进行编译 - 即代码不能在符号名称上具有@N
后缀;但它仍然需要以__stdcall
模式进行编译。
在mingw下编译dll时,需要将选项--kill-at
添加到链接器。这通常使用-Wl,--kill-at
传递。这将导致@N
后缀被删除,因此显示为一个简单的符号,可以在运行时由JVM链接。该选项也可以缩写为-Wl,-k
。
另一种方法是使用一个映射文件,它以未编码的形式导出符号,这是在使用microsoft自己的visual studio编译器进行编译时最常用的机制。
这个文档很难记录,当它发生时产生的错误并没有太大帮助。
我建议查看JNA
,这使得编写本机代码包装器变得更加简单,并且在这种情况下意味着不需要C ++代码。
完成此任务的JNA代码如下所示:
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinUser;
import com.sun.jna.platform.win32.WinDef.LPARAM;
import com.sun.jna.platform.win32.WinDef.WPARAM;
public class JNAMonitorTrigger {
public void monitor(LPARAM param) {
User32.INSTANCE.PostMessage(WinUser.HWND_BROADCAST,
WinUser.WM_SYSCOMMAND,
new WPARAM(0xf170), param);
}
public void on() {
monitor(new LPARAM(-1));
}
public void off() {
monitor(new LPARAM(2));
}
public static void main(String args[]) throws Exception {
JNAMonitorTrigger me = new JNAMonitorTrigger();
me.off();
Thread.sleep(1000);
me.on();
}
};
答案 1 :(得分:1)
即使你在Windows上,你也在使用GCC,这意味着:Is there STDCALL in Linux?基本上仍然适用。
答案 2 :(得分:1)
java.h
生成的文件中的函数声明为extern“C”
您必须将其添加到cpp
文件中:
extern "C"
JNIEXPORT void JNICALL Java_by_dev_madhead_bubikopf_desktop_monitortrigger_util_MonitorTrigger_on
(JNIEnv * env, jobject object) {
SendMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, (LPARAM) -1);
}