如何使用 JNI 从 JAVA 调用带有 C++ 参数的函数?

Posted

技术标签:

【中文标题】如何使用 JNI 从 JAVA 调用带有 C++ 参数的函数?【英文标题】:How to call a function with arguments in C++ from JAVA using JNI? 【发布时间】:2015-04-14 14:08:47 【问题描述】:

我正在搞这个任务一段时间......

我正在尝试从 java 调用 C# DLL 方法。

我使用this 作为教程,它建议构建一个中间 c++ dll。但是它使用了没有参数的方法,我担心它需要修改才能使用带参数的方法。这是因为当我在 java 中调用 t.SetCounter0("aaa") 函数时出现 unsatisfiedlinkerror 异常。

这是java代码:

package jniTester;

import java.io.Console;

public class Test1 

static 
    //System.load("c:\\Users\\ttene\\Documents\\Visual Studio 2012\\Projects\\CPM\\CPMPerformanceCountersController\\x64\\Debug\\CppWrapperDll.dll");
    System.load("c:\\Users\\ttene\\Documents\\Cpm2Java\\CppWrapperDll.dll");


public native void SetCounter0(String x);

public static void main(String[] args) 

    try 
        Test1 t = new Test1();
        System.out.println("1");
        t.SetCounter0("aaa");
        System.out.println("2");

     catch (Exception e) 
        e.printStackTrace();
    

这是cpp:

#include <jni.h>
#include <iostream>

#include "Java\jnicall.h"
#include "MCPP\CppWrapperDll.h"

JNIEXPORT void JNICALL Java_Test1_SetCounter0  (JNIEnv *jn, jobject jobj) 

    std::cout << "Java_Test1_SetCounter0";

    // Instantiate the MC++ class.
    CppWrapperDllC* t = new CppWrapperDllC();

    // The actual call is made. 
    t->callCountersControl();

这是h文件:

#using <mscorlib.dll>
#using "CountersControl.netmodule"

using namespace System;

public __gc class CppWrapperDllC

    public:
        // Provide .NET interop and garbage collecting to the pointer.
        CountersControl __gc *t;

        CppWrapperDllC() 

            t = new CountersControl();
            // Assign the reference a new instance of the object
        

    // This inline function is called from the C++ Code
    void callCountersControl() 

        t->SetCounter0("aaa");
    
;

最后这是 jni h 文件:

#include <jni.h>
/* Header for class Test1 */

#ifndef _Included_Test1
#define _Included_Test1
#ifdef __cplusplus
extern "C" 
#endif
/*
 * Class:     Test1
 * Method:    SetCounter0
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_Test1_SetCounter0(JNIEnv *, jobject);

#ifdef __cplusplus

#endif
#endif

感谢您的帮助。谢谢。

【问题讨论】:

>尝试编译项目 Wink | ;) 它不起作用?当然,谁知道 jni.h 在哪里?顺便说一句,您应该知道 jni.h 对 Java-C# 交互选项有很大帮助,阅读更多材料 Wink | ;) 添加包含目录:项目 -> 属性 -> 配置属性 -> VC++ 目录 -> 包含目录 -> 添加 2 个文件夹“%your jdk%/include”和“%your jdk%/include/win32” 似乎更好(微笑),编译库时有一些小错误,但我认为google会帮助你。将 C++ 项目构建到 HelloWorld.dll 中,然后将 HelloWorld.dll 和 CSharpHelloWorld.netmodule 复制到 D:\。运行 Test1.class 看看发生了什么。 ...取自这篇教程。 我的提示:尝试另一个教程,例如this 似乎更准确/详细。 亲爱的 xerx593,我也尝试了您建议的教程。我现在不尝试调用 C# dll,而只调用 C++ dll。结果是一样的: Exception in thread "main" java.lang.UnsatisfiedLinkError: Test1.SetCounter0([C)V at Test1.SetCounter0(Native Method) at Test1.main(Test1.java:17) The jni signature was changed到 JNIEXPORT void JNICALL Java_Test1_SetCounter0 (JNIEnv *, jobject, jcharArray) 因为我现在发送 char 数组。我希望我可以向您发送短代码 sn-ps,以便您查看它们。谢谢 【参考方案1】:

您应该使用 javah 创建 JNI 标头。如果你使用过它,标题中的声明实际上应该是这样的:

JNIEXPORT void JNICALL Java_Test1_SetCounter0(JNIEnv *, jobject, jstring);

其中 jstring 是作为参数传递给 SetCounter0(...) 的字符串。

【讨论】:

谢谢保罗。我做了你提供的,它确实将 jstring 添加到签名中。不,我得到:C:\Users\tten\Documents\Cpm2Java>java Test1 1 Java_Test1_SetCounter0# # Java 运行时环境检测到致命错误:## 内部错误 (0xe0434352),pid=3340,tid=5060 ## JRE 版本:Java(TM) SE Runtime Environment (8.0_31-b13) (build 1.8.0_31-b13) # Java VM: Java HotSpot(TM) 64-Bit Server VM (25.31-b07 混合模式 windows-amd64 压缩 oops) # 有问题的框架:# C [KERNELBASE.dll+0x940d]... 顺便说一句,我不得不从文件中删除这个包,因为我不能用它执行 javah。我试过: javah -verbose -jni -classpath 。 jniTester.Test1 但它返回:错误:找不到“jniTester.Test1”的类文件。我不明白为什么。所以我删除了包装并且它工作了。 对于打包问题:我使用 ant,所以我没有从命令行使用 javah 的任何经验。对于错误,这可能会有所帮助:***.com/questions/24244288/… 好吧,如你所见,我没有使用任何 Marshaling 方法,因此本文不相关。保罗,你还有什么想法吗?谢谢。 好吧,错误代码本身只说明 .NET 出现了一些问题。但是如果没有日志和完整的错误消息,我无法说出更具体的内容。更改 JNI-header 后是否重新编译了 dll 和 javacode。这个问题可能是由于一些不正确的方法名。不过只是猜测。

以上是关于如何使用 JNI 从 JAVA 调用带有 C++ 参数的函数?的主要内容,如果未能解决你的问题,请参考以下文章

SWIG (Java):如何将带有回调函数的结构从 Android 应用程序传递给 C++?

JNI 通过多线程从 C++ 调用 Java

如何在 JNI 中使用自定义类类型参数调用 Java 函数

您可以在使用 JNI 从 java 调用的 c++ 函数中创建一个新的 JVM 吗?

分析由 JNI 调用运行的 Java

如何使用 C++ 模板实现从类型到对象方法调用的映射?