C++调用Java项目小结

本文详细介绍JNI在C++中的应用方法,包括JVM的初始化、类与对象的创建及方法调用等步骤,并探讨了多线程环境下JNI的使用技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、  JNI的使用

对JNI了解不多,网上有说将java程序生成头文件并制作动态链接库的,过程看起来比较复杂,而且害怕动态库调用出问题,所以就用了Java提供的JNI接口。

a、包含Java提供的同文件,#include "include/jni.h",该文件在Java的jdk目录下,(发现include文件夹就能找到);然后加入以下语句

 //设置环境变量,windows版和Linux版

#ifdef_WIN32

#definePATH_SEPARATOR ';'

#else

#define PATH_SEPARATOR':'

#endif

//放置库搜索目录

#pragma comment(lib, "jvm")

b、初始化时首先要把Java虚拟机的动态链接库加载进去:

HMODULE  JVM_DLL = LoadLibraryA("C://ProgramFiles//Java//jdk1.7.0_04//jre//bin//client//jvm.dll");//这个路径不是固定的,而且拷贝到项目下不行,可能是jvm这个动态库需要有环境支持或者与其他文件有关联吧,具体不太清楚

c、创建虚拟机,这个比较关键,看代码:

JNIEnv *env; //java运行时环境

JavaVM *jvm;//java虚拟机

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

static char ClassPath[] ="-Djava.class.path=.//实验室//bin;.//实验室//lib//commons-codec-1.3.jar;.//实验室//lib//commons-httpclient-3.1.jar;.//实验室//lib//commons-logging-1.1.jar;.//实验室//lib//mysql-connector-java-5.1.19-bin.jar;.//实验室//lib//nekohtml.jar;.//实验室//lib//poi-3.8-20120326.jar;.//实验室//lib//xalan.jar;.//实验室//lib//xercesImpl.jar";

static char LibraryPath[] ="-Djava.library.path=.//实验室//lib";

typedef jint (WINAPI*JNICreateJavaVM)(JavaVM**, JNIEnv**, void *);

//JVM内部函数JNI_CreateJavaVM读取  

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

 

         if(createJavaVM == NULL)  

         { 

                   ::MessageBoxA(NULL,"JNI_CreateJavaVM函数读取失败!", "", MB_OK); 

         } 

 

         //设定JVM启动参数  

         JavaVMInitArgsvm_args; 

         JavaVMOptionoptions[4];//这个数组容量根据设置内容不同而不同,有些代码还设置了最大最小内存,需要的空间就多了

 

         options[0].optionString= Compiler;     

         //类地址

         options[1].optionString= ClassPath;  

         //lib地址  

         options[2].optionString= LibraryPath;

 

         options[3].optionString="-verbose:jni";

 

 

         //使用的jni版本,目前最高为JNI_VERSION_1_6。  

         vm_args.version  = JNI_VERSION_1_6; 

         vm_args.options  = options; 

         vm_args.nOptions= 4; 

         vm_args.ignoreUnrecognized= JNI_TRUE; 

 

         //启动JVM,并生成运行时环境

         intres = createJavaVM(&jvm, &env, &vm_args); 

 

         if(res < 0)   //为0表示成功

         { 

                   ::MessageBoxA(NULL,"JVM启动失败!", "", MB_OK); 

         }

关键是对ClassPath的设置,开始认为只要设置当前类所在路径就可以了,因为所依赖的jar包在LibraryPath中设置,但是不行运行时总是找不到类ID和函数ID。所以设置的时候一定要将所有需要的jar包给写一遍,写的时候”.//”代表当前路径。生成虚拟机时会生成运行时环境,刚开始认为这个运行时环境很重要,总是设法保存这个变量,并做传递,后来因为做多线程,调试时总是出错,出现指针异常的情况,就想到这个运行环境应该是每个线程一个,有需要就创建。

d、找到类并生成对象

jclass clazz;

jmethodID mid;

jobject obj;

clazz = env->FindClass("file/ContentExe");//查找类

         if(clazz == 0) 

         { 

                   ::MessageBoxA(NULL,"没有找到类", "", MB_OK);

                   jvm->DestroyJavaVM();

                   return;

         } 

         //新建一个对象

         obj= env->AllocObject(clazz);

         if(obj == 0)             

         {               

                   ::MessageBoxA(NULL,"创建失败!", "", MB_OK);

jvm->DestroyJavaVM();

                   return;

         }

         //查找方法id

         myid= env->GetMethodID(clazz, "getProcessString","()Ljava/lang/String;");

         if(myid == 0)            

         {    

                   ::MessageBoxA(NULL,"没有找到getProcessString方法", "", MB_OK);

                   jvm->DestroyJavaVM();

                   return;

         }

注意的是,查找类的时候因为已经设置类的路径了,所以用”包名/类名”查找即可。再有就是查找Java类的方法Id,使用的是GetMethodID方法,如果是静态类的话使用GetStaticMethodID方法,参数为类、方法名、方法标识符;方法标识符的获取需要用到javap命令,javap –s –p 类名查看,结果看”Signature: ()Ljava/lang/String;”这句其中括号里面的是这个方法的参数,没有时为空,后面是返回值,如果返回值为空的话是V,当然记不住也行直接使用命令查看就好。

e、Java方法的调用,这个比较简单,通过对象调用方法即可,只是参数需要注意

jstring result= (jstring) env->CallObjectMethod(obj, myid);

其中result是返回的String类型的值,调用CallObjectMethod方法时返回一个Object对象,需要强制类型转换,如果这个myid指向的Java函数需要参数,可在myid后面直接加,需要注意的是类型转换,需要吧参数转换成Java能够识别的类型(如jint、jstring…),还有就是参数按Java函数声明的顺序。

2、  关于多线程

对多线程了解不多,尤其感觉多线程的同步很难控制。网上的资料将工作者线程和UI线程,最开始也不知道说的什么意思,下载了些实例,程序能跑但是还不是不了解区别。于是看了Windows核心编程里关于进程与多线程的章节,晚上1点多时终于想明白他们的区别了,其实网上的资料已经说的很明白了,工作者线程主要是负责处理复杂的计算的,就像是后台运行那样,是界面还可以有其他操作;而UI线程主要是处理与界面有关的内容,我下载了一个例子就是打开两个以上的线程,每个线程管理一个界面实现滚动条的滚动,例子很简单,但是很能说明问题。

另外,主进程本身也有一个线程,这个需要注意,我生成的工作者线程开始执行后,总是跳转到主进程继续执行,工作者进程由于执行一条较费时间的语句到后台执行,查看线程可以明显的看到。

代码如下:

CWinThread*pGetThread;       //采集线程

CWinThread*pShowThread;  //显示结果线程

struct PARAM

{

int m_nID;

};//用于构造线程参数,根据具体情况添加(很有用的变量!)

PARAMm_pParam0;

PARAMm_pParam1;

 

pGetThread =AfxBeginThread(GetThread,&m_pParam0);//需要有GetThread方法

pShowThread =AfxBeginThread(ShowThread,&m_pParam1);//需要有ShowThread方法

UINTGetThread(LPVOID pParam);//需要具体实现

UINTShowThread(LPVOID pParam);

3、  CString类型与jstring类型的相互转换

//CString与jstring类型的转换

jstringretjstring;   //用于保存转换后的jstring类型变量

jchar mystring[2000];  

jsizeiLoop;  

jsize jlength= convert.GetLength();  

for (iLoop =0; iLoop < jlength; iLoop++)  

mystring[iLoop] = (jchar) convert.GetAt(iLoop);   //convert为所要转换的CString类型变量

retjstring = m_penv->NewString(mystring, jlength); //生成jstring类型变量

 

//jstring 转换到CString

CString myCString;//用于保存转换后的CString类型变量

         jsizeistringlength;

         jbooleanisCopy = JNI_TRUE;

         constjchar *pChar = m_penv->GetStringChars(jnistr,&isCopy);

         istringlength= m_penv->GetStringLength(jnistr);

         myCString= (BSTR) pChar; //强制类型转换得到CString类型变量

m_penv->ReleaseStringChars(jnistr,pChar);

4、  关于字符集

Java使用的字符集是Unicode,VS2010默认的字符集也是Unicode的,这些本身没有问题,但是在读取Java保存的文本文件时,总是出现乱码的情况,个人认为是txt文档使用的字符集的问题,通过网上查资料,可以通过配置地域的信息的函数setlocale(需要头文件<locale.h>),将当前字符集转换为’’chs’’,需要注意的是要保存原字符集,读取文件后重新设置回去。

char*old_locale = _strdup(setlocale(LC_CTYPE,NULL));

setlocale(LC_CTYPE, "chs" );//设置编码格式

//读文件操作

setlocale( LC_CTYPE, old_locale );

5、  其他

a、关于获取当前路径

需要使用到的函数为GetModuleFileName,具体代码如下:

TCHARszPath[MAX_PATH];  

GetModuleFileName(NULL,szPath, MAX_PATH);  //获取当前路径

         CStringPathName(szPath); //将其转换为CString类型,方便操作

b、关于CString与LPCSTR类型转换

网上好多人说这两个类型只用强制类型转换就可以实现相互转换了,但是在Unicode字符集下做此强制类型转换出现如下错误提示:

error C2440: “类型转换”: 无法从“CString”转换为“LPCSTR”

没有可用于执行该转换的用户定义的转换运算符,或者无法调用该运算符

         如果将当前字符集改为“多字节字符集”就没有问题。最终的解决方法是:

int len =WideCharToMultiByte(CP_ACP, 0, strCong, strCong.GetLength(), NULL,0, NULL,NULL);                //strCong为CString类型变量

                  char *str = new char[len+1];   //生成的char *变量可以直接转换为LPCSTR类型

                  WideCharToMultiByte(CP_ACP, 0, strCong,strCong.GetLength(), str, len, NULL,NULL);

                  str[len+1] = '\0';       //添加LPCSTR所需的结尾符

WideCharToMultiByte该函数映射一个unicode字符串到一个多字节字符串,具体参数意义可以参考百科。

c、CFileFind类

         查找文件时使用,很方便的一个类。跟一个前辈说第一次用这个类,被鄙视了一把。

这个类需要注意的就是很多函数都要在使用FindNextFile函数后才能使用,但没有影响查找的开头,具体原因没有深究,太懒了!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值