C#功能扩充: 让Java加入吧——C#启动JVM!
https://sourceforge.net/projects/jvcforcsharp/files/
这几天用Java写了许久的大作业。
一直纠结在Java的GUI程序设计上。
swing里的JList真变态,连Java核心那本书都把它放高级卷里。
起初写了点代码,界面太难看,实在受不了。
于是想起了微软。VS家族。
网上搜下"C++ jvm"能出来一堆啊,所以很欣喜地用++兄做着界面。
但是到了要显示文字的时候,它就麻烦了。
我还是喜欢使用+号连接字符串啊,厄,好吧,换C#。
再搜,没有代码了,怎么办?自己写吧。
我们知道C#可以运行非托管的代码,所以我们可以使用WindowsAPI
先用纯C++写了一遍不使用jni.h调用jvm的代码,这样到时候可以很轻松地转化为C#代码(不过不用jni.h,我们需要在C#里自己写个JavaENV和JavaVM的类):
下面让我简单介绍一下原理吧:
void* tmp是专门存临时指针数据的,因为VS动不动就提示不能转换,干脆设个临时值加(int)就强制转换了。
char optsstr[][100] = {"-Djava.compiler=NONE", "-Djava.class.path=.;testjvm.jar;", "-verbose:NONE"};
int optsptr[] = { 0,0,0 };
optsptr[i]=(int)((char*)(optsstr[i]));
上面这段就是optsptr各种存初始化参数的字符串指针。
int opts[] = {0,0,0,0,0,0};
这个opts有六个值因为在jni.h里JavaVMOption包含char*和int两项
这里有三个项,那就是6个值咯。
args.version=0x00010006;
args.nOptions=3;
tmp=opts;
args.options=(int)tmp;
args.ignoreUnrecognized=1;
这个是创建jvm需要的参数信息,没什么特别的。
HMODULE hdll=LoadLibrary(L"C://Program Files//Java//jre6//bin//client//jvm.dll");
_CreateJavaVM m_CreateJavaVM = (_CreateJavaVM)GetProcAddress(hdll, "JNI_CreateJavaVM");
用API动态价载DLL,一般人都会吧。
m_CreateJavaVM(&jvmp,&envp,&args);
这次调用有个特别的地方:
它的prototype是这样JNI_的CreateJavaVM(jvm**,env**,args*);
开始以为jvm**真的要int a;int* b=&a;int** c=&b;呢
而且更疯狂的是jni里的定义是jvm { functions* },就是jvm里有个函数表functions
jvm共有8个值,3个是reserved;env则有233个,4个是reserved。
于是先建了int flist[],再
int a=(convert to int)(flist);
int b=(convert to int)(&a);
int c=(convert to int)(&b);
结果第一次运行,然后读flist里什么都是0,没有变化!
连试了几次,最后还是扎到汇编里去了。
看了反汇编代码,跟进JNI_CreateJavaVM里面去,才知道这函数最后就只给jvm*和env*各一个值就出来了。
于是简单了,直接设两个int,然它们接指针数据吧。
int classname = 0;
char* classnameptr = "ljy/csharp/jvm/Hello";
classname=(int)classnameptr;
_FindClass m_FindClass=(_FindClass)(((int**)envp)[0][6]);
int _r = m_FindClass(envp,classname);
这个就是加载jar包里的类,其中要说的只有_FindClass m_FindClass=(_FindClass)(((int**)envp)[0][6]);
((int**)envp)[0][6]拆解开就是:
env* envp;
env { functions* }
functions { [0]reserved0 ... [6]FindClass ... }
所以envp[0]是得到functions的首地址,而envp[0][6]当然是得到第六个函数FindClass的地址咯
_DestroyJavaVM m_DestroyJavaVM = (_DestroyJavaVM)(((int**)jvmp)[0][3]);
m_DestroyJavaVM(jvmp);
用完了记得摧毁,这是好习惯…
现在原理清楚了,开始转成C#语言就好了。
先写NativeAPI,DllImport("Kernel32")去引入WindowsAPI吧。
再写NativeMemory,用Marshal各种直接操作内存…
最后JavaConnector,按刚写的C++代码重写成C#版就好了。
其中JavaConnector中还有JavaVM和JavaENV两个家伙。
JavaVM也就算了,JavaENV可是让人够受。
一方面JavaENV函数200多个啊,光写delegate就一堆了。
好在直接把jni.h里的东西拿出来,用VB写个小程序自动解析生成下代码,一切就顺利了。
我们用eclipse写个代码打成jar包吧:
到C#里试了一试,OK,搞定!