JNI系列(三)C++代码通过JNI调用Java代码

上一篇讲解了,如何通过JAVA调用C++代码,这一篇讲解如何通过C++调用JAVA代码,C++调用JAVA代码。。在网上找了一圈,都没找到能正确运行的,参考以下文章:

https://blog.csdn.net/houwenbin1986/article/details/105294374/

https://blog.csdn.net/u011304970/article/details/75713209

虚拟机加载可按照以下步骤进行:

1)装载jvm动态库
2)查找JNI_CreateJavaVM接口
3)设置JVM启动参数,调用JNI_CreateJavaVM接口启动虚拟机
4)查找启动类,设置命令行参数,设置窗口输出重导向文件
5)调用启动类的启动方法启动java程序
6)要停止Java程序运行时,调用java类的停止方法即可

项目目录如下:

D:\C_WORK\D1\JRE
├─bin
│  │  java.dll
│  │  verify.dll
│  │  zip.dll
│  │  
│  └─server
│          jvm.dll
│          
├─lib
│      rt.jar
│      
└─test
    │  Hello.jar
    │  run.bat
    │  Test.java
    │  TestFunction.java
    │  
    ├─com
    │  └─fendo
    │          Hello.java
    │          
    └─META-INF
            MANIFEST.MF

jdk使用的是1.8,vs用的是2019,jre目录下的内容全是复制jdk目录下的,Test.java文件:

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
 
public class Test {
    
    public void Hello(String message) {
        System.out.println("Hello in class Test, From c++ " + message);
    }
 
	public static void main(String[] args) {
		System.out.println("Hello Java World!");
        //得到模板
        TestFunction func = new TestFunction();
        //
        Map<String, Object> params = new HashMap<String, Object>();
        //测试参数
        params.put("a", 1L);
        params.put("b", 2);
        params.put("c",  new Long[]{1L,2L,3L});
        //执行函数
        Map<String,Object> output = func.apply(params);
        //遍历结果
        for(Map.Entry<String, Object> entry : output.entrySet()){
            String mapKey = entry.getKey();
            Object mapValue = entry.getValue();
            System.out.println(mapKey + ":" + mapValue);
        }
	}
}

TestFunction.java

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
 
//测试jdk1.8的Function特性
public class TestFunction implements Function<Map<String, Object>, Map<String, Object>> {
 
    public TestFunction() {
		
    }
 
    //重写apply实现
    public Map<String, Object> apply(Map<String, Object> param) {
        HashMap output = new HashMap();
        Long a = (Long)param.get("a");
        int b = (Integer)param.getOrDefault("b", 0);
        if (output != null ) {
            output.put("output", a+b); // Sum
            return output;
        }
 
        return new HashMap();
    }
}

运行run.bat会自动生成Hello.jar,然后在vs2019下新建项目

复制jni.h,jni_md.h文件到项目下

在main.cpp文件里添加如下代码:

#include "jni.h"
#include <windows.h>
#include <Shlwapi.h>  
#pragma comment(lib,"Shlwapi.lib")

#include <iostream>
using namespace std;

//前置定义
bool CreateJvmAndTest();

//入口
int main(int argc, char* argv[])
{
    cout << "Hello C++ JVM..." << endl;
    CreateJvmAndTest();
    return 0;
}

//构造函数
typedef jint(JNICALL* JNICREATEPROC)(JavaVM**, void**, void*);

//启动java虚拟机
bool CreateJvmAndTest()
{
    char exepath[1024] = { 0 };
    GetModuleFileNameA(nullptr, exepath, 1024);
    PathRemoveFileSpecA(exepath);
    //获取jvm动态库的路径
    std::string jvmPath = std::string(exepath) + "\\..\\..\\jre\\bin\\server\\jvm.dll";
    //java虚拟机启动时接收的参数,每个参数单独一项
    int nOptionCount = 2;
    JavaVMOption vmOption[2];
    //设置JVM最大允许分配的堆内存,按需分配!!!
    vmOption[0].optionString = (char*)"-Xmx1024M";
    //设置classpath
    std::string classpath = std::string("-Djava.class.path=.\\;");
    classpath += (std::string(exepath) + ";");
    classpath += (std::string(exepath) + std::string("\\..\\..\\jre\\test\\Hello.jar;"));
    classpath += (std::string(exepath) + std::string("\\..\\..\\jre\\test\\;"));
    vmOption[1].optionString = (char*)classpath.c_str();


    JavaVMInitArgs vmInitArgs;
    //
    vmInitArgs.version = JNI_VERSION_1_8;//JDK1.8版本
    vmInitArgs.options = vmOption;
    vmInitArgs.nOptions = nOptionCount;
    //
    // 忽略无法识别jvm的情况
    vmInitArgs.ignoreUnrecognized = JNI_TRUE;

    //设置启动类,注意分隔符为"/"
    const char startClass[] = "com/fendo/Hello";
    //启动方法,一般是main函数,当然可以设置成其他函数
    const char startMethod[] = "main";

    //加载JVM动态库
    HINSTANCE jvmDLL = ::LoadLibraryA(jvmPath.c_str()); //加载失败的话:注意如果是64位jvm,就需要工程也是x64
    if (jvmDLL == NULL)
    {
        cout << "加载JVM动态库错误" + ::GetLastError() << endl;
        return false;
    }

    //获取JVM函数地址
    JNICREATEPROC jvmProcAddress = (JNICREATEPROC)GetProcAddress(jvmDLL, "JNI_CreateJavaVM");
    if (jvmDLL == NULL)
    {
        FreeLibrary(jvmDLL);
        cout << "加载JVM动态库错误" + ::GetLastError() << endl;
        return false;
    }

    //创建JVM
    JNIEnv* env = nullptr;
    JavaVM* jvm = nullptr;

    jint jvmProc = (jvmProcAddress)(&jvm, (void**)&env, &vmInitArgs);
    DWORD error = GetLastError();
    if (jvmProc < 0 || jvm == nullptr || env == nullptr)
    {
        FreeLibrary(jvmDLL);
        cout << "创建JVM错误" + ::GetLastError() << endl;
        return false;
    }

    //加载启动类
    jclass mainclass = env->FindClass(startClass);
    if (env->ExceptionCheck() == JNI_TRUE || mainclass == nullptr)
    {
        env->ExceptionDescribe();
        env->ExceptionClear();
        FreeLibrary(jvmDLL);
        cout << "加载启动类失败" << endl;
        return false;
    }

    //加载 main 启动方法
    jmethodID methodID = env->GetStaticMethodID(mainclass, startMethod, "([Ljava/lang/String;)V");
    if (env->ExceptionCheck() == JNI_TRUE || methodID == nullptr)
    {
        env->ExceptionDescribe();
        env->ExceptionClear();
        FreeLibrary(jvmDLL);
        cout << "加载启动方法失败" << endl;
        return false;
    }

    cout << "开始执行" << endl;
    env->CallStaticVoidMethod(mainclass, methodID, nullptr);
    cout << "执行结束" << endl;

    //测试JDK1.8函数编程
    jclass funcClass = env->FindClass("Test");
    if (env->ExceptionCheck() == JNI_TRUE || funcClass == nullptr)
    {
        env->ExceptionDescribe();
        env->ExceptionClear();
        FreeLibrary(jvmDLL);
        cout << "加载Function类失败" << endl;
        return false;
    }

    //加载 main 启动方法
    jmethodID funcMethodID = env->GetStaticMethodID(funcClass, startMethod, "([Ljava/lang/String;)V");
    if (env->ExceptionCheck() == JNI_TRUE || funcMethodID == nullptr)
    {
        env->ExceptionDescribe();
        env->ExceptionClear();
        FreeLibrary(jvmDLL);
        cout << "加载启动方法失败" << endl;
        return false;
    }

    cout << "开始执行" << endl;
    env->CallStaticVoidMethod(funcClass, funcMethodID, nullptr);
    cout << "执行结束" << endl;

    cout << "开始执行类对象方法" << endl;
    //调用成员方法
    jobject string_object = env->AllocObject(funcClass);//创建类对象
    //
    jmethodID paramMethodID = env->GetMethodID(funcClass, "Hello", "(Ljava/lang/String;)V");//查找类方法【void Hello(String msg)】
    //
    jstring param = env->NewStringUTF("hello");
    env->CallVoidMethod(string_object, paramMethodID, param);//执行对象方法
    //
    cout << "执行结束类对象方法" << endl;


    //jvm释放
    jvm->DestroyJavaVM();
    FreeLibrary(jvmDLL);

    return true;
}

将jre目录下复制到x64同目录

debug调试运行,输出如下

 

完整源码:https://download.csdn.net/download/u011781521/12550734

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值