簡體   English   中英

JNI不適用於__stdcall

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM