C#与C++间无法正常传递加密文本,求问题定位与修复方案
问题根源与修复方案
你的问题核心是C++ DLL和C#调用之间的字符集不匹配,再加上C++代码里的几个细节错误,导致输入输出无法同时正常工作。我来一步步拆解问题,然后给你可行的修复方案:
先分析为什么会出现双向矛盾
你的C++函数参数是混合字符集的:输入是const char*(ANSI单字节),输出是wchar_t*(Unicode双字节),但C#的DllImport只能全局设置一种CharSet,这就必然导致一边匹配、另一边乱码:
- 当
CharSet=Ansi时:C#的string会被转成char*(输入正常),但输出的StringBuilder会按ANSI解析wchar_t*数据,双字节被拆成单字节,自然只显示错误字符; - 当
CharSet=Unicode时:C#的string会被转成wchar_t*,但C++函数接收的是char*,双字节数据被当成单字节解析,输入自然乱码,而输出的StringBuilder按Unicode处理wchar_t*,所以输出正常。
除此之外,你的C++代码还有两个关键错误:
- 冗余且危险的字符串转换:把
char*转成wchar_t*再转回std::string,完全没必要,还会破坏非ANSI字符的编码; swprintf调用错误:直接把加密后的宽字符串当成格式字符串传入,swprintf会把字符串里的%等字符当成格式占位符解析,导致输出乱码甚至崩溃,正确的用法应该是传入L"%s"作为格式字符串。
推荐修复方案(统一Unicode+Base64编码)
加密后的二进制数据不适合直接用宽字符串存储(可能包含非法Unicode字节),而且为了支持多语言字符,推荐统一使用Unicode字符集,同时将加密结果转成Base64字符串(二进制转ASCII,避免编码问题)。
1. 修改C++ DLL代码
#include <windows.h> #include <string> #include <vector> // 假设你已实现以下函数 std::string encrypt(const std::string& plaintext, const std::string& key, const std::string& iv); std::string base64_encode(const std::string& data); extern "C" __declspec(dllexport) void aes_crypter(const wchar_t* string_in, wchar_t* string_out, size_t out_buffer_size) { // 1. 将输入宽字符串转成UTF-8字节流(适合加密处理,支持全语言) int utf8_len = WideCharToMultiByte(CP_UTF8, 0, string_in, -1, nullptr, 0, nullptr, nullptr); if (utf8_len == 0) { string_out[0] = L'\0'; return; } std::vector<char> utf8_buf(utf8_len); WideCharToMultiByte(CP_UTF8, 0, string_in, -1, utf8_buf.data(), utf8_len, nullptr, nullptr); std::string plaintext(utf8_buf.data()); // 2. 执行AES加密 std::string encrypted_data = encrypt(plaintext, "abcdefghijklmnopqrstuvwxyz123412", "612345601234512"); // 3. 二进制加密结果转Base64字符串(避免二进制数据的编码冲突) std::string base64_result = base64_encode(encrypted_data); // 4. Base64字符串转回宽字符串输出 int wide_len = MultiByteToWideChar(CP_UTF8, 0, base64_result.c_str(), -1, nullptr, 0); if (wide_len == 0) { string_out[0] = L'\0'; return; } // 确保不超出缓冲区大小 wide_len = wide_len > out_buffer_size ? out_buffer_size : wide_len; MultiByteToWideChar(CP_UTF8, 0, base64_result.c_str(), -1, string_out, wide_len); string_out[out_buffer_size - 1] = L'\0'; // 强制添加终止符,防止溢出 }
2. 修改C#调用代码
using System; using System.Runtime.InteropServices; using System.Text; class AESHelper { [DllImport("simpleAES.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] private static extern void aes_crypter(string string_in, StringBuilder string_out, int out_buffer_size); public static string EncryptString(string input) { const int BufferSize = 4096; var resultBuilder = new StringBuilder(BufferSize); aes_crypter(input, resultBuilder, BufferSize); return resultBuilder.ToString(); } }
临时快速修复(最小改动)
如果你暂时不想重构加密逻辑,只需要解决当前调用问题,可以做两处小修改:
- 修正C++里的
swprintf调用:swprintf(string_out, 4096, L"%s", output_crypt); // 添加L"%s"作为格式字符串 - 把C++函数的输入参数改成
const wchar_t*,统一用Unicode:extern "C" __declspec(dllexport) void aes_crypter(const wchar_t* string_in, wchar_t* string_out) { std::wstring ws(string_in); std::string str(ws.begin(), ws.end()); auto encr = encrypt(str, X_KEY, X_PSS); std::wstring widestr(encr.begin(), encr.end()); swprintf(string_out, 4096, L"%s", widestr.c_str()); } - C#调用保持
CharSet=Unicode:[DllImport("simpleAES.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] private static extern void aes_crypter(string string_in, StringBuilder string_out);
注意:这种临时方案只适合输入是ASCII字符的场景,非ASCII字符会丢失,长期来看还是推荐用UTF-8+Base64的方案。
内容的提问来源于stack exchange,提问作者Samuel Hardson
相关产品推荐
相关产品推荐

