C#调用c++ dll读取2进制文件时而正常,时而异常
前言
这个问题发生在一款正在开发的病例查看软件,病例查看软件为了保护病人的一些数据,对病例文件进行了加密,这些文件和密钥同时进行分发(这是存在一定风险的,然方案之前就定了,修改麻烦)。
密钥的读取、文件的加密解密由cpp开发的encryption.dll进行。
问题现象
使用C#调用cpp的一个dll读取2进制文件密钥,然后根据密钥解码文本文件,发现某些病例解码后文件和未加密前是一致的,有些文件则出现乱码,导致不能使用。
排查过程
开始怀疑是,在调用的C#端进行加密解密重构,哪里可能出现了异常,因为之前通过注入时,由于scope管理的问题,导致病例id在查看时没有更新,于是读取到密钥不是当前的密钥,从而不能正常解密文件;然一通查下来,重构没有问题,id更新正常,加密解密的输入没有问题。
那么就怀疑加密模块encryption.dll可能是出了什么问题。
但单独将encryption的项目重新配置了下,以console程序的方式,在主函数里添加测试代码后发现一切正常……
这时就怀疑,C#端调用时获取到的密钥可能失真了,与c++端给出的密钥可能不一致。试了下,确实如此。
以下为C++获取的密钥:
以下为C#调用端拿到的C++返回的密钥,忽略左侧的日期。
原因终于找到 ……
原因分析
C++在加载密钥后,以字符串的方式返回给调用的C#方法,C#方法临时保存密钥在内存中,在解密时将密钥与输入文件地址当作参数,再传入C++程序中进行解密。
然而C++中的string字符串:C++ std::string
:可以包含任意字节(包括 \0
),是字节容器
而C#中字符串:C# string
:文本字符串
要试图让 C# string 和 C++ std::string 存储相同的二进制数据 - 这是不可能的任务,因为它们的根本设计目的不同。
解决方案
1. 不改变当前的密钥方式与存储方式的情况下,只能修改密钥的读取;C++程序加载密钥后,不再以字符串的方式返回给C#调用,而是以char数组或char*方式返回。
2. C++端直接不再返回密钥,而是自行在读取后保存在内存中,只将加密解密方法暴露出来即可。这应该是最符合面向对象原则的一种处理方法。
以上都是在还使用原c++ dll的情况下的,最差的情况,就是C#端自行实现密钥加载、加密解密方法。
建议
多语言混合编程时,一定要注意语言之间的差异。如像C++字符串与C#字符串间的差异,有符号的char与无符号的char的区别,内存的管理等等……