jar打包成dll(C#调用java代码)

Posted 街角幸福

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了jar打包成dll(C#调用java代码)相关的知识,希望对你有一定的参考价值。

一.前言碎语
(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源代码

以上是关于jar打包成dll(C#调用java代码)的主要内容,如果未能解决你的问题,请参考以下文章

springboot java调用海康威视sdk 打包成jar后无法引用dll的问题

c#调用jar包的方法(超详细)

c++调用opencv相关函数并打包成dll,c#不能调用

可运行JAR包调用JNative

c#如何将一个类库打包成两个dll

java使用jna加载dll文件可以运行成功,打包后运行jar文件却找不到dll文件