项目是使用 C# 写的,传输数据使用对方给的Java加密解密算法。直接使用C#写算法要研究对方的算法,耗时较长。
因此直接将jar包转成dll进行调用。
前言
使用IKVM工具将Java生成的Jar包转成dll,使用C#进行调用,可以正常调用,但是遇到中文就会乱码。
以下将从 如何使用 IKVM工具 、解决乱码 两个方面介绍。
IKVM使用 |
一、Jar包生成dll
1.1、环境准备
- IKVM工具下载,最好使用 ikvm-8.1.5717.0 是对应 JDK 1.8的,如果下载的版本低的话,可能会报错。
- 把IKVM配置到环境变量中
1.2、生成jar包
注意命名空间,在C#中会使用到这个空间:cn.hsaf.common.utils.HseEncAndDecUtil
1.3、生成dll
打开cmd,打开到jar包所在文件夹,使用命令 ikvmc -out:ybEnc.dll(要生成dll的名字) ybEnc.jar(jar包的名字) 即可生成dll。
1.4、C#中使用
- 引用生成的dll
- 需要额外引用ikvm安装包中的一些dll(没有仔细研究引用哪些,按需应用吧。。)
- 可以直接使用命名空间.方法名 调用方法 如: cn.hsaf.common.utils.HseEncAndDecUtil.HelloWorld 。
排查问题 |
二、解决乱码的几种尝试
2.1 C#中接收的有问题
一开始出现的时候,感觉直接在C#端转一下就行(因为之前在C#中http请求获取第三方返回数据时,尝试过,结果解决了), 比如可能接收的是GBK 或者 UTF-8 等之类的,在C#中调用后返回的字符串使用UTF-8 to ISO之类的,在网上搜了一堆,逐一试了都不好使,有点投机取巧了,而且这样试太盲目了。。
2.2 Java传过来的有问题
C#端解决不了,换种思路,那么会不会是 Java 中传过来的 字符串 本身就是乱码呢,接着找到三方给的java工程demo,在main 方法里 把入参写死 直接调用方法 使用sout 打印出参在控制台,控制台输出的也是正常的,中文不是乱码,加密串也可以正常解析出来。不过此时也验证了一点:demo是没有问题的,应该demo里的算法跟对方用的是一个东西(因为之前遇到过给的demo都有问题的),入参包括密钥也是对的 。。
既然demo没问题,那么我是不是可以看看Java端返回的串,是什么编码格式的,在C#端解一下就行了,然后找到了这个方法:
public static String getEncoding(String str) {
String encode = "GB2312";
try {
if (str.equals(new String(str.getBytes(encode), encode))) { //判断是不是GB2312
String s = encode;
return s; //是的话,返回“GB2312“,以下代码同理
}
} catch (Exception exception) {
}
encode = "ISO-8859-1";
try {
if (str.equals(new String(str.getBytes(encode), encode))) { //判断是不是ISO-8859-1
String s1 = encode;
return s1;
}
} catch (Exception exception1) {
}
encode = "UTF-8";
try {
if (str.equals(new String(str.getBytes(encode), encode))) { //判断是不是UTF-8
String s2 = encode;
return s2;
}
} catch (Exception exception2) {
}
encode = "GBK";
try {
if (str.equals(new String(str.getBytes(encode), encode))) { //判断是不是GBK
String s3 = encode;
return s3;
}
} catch (Exception exception3) {
}
return ""; //如果都不是,说明输入的内容不属于常见的编码格式。
}
通过测试,发现返回的是GB2312,喜出望外,赶紧在C#中以GB2312接收试试,结果还是乱码。。
会不会是搞复杂了,如果我直接把结果返回出去呢,是不是直接返回中文C#那边也会乱码,然后尝试了下,kao,没有乱码(验证了一点 IKVM方式调用 一般情况下是没啥问题的),那是为啥呢,通过方法返回就乱码,直接返回字符串不乱码。
2.3 增加日志记录调用过程中哪里出现的乱码
奇了怪了,两边都没有问题,那问题还能出在哪里呢,这时我甚至开始怀疑是不是IKVM有bug转成dll后 不兼容之类的,毕竟这种方式算是 暴力破解算法。然后开始各种找 IKVM的资料,到网上搜,官网上找,都没有找到相关的论坛,灵机一动,从github上找找呢,通过 description readme 等方式找(参照下方的高级搜索) 也未找到相关资料。
in:name JPA stars:>1000 forks:>
in:readme JPA
in:description JPA language:java pushed:>2022-01-01 stars:>1000
到这里基本已经不知道从哪查起了,迷茫了C#,Java,IKVM都堵死了,是不是可以放弃了,已经没啥思路了,要不搞个SpringBoot给C#调用Http请求呢。。。
休息一会后又从头捋了下,再来最后一次尝试,假设IKVM是没有问题的,肯定还是代码的问题,应该是转成dll后有啥不兼容的地方(因为java里也试了下,输出出来是没问题的),想起了平时解决问题的断点法,这种jar生成dll的东西无法调试 但是为了看到代码的走向,可以加日志在认为有问题的代码下面加上日志(java工程写日志的小例子如下)。
public static void info(String msg) {
PrintStream outFile = null;
try {
//指向日志文件。FileOutputStream中append参数为true时,创建对象不会覆盖源文件,继续在文件的末尾追加写数据。
outFile = new PrintStream(new FileOutputStream("F:\\src\\log\\log.txt", true));
//改变输出方向,默认情况我们用System.out.println是会打印到控制台上.但是用了System.setOut,就会打印到你的文件中。参数必须为FileOutputStream类型。并且输入内容更为灵活。
System.setOut(outFile);
//日期当前时间
Date time = new Date();
//格式化日期
SimpleDateFormat sdfTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//转换格式
String strTime = sdfTime.format(time);
//内容输出到指定文件中
System.out.println(strTime + ":" + msg);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
认为有问题的地方加上日志写到文件里
经过测试发现2 的是没有问题的,输出的是正常的没有乱码,然后改成输出2的形式,C#调用,成功返回!!
简要总结下:平时C#接触的较多一些,打算从这个方向突破,结果失败,只好现学了IKVM 方式的调用,IKVM学会后 多了些尝试的方式,最后通过加断点的形式解决。也验证了一点,java直接跑跟C#调用dll 还是有一些区别的。