一.前言碎语


(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、

  2、添加已生成的.dll文件

新建一个C#.NET项目,首先添加一下必须的DLLs 

   %IKVM_HOME%/bin/IKVM.OpenJDK.Core.dll 

   %IKVM_HOME%/bin/IKVM.Runtime.dll 

   %IKVM_HOME%/bin/IKVM.Runtime.JNI.dll


      将之前生成好的.dll文件加载到C#项目中


(6)测试


在C#项目中使用java类,其方法同java。但对包的引用使用C#的语法using


 


源代码:


Java源代码:


C#窗体源代码:

package com.zht; 

 //要调用的Java类  

 public class Test { 

    //要调用的Java方法  

     public String returnString() { 

         return "Hello, zht!"; 

     } 



 }


结果:

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,然后运行:


OK!成功!

ikvmc -target:library -reference:little_rt.dll jai_codec.jar




这样继续一个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源代码