如何实现Python向C#传递可变长度字符串数组?(基于ctypes与Unmanaged Exports)
我来给你一个亲测可行的方案,解决用Unmanaged Exports配合ctypes从Python向C#传递可变长度字符串列表并获取返回值的问题。之前你试了四种方法都没成,大概率是在内存管理、编码或者调用约定上踩了坑,下面一步步来实现:
可行解决方案:Python → C# 传递可变长度字符串列表并获取返回值
核心思路是:用Unmanaged Exports导出符合C调用约定的C#函数,Python端通过ctypes构造非托管字符串数组传递给C#,C#解析处理后返回字符串,同时做好内存管理避免泄漏。
C# 端实现
首先确保你的C#项目已经安装了UnmanagedExports NuGet包,然后编写如下代码:
using System; using System.Linq; using RGiesecke.DllExport; using System.Runtime.InteropServices; namespace StringListProcessor { public class StringHandler { // 导出处理字符串列表的函数,使用C调用约定适配Python的ctypes [DllExport("ProcessStringList", CallingConvention = CallingConvention.Cdecl)] public static IntPtr ProcessStringList(IntPtr[] stringPtrArray, int arrayLength) { // 将非托管指针数组转换为C#字符串数组 var inputStrings = new string[arrayLength]; for (int i = 0; i < arrayLength; i++) { // 按UTF-8编码解析指针指向的非托管字符串 inputStrings[i] = Marshal.PtrToStringUTF8(stringPtrArray[i]); } // 这里替换成你的实际业务逻辑,示例为拼接所有输入字符串 string result = $"处理结果:{string.Join(" | ", inputStrings)}"; // 将结果字符串分配到非托管内存,返回指针给Python return Marshal.StringToHGlobalUTF8(result); } // 导出内存释放函数,让Python可以清理C#分配的非托管内存 [DllExport("FreeUnmanagedString", CallingConvention = CallingConvention.Cdecl)] public static void FreeUnmanagedString(IntPtr stringPtr) { Marshal.FreeHGlobal(stringPtr); } } }
C# 端关键点说明
- 调用约定必须和Python一致:指定
CallingConvention.Cdecl,ctypes默认用这个约定,避免栈溢出错误。 - 编码统一:用UTF-8编码在Python和C#间传递字符串,确保两边解析/编码方式匹配。
- 内存管理:C#用
Marshal.StringToHGlobalUTF8分配的非托管内存,必须由Python主动释放,所以额外导出FreeUnmanagedString函数。
Python 端实现
用ctypes调用C#编译好的DLL,代码如下:
import ctypes # 加载编译后的C# DLL,替换成你的实际DLL路径 string_processor_dll = ctypes.CDLL("./StringListProcessor.dll") # 定义函数的参数类型和返回类型,这一步非常重要,避免ctypes错误解析参数 # 处理字符串列表的函数:参数是void*数组 + int,返回void* string_processor_dll.ProcessStringList.argtypes = (ctypes.POINTER(ctypes.c_void_p), ctypes.c_int) string_processor_dll.ProcessStringList.restype = ctypes.c_void_p # 内存释放函数:参数是void*,无返回值 string_processor_dll.FreeUnmanagedString.argtypes = (ctypes.c_void_p,) string_processor_dll.FreeUnmanagedString.restype = None def send_string_list_to_csharp(str_list): # 将Python字符串编码为UTF-8字节,转为ctypes的c_char_p(对应非托管指针) c_strings = [ctypes.c_char_p(s.encode("utf-8")) for s in str_list] # 构造指针数组,用于传递给C# string_ptr_array = (ctypes.c_void_p * len(c_strings))(*c_strings) # 调用C#函数 result_ptr = string_processor_dll.ProcessStringList(string_ptr_array, len(c_strings)) # 将非托管指针指向的字节解码为Python字符串 result = ctypes.string_at(result_ptr).decode("utf-8") # 释放C#分配的非托管内存,避免泄漏 string_processor_dll.FreeUnmanagedString(result_ptr) return result # 测试示例 if __name__ == "__main__": test_list = ["苹果", "Banana", "Cherry", "Python → C#"] output = send_string_list_to_csharp(test_list) print(output) # 预期输出:处理结果:苹果 | Banana | Cherry | Python → C#
Python 端关键点说明
- 必须指定
argtypes和restype:不指定的话,ctypes会自动推断参数类型,很容易导致内存错误或崩溃。 - 编码匹配:Python字符串用
encode("utf-8")转字节,和C#端的Marshal.PtrToStringUTF8对应。 - 内存释放:调用完C#函数后一定要调用
FreeUnmanagedString,否则每次调用都会残留内存。
额外注意事项
- 位数匹配:C#项目编译的位数(x86/x64)必须和你的Python版本一致,否则会出现DLL加载失败。
- 调试技巧:如果运行出错,可以先检查DLL路径是否正确,然后确认参数类型、调用约定、编码是否完全匹配。
内容的提问来源于stack exchange,提问作者Dimitri Shvorob
相关产品推荐
相关产品推荐

