这个项目是我參加内蒙古挑战杯的比赛项目,因为时间关系,我没时间实现OpenOMR开源项目由JAVA全然向C++的转换,经过我半个多月的尝试,我将OpenOMR中的1/3的代码改写成C++,只是非常快我就发现,假设依照这个进度,我是不管怎样也无法按时完毕工作了,更重要的是Joone人工智能库的算法要是全然移植不是我一个大二学生可以在这么短的时间做到的,于是我放弃JAVA转C++的解决方。

取而代之的是,我使用JAVA做算法的核心,这样就能够用最小的代价高速完毕项目,而用C++去调用JAVA的方法,并封装成dll,最后使用C# + IrisSkin2 + 自绘空间的方式制作界面并实现业务逻辑(不要觉得C++的步骤多此一举,实际上我的母语是C++,所以做起来很顺手,反而使我的效率大幅度提升)。

以下说一下我在实现C++调用JAVA的过程中遇到的一些问题。

开发环境: Visual Studio 2010 Ultimate (英文版) + eclipse 3.6.1 + JDK 1.6.0_10

众所周知JAVA依赖jvm,并且运行起来很繁琐,我这个项目的命令行例如以下(批处理)

java -cp %cd%/joone-engine-2.0.0RC1/joone-engine.jar;%cd%/jfreechart-1.0.1/lib/jcommon-1.0.0.jar;.;%cd%/jfreechart-1.0.1/lib/jfreechart-1.0.1.jar -Xmx256m openomr.openomr.SheetMusic

不要说用户看这东西会不知所措,就是我看着这东西也是非常头疼(尽管我非常熟悉dos)

以下讲一下技术难点:

一、不依赖用户机器上的JAVA环境

解决方式:使用本地应用程序集成jre环境,脱离对客户机器jre的依赖

遇见的问题:最開始的时候我觉得仅仅须要附带jvm.dll就可以实现我的目的,代码例如以下

#include <iostream>
#include <Windows.h>
#include "jdk1.6.0_10/include/jni.h"

#ifdef _WIN32
#define PATH_SEPARATOR ';'
#else
#define PATH_SEPARATOR ':'
#endif

#pragma comment(lib, "jvm")

....................

int main()
{
HMODULE JVM_DLL;

// 获取程序自带JVM路径
CHAR JvmFileName[MAX_PATH] = {0};

GetModuleFileNameA(NULL, JvmFileName, MAX_PATH);
(*strrchr(JvmFileName, '//')) = '/0';
::strcat(JvmFileName, "//jvm.dll");

//获得JVM.DLL启动实体
//JVM_DLL = LoadLibraryA(JvmFileName);
if (JVM_DLL == NULL)
{
::MessageBoxA(NULL, "载入不了jvm.dll", "", MB_OK);
}

//JVM内部函数JNI_CreateJavaVM读取
JNICreateJavaVM createJavaVM = (JNICreateJavaVM)GetProcAddress(JVM_DLL, "JNI_CreateJavaVM");

if (createJavaVM == NULL)
{
::MessageBoxA(NULL, "JNI_CreateJavaVM函数读取失败!", "", MB_OK);
}
.......................
}

我将jvm.lib和jvm.dll都放在Debug目录中,我发现这时的程序能正确载入jvm.dll,可是运行

JNICreateJavaVM createJavaVM = (JNICreateJavaVM)GetProcAddress(JVM_DLL, "JNI_CreateJavaVM");

就提示失败了,为了解决问题我開始阅读jni的文档,可是收效甚微,于是開始在各大技术论坛開始寻找解决方式,可是没有找到解决方式,于是我将系统的jre中的jvm.dll载入到程序中,这次程序最终正常跑了起来。

小结:jvm.dll的执行依赖jre中很多dll,而我在项目初期仅仅外挂一个jvm.dll导致了jvm无法正常创建,顺便说一下,解决问题从下午5点一直到晚上接近十点才彻底解决,有时候人的惯性思维真的会限制住灵感。

技术难点二:带有文件夹结构的java类调用

这个纠结了小半天,直接贴代码了

#include <iostream>
#include <Windows.h>
#include "jdk1.6.0_10/include/jni.h"

#ifdef _WIN32
#define PATH_SEPARATOR ';'
#else
#define PATH_SEPARATOR ':'
#endif

#pragma comment(lib, "jvm")

using namespace std;

typedef jint (JNICALL *JNICREATEPROC)(JavaVM **, void **, void *);

//主函数名,也可改为其它名称,JVM以此查询启动接口。
const char MainName[] ="main";

//虚拟机启动參数总数。
const int JVMOptionCount = 5;

//JVM编译器设定,none为使用默认编译器。
static char Compiler[] = "-Djava.compiler=NONE";

//最小内存
static char MinMB[] = "-Xms256M";

//最大内存
static char MaxMB[] = "-Xmx512M";

//jar包中主函数class所在路径。
static char AppClass[] = "openomr/openomr/SheetMusic";

//须要运行的jar包所在路径,'./'为当前路径简写,多jar包以';'切割。
static char ClassPath[] = "-Djava.class.path=./;E:/joone-engine-2.0.0RC1/joone-engine.jar;E:/jfreechart-1.0.1/lib/jcommon-1.0.0.jar;E:/jfreechart-1.0.1/lib/jfreechart-1.0.1.jar";

static char LibraryPath[] = "-Djava.library.path=./";

typedef jint (WINAPI* JNICreateJavaVM)(JavaVM**, JNIEnv**, void *);
/**
* Win主函数
**/
int main()
{
HMODULE JVM_DLL;

// 获取程序自带JVM路径
CHAR JvmFileName[MAX_PATH] = {0};

GetModuleFileNameA(NULL, JvmFileName, MAX_PATH);
(*strrchr(JvmFileName, '//')) = '/0';
::strcat(JvmFileName, "//jvm.dll");

//获得JVM.DLL启动实体
//JVM_DLL = LoadLibraryA(JvmFileName);
JVM_DLL = LoadLibraryA("E://project//Cpp调用Java//Debug//jdk1.6.0_10//jre//bin//client//jvm.dll");
if (JVM_DLL == NULL)
{
::MessageBoxA(NULL, "载入不了jvm.dll", "", MB_OK);
}

//JVM内部函数JNI_CreateJavaVM读取
JNICreateJavaVM createJavaVM = (JNICreateJavaVM)GetProcAddress(JVM_DLL, "JNI_CreateJavaVM");

if (createJavaVM == NULL)
{
::MessageBoxA(NULL, "JNI_CreateJavaVM函数读取失败!", "", MB_OK);
}

//指向本地方法调用接口
JNIEnv* env;
//表示Java虚拟机
JavaVM* jvm;

//设定JVM启动參数
JavaVMInitArgs vm_args;
JavaVMOption options[JVMOptionCount];

/**
*jtl(Java Tools Language)设定:JVM的缺省行为是用“即时”编译器(或JIT[字节代码编译器])运行字节码。
*当载入类时,JIT将类字节码转换成本机代码。使用JIT会导致在每一个类载入后有短暂延迟,
*但可提高程序的整体性能。在某些情况下,运行时间可缩短十分之中的一个。
*可用Compiler指定jtl,如将Compiler=foo后,该例中虚拟机将查找名为foo.dll的JIT编译器。
*搜索其他编译器是在jre/bin文件夹中和系统的PATH上进行的。
*若找不到这种编译器,虚拟机将缺省使用解释器。
*/
options[0].optionString = Compiler;
//类地址
options[1].optionString = ClassPath;
//lib地址
options[2].optionString = LibraryPath;
//最小内存
options[3].optionString = MinMB;
//最大内存
options[4].optionString = MaxMB;
//PS:此參数用于设定跟踪执行时的信息,暂不须要。
//options[3].optionString = "-verbose:jni";

//使用的jni版本号,眼下最高为JNI_VERSION_1_6。
vm_args.version = JNI_VERSION_1_6;
vm_args.options = options;
vm_args.nOptions = JVMOptionCount;
vm_args.ignoreUnrecognized = JNI_TRUE;

//启动JVM,并返回结果
int res = createJavaVM(&jvm, &env, &vm_args);

if (res < 0)
{
::MessageBoxA(NULL, "JVM启动失败!", "", MB_OK);
}

//查找目的类
jclass clazz = env->FindClass(AppClass);

if (clazz == 0)
{
::MessageBoxA(NULL, "JNI_CreateJavaVM函数读取失败!", "", MB_OK);

return cin.get();
}

//取得入口主函数序列号
jmethodID mid = env->GetStaticMethodID(clazz, MainName, "([Ljava/lang/String;)V");

if (0 == mid)
{
::MessageBoxA(NULL, "没有找到主函数!", "", MB_OK);

return cin.get();
}

cout << "env->CallStaticVoidMethod(clazz, mid, NULL);開始运行" << endl;
//main启动
env->CallStaticVoidMethod(clazz, mid, NULL);
cout << "env->CallStaticVoidMethod(clazz, mid, NULL);运行结束" << endl;

//JVM释放
jvm->DestroyJavaVM();

return cin.get();
}

期间遇到的主要问题就是在VS2010上调试程序,始终FindClass查找类失败,为此试了各种办法,先是调用jar,后来又把jar解压。。。都没有成功,于是乎。。。各种纠结。。。下午赶上腾讯的CF有活动,因此申请了个新QQ,去领了把M4A1-A + 防弹衣嘿嘿,小小的YD一下。。。晚上回来突然想法一个问题,就是VS2010的解决方式的文件夹结构问题

先看一下我的測试项目的文件夹结构-

大家注意,E:/project/Cpp调用Java/Debug是可运行文件生成的目录,我的库也是直接放在了这个目录内,可是这就导致了一个问题,我的代码和exe不再同一个目录,于是

static char ClassPath[] = "-Djava.class.path=./;E:/joone-engine-2.0.0RC1/joone-engine.jar;E:/jfreechart-1.0.1/lib/jcommon-1.0.0.jar;E:/jfreechart-1.0.1/lib/jfreechart-1.0.1.jar";

static char LibraryPath[] = "-Djava.library.path=./";

这里面的”./“在源代码中代表的路径就是E:/project/Cpp调用Java/Cpp调用Java

而在生成的exe中是E:/project/Cpp调用Java/

但因为VS2010的特性,它的工作文件夹不是exe所在文件夹,而是源代码所在文件夹,这就导致了

static char AppClass[] = "openomr/openomr/SheetMusic";

在加上工作文件夹组成的绝对路径错误,也就是真正导致FindClass失败的原因

小结:这个问题在非常诡异,须要在今后的开发中注意,不能再犯这样的低级错误了

哈哈,调用java方法成功了,我能够放松一下了。。。我去CF了。。。

Music Studio项目心得--JNI实现C++调用JAVA的更多相关文章

  1. Android JNI c/c++调用java 无需新建虚拟机

    近期通过研究SDL源码 得出android JNI  c/c++调用java 无需新建虚拟机: 具体步骤如下 第一步获得:两个参数 JNIEnv和jclass void Java_com_Test_A ...

  2. JNI中C调用Java方法

    背景需求 我们需要在JNI的C代码调用Java代码.实现原理:使用JNI提供的反射借口来反射得到Java方法,进行调用. JNI关键方法讲解. 1. 在同一个类中,调用其他方法 JNIEXPORT v ...

  3. cocos2d-x笔记5: 通过jni实现C++调用Java

    Cocos2d-x的跨平台性很强大,但是偶尔也需要平台的原生API结合. C++在Win32平台下简单的很,C++可以直接用MFC或者调用Win32API. Ios在XCode下直接就能C++和OC混 ...

  4. Android与JNI(三) ---- c++调用java(转载)

    源码下载:JniDemo JNI就是Java Native Interface, 即可以实现Java调用本地库, 也可以实现C/C++调用Java代码, 从而实现了两种语言的互通, 可以让我们更加灵活 ...

  5. Android JNI中C调用Java方法

    背景需求 我们需要在JNI的C代码调用Java代码.实现原理:使用JNI提供的反射借口来反射得到Java方法,进行调用. JNI关键方法讲解. 1. 在同一个类中,调用其他方法 JNIEXPORT v ...

  6. JNI C反射调用java方法

    前面记录了调用C的学习笔记,现在来记录一下C反射调用Java的笔记.JNI开发学习之调用C方法 Android开发中调用一个类中没有公开的方法,可以进行反射调用,而JNI开发中C调用java的方法也是 ...

  7. JNI由浅入深_7_c调用Java方法一

    1.在Java中声明方法 <span style="font-size:14px;">/** * javah -encoding utf-8 -jni com.exam ...

  8. Android studio 项目支持JNI方法

    步骤: 1. build.gradle 配置如下,主要两项 ndk 和 sourceSets apply plugin: 'com.android.application' android { com ...

  9. 架构实战项目心得(三):JAVA和MAVEN的环境配置

    1 java环境配置: 1 下载并安装jdk1.82 配置java环境变量: vi /etc/profile,在文件底部增加以下内容:export JAVA_HOME=/data/program/so ...

随机推荐

  1. Spring3表达式语言(SpEL)学习笔记

    最新地址请访问:http://leeyee.github.io/blog/2011/06/19/spring-expression-language Spring Excpression Langua ...

  2. Mongodb 上传图片

    mongdb 上传图片: [root@hy-mrz01 ~]# mongofiles put -u "pics" -p "jh7yxx" --host 127. ...

  3. Google用户登录界面 Android实现

    实验效果: 项目目录: Java代码(放在Src文件下) package com.bn.chap9.login; import java.io.BufferedReader; import java. ...

  4. 一些Windows API导致的Crash以及使用问题总结

    RegQueryValueEx gethostbyname/getaddrinfo _localtime64 FindFirstFile/FindNextFile VerQueryValue Crea ...

  5. RStudio版本号管理 整合Git

    本文为原创,转载注明出处. 系统环境: win7 x64 R-3.1.0-win.exe RStudio-0.98.507.exe 前置条件:必须拥有github仓库: 如:https://githu ...

  6. ALV 数值列负号前置 (EDIT_MASK应用)

    1.建立自定义函数 浮点数显示FUNCTION conversion_exit_zsign_output.*"---------------------------------------- ...

  7. asp.net iis URLRewrite 实现方法详解

    原文 asp.net iis URLRewrite 实现方法详解 实现非常简单首先你要在你的项目里引用两个dll:actionlessform.dll.urlrewriter.dll,真正实现重写的是 ...

  8. 14.1.1 InnoDB as the Default MySQL Storage Engine

    14.1 Introduction to InnoDB 14.1.1 InnoDB as the Default MySQL Storage Engine 14.1.2 Checking InnoDB ...

  9. boost asio 异步实现tcp通讯

    ---恢复内容开始--- asioboost   目录(?)[-] 一前言 二实现思路 通讯包数据结构 连接对象 连接管理器 服务器端的实现 对象串行化   一.前言 boost asio可算是一个简 ...

  10. TTL 超时问题

    在TCP/IP网络中,网络层并不对数据包进行可靠性传输保证,只通过ICMP报文提供反馈机制(例如:差错控制).PING命令就是ICMP的请求/响应报文,也是网络最常用的测试手段.通常使用PING命令测 ...