You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 07:41:55