JNI不适用于__stdcall

时间:2013-12-26 19:16:33

标签: java c++ c java-native-interface mingw

我正在使用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)错误地定义了这个?

3 个答案:

答案 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);
}