C#调用Delphi DLL时如何覆盖字符串封送的默认编码?
解决C# .NET Core 7调用Delphi 2007 DLL时指定WIN1250编码封送字符串的问题
Delphi 2007中的PChar对应WIN1250编码的ANSI字符,但C#默认的ANSI字符集封送会使用系统当前代码页,导致非WIN1250环境下字符传递异常。以下是针对输入、输出参数的手动编码控制方案:
一、输入参数的处理
CLR自动封送字符串无法指定编码,需手动将C#字符串转换为WIN1250编码的字节数组,再分配非托管内存传递:
- 修改DLL导入定义,用
IntPtr替代string作为参数类型:
[DllImport(@"LibraryName.dll", CallingConvention = CallingConvention.StdCall)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool TestSignString(IntPtr s, IntPtr sign);
- 调用时手动转换并管理内存:
// 定义WIN1250编码 Encoding win1250 = Encoding.GetEncoding(1250); string inputS = "待传递的字符串"; string inputSign = "签名内容"; // 将字符串转为WIN1250字节数组,添加C风格终止符'\0' byte[] sBytes = win1250.GetBytes(inputS + '\0'); byte[] signBytes = win1250.GetBytes(inputSign + '\0'); // 分配非托管内存 IntPtr sPtr = Marshal.AllocHGlobal(sBytes.Length); IntPtr signPtr = Marshal.AllocHGlobal(signBytes.Length); try { // 将字节数组复制到非托管内存 Marshal.Copy(sBytes, 0, sPtr, sBytes.Length); Marshal.Copy(signBytes, 0, signPtr, signBytes.Length); // 调用DLL函数 bool result = TestSignString(sPtr, signPtr); } finally { // 释放非托管内存,避免泄漏 Marshal.FreeHGlobal(sPtr); Marshal.FreeHGlobal(signPtr); }
二、输出参数的处理
如果DLL函数需要向C#返回字符串(通过输出参数写入缓冲区),需提前分配足够的非托管内存,调用后用WIN1250编码读取内容:
假设Delphi DLL有如下输出函数:
function TestGetSignedString(input: PChar; outputBuffer: PChar; bufferSize: Integer): LongBool; stdcall;
- C#导入定义:
[DllImport(@"LibraryName.dll", CallingConvention = CallingConvention.StdCall)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool TestGetSignedString(IntPtr input, IntPtr outputBuffer, int bufferSize);
- 调用时处理输出:
Encoding win1250 = Encoding.GetEncoding(1250); string inputStr = "输入字符串"; int bufferSize = 256; // 根据实际需求设置足够大的缓冲区 // 转换输入字符串 byte[] inputBytes = win1250.GetBytes(inputStr + '\0'); IntPtr inputPtr = Marshal.AllocHGlobal(inputBytes.Length); Marshal.Copy(inputBytes, 0, inputPtr, inputBytes.Length); // 分配输出缓冲区 IntPtr outputPtr = Marshal.AllocHGlobal(bufferSize); try { bool success = TestGetSignedString(inputPtr, outputPtr, bufferSize); if (success) { // 从非托管内存读取字节,直到遇到终止符'\0' List<byte> outputBytes = new List<byte>(); int offset = 0; while (true) { byte b = Marshal.ReadByte(outputPtr, offset); if (b == 0) break; outputBytes.Add(b); offset++; if (offset >= bufferSize) break; // 防止缓冲区溢出 } // 将字节数组转为C#字符串 string outputStr = win1250.GetString(outputBytes.ToArray()); } } finally { // 释放所有非托管内存 Marshal.FreeHGlobal(inputPtr); Marshal.FreeHGlobal(outputPtr); }
关键说明
- 必须手动管理非托管内存,调用完成后务必释放,避免内存泄漏。
Encoding.GetEncoding(1250)依赖系统支持该代码页,WIN1250为常见中欧编码,多数系统默认支持。
内容的提问来源于stack exchange,提问作者J. Ogurek
相关产品推荐
相关产品推荐

