[英]JNI does not work with __stdcall
我正在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
)定義不正確?
對於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();
}
};
即使您在Windows上,也正在使用GCC,這意味着: Linux中是否有STDCALL? 基本上仍將適用。
由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);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.