一.前言碎语
(1)动机:充分利用java阵营众多的类库
(2)工具:IKVM――把java bytecode 转换成IL程序,并提供大部分J2SE 1.4类的.net实现(IKVM.GNU.Classpath.dll)
winrar――提取jar,打包jar
Java IDE(可选)――阅读源代码,浏览类之间的关系,我用的是eclipse
反编译工具(可选)――没源代码时用,主要也是浏览类与类之间的关系,java反编译我用的是DJ Java Decompiler,.net用Reflector。
(3)原理:
java也好,.net也好,其执行无非就是运行时 + 库,把java汇编指令一条一条转换为IL汇编指令,就可以实现java程序到.net程序的转换――你可以通过最纯正的.net使用方式来使用这些类库/程序。ikvm实现的就是这样一个功能。
如果一个java程序调用一大堆java类库,那么必须把这些程序和类库全部转换成.net的IL格式,这个程序才能执行。
三、介绍
IKVM.NET是一个针对Mono和微软.NET框架的java实现,其设计目的是在.NET平台上运行java程序。它包含了以下的组件:
一个用.NET实现的java虚拟机。
一个java类库的.NET实现。
致力于在java和.NET之间交互的工具。
四、IKVM.NET的组件
IKVM.NET包含以下的部分:
IKVM.Runtime.dll VM运行时和所有支持代码。它包括以下的功能:
Byte Code JIT 编译器和验证器,使用JIT将Java Byte Code编译为CIL(C中间语言)。
对象模式映射结构,将.NET中的System.Object,System.String,System.Exception映射为JAVA代码中的java.lang.Object, java.lang.String,java.lang.Throwable。
管理本地方法(在Classpath中)的.NET重新实现。
IKVM.GNU.Classpath.dll 被编译的GNU Classpath版本,它是由自由软件基金会实现的JAVA类库和一些IKVM.NET附加代码组成的。注意:这里的GNU Classpath不是IKVM.NET的一部分,但是前者被用在IKVM.NET中。
IKVM.JNI.dll 通过实现JNI接口管理C++汇编。作为一个可选部分,只在程序使用自己的本地库时才被用到。而对于纯JAVA程序来讲是不会被用到的。
ikvm.exe 与java.exe很类似的启动执行程序(动态模式)。
ikvmc.exe 静态编译器,被用来编译java类和jar使其成为.NET汇编(静态模式)。
ikvmstub.exe 一个从.NET汇编生成存根类的工具,就如javap一样反编译.NET汇编。IKVM.NET了解如何存根并用实际的.NET类型引用替换对存根的引用。
IKVM.AWT.WinForms.dll 非常有限的零散AWT实现。
五、IKVM原理
1.如何替换JVM
IKVM应用包含了采用.NET实现的java虚拟机。在一些场合,我们可以用它替换掉java。例如: java -jar myapp.jar 将被替换为 ikvm -jar myapp.jar。
2.在.NET应用中使用java类库
IKVM.NET包含ikvmc,这个在java bytecode与.NET中间语言的转换器。如果我们使用一个被用在.NET平台的java库的话, 运行ikvmc –targetlibrary mylib.jar(mylib.jar在这里指代我们的jar文件)来生成mylib.dll。例如apache FOP项目是一个开源的XSL-FO处理器项目,它使用java语言编写的用于从xml生成PDF文档。使用IKVM.NET技术,我们可以将apache FOP用在任何的.NET应用中。这样在开发.NET应用的同时利用IKVM便可以使用java开源项目这个免费的软件仓库。尽管在IKVM.NET没有提供在.NET中使用的java编译器,但是我们可用开源的Jikes编译器将java源代码编译为JVM bytecode,然后使用ikvmc –targetexe myapp.jar来生产.NET执行文件。我们甚至可以通过包含ikvmstub应用的方式在我们的java代码中用.NET API。
四 语法(译者注:这个工具是在windows命令提示窗中使用的)
ikvmc [ options ] classOrJarfile [ classOrJarfile ... ]
参数见下:
类文件(.class)或包文件(.jar)
Java类或包文件的名字可以含有通配符(如*.class)。
参数----参数说明
-out:输出文件----指定输出文件的文件名。文件名应含有扩展名.dll(参数-target设为library时)或.exe(参数-target设为exe或winexe时)。一般地,缺省状态下ikvmc会根据输入文件名和-target参数来确定输出文件名。但是,如果在输入文件名中使用了通配符,那么就必须使用这个选项来指定输出文件名。
-assembly:程序集名称----指定生成的程序集名称。一般地,缺省状态下,程序集名就是输出文件名。
-target:目标类型----指定生成目标.exe或.dll文件的类型,可以指定如下值:
exe——生成一个在windows命令窗口中运行的可执行程序。
winexe——生成一个带有界面的.exe应用程序。
library——生成一个动态链接库(.dll)文件。
module——生成一个.net模块。
在Linux平台上,exe与winexe两个参数是没有区别的。
-keyfile:主文件名----主文件名用于指定结果程序集。
-version:M.m.b.r ----指定程序集版本。
-main:类名----指定含有main函数的类的名称。缺省情况下,如果-target参数设置为exe或winexe,ikvmc会搜寻合法的main函数,在找到的情况下会给用户提示。
-reference:----指定类库位置 。 如果Java代码使用了.NET的编程接口(API),那么使用这个选项指定这些动态链接库(dll)。如果引用了不止一个类库文件,那么此选项可以多次使用。可以使用通配符(例如:c:\libs\*.dll)。
- recurse:filespec---- 处理当前文件夹下所有可与filespec匹配的文件。例如:- recurse: *.class
-nojni----对于非Java函数(native methods)不要生成用于非Java代码的Java接口(JNI)。
-resource:路径名----指定Java资源的引用路径名。
-exclude:文件名---- 要排除的类名列表。
-debug----将调试信息输出。注意:只有当.class文件含有调试信息(即使用javac-g参数编译)时才有用。
-srcpath:路径----指定源代码的位置。与-debug同时使用。程序会将包中类的搜索路径指定为此路径,并从中搜索类的源代码。
-Xtrace:名称---- 显示所有该指定名称的跟踪点。
-Xmethodtrace:函数名 ---- 指定的生成的函数名,将函数跟踪器(method trace)生成到此函数中。
几点说明:
ikvmc利用Java类文件和包文件生成.NET程序集。它将输入文件中的Java二进行代码转化成.NET公共中间语言(CIL)。并利用它来生成:
.NET可执行文件(-target:exe or –target:winexe)
.NET类库(-target:library)
.NET模块(-target:module)
Java程序一般由一系列的包文件组成。ikvmc可以将多个包文件(和类文件)转化成一个单独的.NET可执行程序或类库文件。例如,一个程序包含main.jar、lib1.jar和lib2.jar可以转化成一个单独的main.exe文件。
处理多个包文件时,如果有多个相同的类或源文件,ikvmc会使用第一个读入的版本,而忽略以后读到的。这时会产生警告信息。因此,包文件的顺序至关重要。
注意:
为了得到最佳结果,使用ikvmc转换Java程序时,请按照Java程序中类路径的顺序在,把要转换包的列表键入ikvmc的命令行。
六.操作过程
(1)将已经编译后的java中Class文件进行打包;打包命令JAR
如:将某目录下的所有class文件夹全部进行打包处理;
使用的命令:jar cvf test.jar -C com/ .
其中test.jar为要生成的jar包;com/ . 为指定的当前目录下的文件夹,该文件夹包括子文件夹及class文件;
(2)到IKVM官方网站下载IKVM需要的组件 http://www.ikvm.net/
ikvm-7.2.4630.5
(3)设置路径
解压ikvm-0.42.0.3.zip,并将%IKVM_HOME%/bin添加到path中。此处的%IKVM_HOME%是指解压后ikvm的主目录。
(4)将java的jar包转换为.dll控件
使用的命令:ikvmc -out:IKVM.dll test.jar
其中IKVM.dll为将要生成的.dll控件文件名;test.jar为之前打包好的jar包文件。
(5)在C#项目中添加所需的控件
1、新建一个C#.NET项目,首先添加一下必须的DLLs
%IKVM_HOME%/bin/IKVM.OpenJDK.Core.dll
%IKVM_HOME%/bin/IKVM.Runtime.dll
%IKVM_HOME%/bin/IKVM.Runtime.JNI.dll
2、添加已生成的.dll文件
将之前生成好的.dll文件加载到C#项目中
(6)测试
在C#项目中使用java类,其方法同java。但对包的引用使用C#的语法using
源代码:
Java源代码:
package com.zht;
//要调用的Java类
public class Test {
//要调用的Java方法
public String returnString() {
return "Hello, zht!";
}
}
C#窗体源代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using com.zht;
namespace KIVMTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Test t = new Test();
string str = t.returnString();
MessageBox.Show(str);
}
}
}
结果:
启动C#窗口后,显示提示窗口,内容为:Hello, zht!
七.其他
(1) 将jar转换为dll:运行:ikvmc -target:library ××××.jar,会将jar文件自动转换为 ××××.dll;但是,如果jar中引用的类不存在于××××.jar,也不存在于IKVM.GNU.Classpath.dll之中,则会给出警告信息,该类也不会转换。这时,需要引用缺少的类所在的dll,格式如下:
ikvmc -target:library -reference:lib1.dll -reference:lib2.dll -reference:lib3.dll jar1.jar
jar的提取:jar可以用winrar打开,把你想要的目录或类解压缩。
把java class文件打包为jar:把目录压缩成zip格式,把后缀改成jar,OK。
(2)例子:转换Geotools的main模块
下面以Geotools的main模块为例,说明怎样将jar文件转换为IL文件。
geotools是有10年历史的GIS中间件。其main模块有1000多个类,源代码文件大小合计9.74M。jar文件名为gt2-main.jar,2.34M.
步骤1:分析main模块依赖的类库,分析类库间的依赖关系
geotools-main依赖的类库有:
rt.jar――java的主要的lib
geoapi.jar――OpenGIS一帮人定义的标准geoapi
jai_codec.jar, jai_core.jar,――Java 2D API
JTS-1.4.jar,――Java 拓扑套件,主要用于拓扑分析,可能依赖
vecmath-1.3.jar――vector数学库
xalan-2.5.1.jar――xml 转换lib
opengis-legacy-0.1.jar,――忘了干什么的,反正和gis有关
units-0.01.jar――不知道是什么
batik*.jar――SVG lib
mailapi-1.3.jar
步骤2:转换类库
先转换那些没依赖的库,比如vecmath-1.3.jar,......
运行:ikvmc -target:library vecmath-1.3.jar
结果:Note: output file is "vecmath-1.3.dll" Note: automatically adding reference to "f:\ikvm-0.20.0.0\ikvm\bin\ikvm.gnu.classpath.dll"
成功转换成vecmath-1.3.dll。
如果碰上了依赖的库没转换,比如运行:
kvmc -target:library jai_codec.jar
结果提示有些类找不到:
Note: output file is "jai_codec.dll"
Note: automatically adding reference to "f:\ikvm-0.20.0.0\ikvm\bin\ikvm.gnu.classpath.dll"
Warning: class "com.sun.image.codec.jpeg.JPEGCodec" not found
Warning: class "com.sun.image.codec.jpeg.JPEGEncodeParam" not found
Warning: class "com.sun.image.codec.jpeg.JPEGImageEncoder" not found
Warning: class "com.sun.image.codec.jpeg.JPEGDecodeParam" not found
Warning: class "com.sun.image.codec.jpeg.JPEGImageDecoder" not found
Warning: class "com.sun.image.codec.jpeg.ImageFormatException" not found
Warning: class "com.sun.image.codec.jpeg.JPEGQTable" not found
Warning: class "sun.security.action.GetPropertyAction" not found
因此对于有依赖关系的库,需要从最下面那个,大家都依靠它的那个库转换起。在这里就是rt.jar。
运行:
ikvmc -target:library rt.jar
惨!!!ikvm弹出错误窗口――不是我无能,是rt.jar太狡猾!
怎么办?需要从rt.jar中找出所需要的类,将这些类转换就行了。
用winrar找出需要的类,比如jai_codec需要com.sun.image.codec.jpeg.JPEGCodec,....等8个类,就从rt.jar中找出这些类出来。注意,这些类可能还依赖于别的类,依赖的类也需要找出来,全部打包成一个新的jar,如little_rt.jar。如果有些类在IKVM.GNU.Classpath.dll中已经有了,可以不用提取。
最惨的情况可能是1个类依赖2个类,2个类又依赖8个类,然后又依赖更多的类,这种情况下与其要把这些类提取出来,不如更改这个类,把对外依赖的东东都去掉,全部return null, return 0什么的。先转换成功,然后再用.net把这个类重写就行了。幸运的是这里没碰见这情况。
用把little_rt.jar转换为little_rt.dll,然后运行:
ikvmc -target:library -reference:little_rt.dll jai_codec.jar
OK!成功!
这样继续一个jar一个jar的转换,这些jar会依赖rt.jar中更多的类,需要都提取出来,放在little_rt.jar中,再转换为little_rt.dll。
就这样把geoapi.jar,jai_codec.jar, jai_core.jar, JTS-1.4.jar, vecmath-1.3.jar,xalan-2.5.1.jar,opengis-legacy-0.1.jar, units-0.01.jar都转换成了dll。mailapi-1.3.jar和batik*.jar没转换。这两个直接转换都没成功,batik*.jar是一系列jar,之间的关系比较复杂我也搞不清。凭我对geotools的了解, main模块里基本没用到mail,而SVG是以插件形式提供的,在这里用的也不多。那就不管这两个,来转换gt2-main.jar先。
运行:ikvmc -target:library -reference:jai_codec.dll -reference:geoapi.dll -reference:xalan-2.5.1.dll -reference:JTS-1.4.dll -reference:vecmath-1.3.dll -reference:units-0.01.dll -reference:opengis-legacy-0.1.dll -reference:little_rt.dll -reference:jai_core.dll gt2-main.jar
结果:
Note: output file is "gt2-main.dll"
Note: automatically adding reference to "f:\ikvm-0.20.0.0\ikvm\bin\ikvm.gnu.classpath.dll"
Warning: class "org.geotools.ct.CoordinateTransformation" not found
Warning: unable to compile class "org.geotools.renderer.lite.InternalTranscoder"(missing class "org.apache.batik.transcoder.image.ImageTranscoder")
Warning: class "javax.swing.text.DefaultFormatterFactory" not found
Warning: unable to compile class "org.geotools.ct.CoordinateTransformation$Inverse"(missing class "org.geotools.ct.CoordinateTransformation")
Warning: class "org.geotools.renderer.style.InternalTranscoder" not found
Warning: class "org.apache.batik.transcoder.TranscoderInput" not found
Warning: class "org.apache.batik.transcoder.SVGAbstractTranscoder" not found
Warning: class "org.apache.batik.transcoder.TranscodingHints$Key" not found
Warning: class "org.apache.batik.transcoder.TranscoderOutput" not found
Warning: class "org.apache.batik.transcoder.TranscodingHints" not found
Warning: class "org.geotools.renderer.lite.InternalTranscoder" not found
Warning: unable to compile class "org.geotools.data.DataTestCase"(missing class "junit.framework.TestCase")
Warning: class "javax.mail.Session" not found
Warning: class "javax.mail.internet.InternetAddress" not found
Warning: class "javax.mail.Address" not found
Warning: class "javax.mail.internet.MimeMessage" not found
Warning: class "javax.mail.Message" not found
Warning: class "javax.mail.Message$RecipientType" not found
Warning: class "javax.mail.Transport" not found
Warning: class "javax.mail.MessagingException" not found
Warning: class "javax.mail.internet.AddressException" not found
可以看见只有org.geotools.ct.CoordinateTransformation,org.geotools.renderer.lite.InternalTranscoder,org.geotools.data.DataTestCase这三个类没转换成功,其中org.geotools.data.DataTestCase是测试用的,可以不管,剩下两个类要用到batik中的类,可以按照上述思路把batik转换为dll,也可重写这两个类――剩下的1000个类,已经全部转换成功,可以在.net平台上用了。
检验结果:用reflector打开gt2-main.dll
说明:
1,rt.jar中sun.*;com.*命名空间中的类,IKVM.GNU.Classpath.dll都没有
2,进行.net开发时需要引用IKVM.GNU.Classpath.dll
3,ikvm的jar->dll转换可能存在bug,最好把test case也全部转换过来,测试测试
4,ikvm的jar->dll转换可能存在性能问题,需要时可进行重构
5,有少量java 语法产生的指令不能直接转换,会报错。这是极少量的,我转换了好几M的东西了,只报了2处这种错。这时候可能需要改动改动java源代码