• 【乱码】记一次C#调用Java乱码


    项目是使用 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 ""; //如果都不是,说明输入的内容不属于常见的编码格式。
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    通过测试,发现返回的是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
    
    • 1
    • 2
    • 3

    到这里基本已经不知道从哪查起了,迷茫了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();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    认为有问题的地方加上日志写到文件里
    在这里插入图片描述
    经过测试发现2 的是没有问题的,输出的是正常的没有乱码,然后改成输出2的形式,C#调用,成功返回!!

    简要总结下:平时C#接触的较多一些,打算从这个方向突破,结果失败,只好现学了IKVM 方式的调用,IKVM学会后 多了些尝试的方式,最后通过加断点的形式解决。也验证了一点,java直接跑跟C#调用dll 还是有一些区别的。

  • 相关阅读:
    Oracle数据加载工具SQL* loader
    AI音乐大模型:是创意的助力还是产业的挑战?
    【华为OD机试真题 JS】关联子串
    华为高级技术专家多年经验分享微服务治理体系、架构及实践文档
    MySQL基础终端命令与Python简单操作MySQL
    Java 设计模式之桥接模式
    C#拾遗补漏之goto跳转语句
    webpack插件开发必会Tapable
    基于householder变换的QR分解
    你不知道并且没听说过的js原生网页共享接口
  • 原文地址:https://blog.csdn.net/Bthm_123/article/details/127943394