在Python中使用ctypes访问C# dll的方法

11 投票
5 回答
17842 浏览
提问于 2025-04-16 21:40

我想在我的Python程序的一个关键部分实现C#代码,以提高速度。根据Python的文档和这个网站的说法,你可以像下面这样加载一个动态链接库(DLL):

cdll.LoadLibrary("你的-dll-文件名.dll")

这是我处理这个功能的代码部分:

from ctypes import *
z = [0.0,0.0]
c = [LEFT+x*(RIGHT-LEFT)/self.size, UP+y*(DOWN-UP)/self.size]
M = 2.0

iterator = cdll.LoadLibrary("RECERCATOOLS.dll")

array_result = iterator.Program.ITERATE(z[0],z[1],c[0],c[1],self.iterations,M)

z = complex(array_result[0],array_result[1])
c = complex(array_result[2],array_result[3])
last_iteration = int(round(array_result[4]))

我使用的RECERCATOOLS.dll是这个(C#代码,不是C或C++):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using KarlsTools;

public class Program
{
    public static Array ITERATE(double z_r,double z_i,double c_r,
                        double c_i, int iterations, 
                        double limit)
    {
        Complex z = new Complex(z_r, z_i);
        Complex c = new Complex(c_r, c_i);

        for (double i = 1; Math.Round(i) <= iterations; i++)
        {
            z = Complex.Pow(z, 2) + c;
            if (Complex.Abs(z) < limit)
            {
                double[] numbers = new double[] { Complex.Real(z),
                                                  Complex.Imag(z),
                                                  Complex.Real(c),
                                                  Complex.Imag(c),
                                                  i};
                return numbers;
            }
        }
        double iter = iterations;
        double[] result = new double[]        { Complex.Real(z),
                                                  Complex.Imag(z),
                                                  Complex.Real(c),
                                                  Complex.Imag(c),
                                                  iter};
        return result;
    }
}

为了构建这个DLL,我在Visual Studio 2010项目中使用“Build”命令,这个项目只包含这个文件和一个对“Karlstools”的引用,这个模块让我可以使用复数。

我不知道为什么,当我尝试运行我的Python代码时,它总是抛出一个异常:

    [...]
    array_result = iterator.Program.ITERATE(z[0],z[1],c[0],c[1],self.iterations,M)
  File "C:\Python32\lib\ctypes\__init__.py", line 353, in __getattr__
    func = self.__getitem__(name)
  File "C:\Python32\lib\ctypes\__init__.py", line 358, in __getitem__
    func = self._FuncPtr((name_or_ordinal, self))
AttributeError: function 'Program' not found

我需要对此进行帮助,因为即使所有设置为public,函数设置为static,或者我尝试直接访问函数而不指定“Program”类,它仍然会抛出异常……我完全不知道问题出在哪里。

5 个回答

3

其他人提到,C#写的DLL和普通的本地DLL不能一样对待。

你可以选择把你的C#功能导出为一个COM对象,这样Python就可以很方便地使用它。

我个人会考虑用本地代码的解决方案,但你可能已经在C#上投入了很多精力,所以现在不太想改变方向。

13

其实这很简单。只需要通过NuGet给你的.Net项目添加一个叫“UnmanagedExports”的包就可以了。具体的步骤可以查看这个链接:https://sites.google.com/site/robertgiesecke/Home/uploads/unmanagedexports

这样你就可以直接导出,而不需要做COM层。下面是一个示例的C#代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using RGiesecke.DllExport;

class Test
{
    [DllExport("add", CallingConvention = CallingConvention.Cdecl)]
    public static int TestExport(int left, int right)
    {
        return left + right;
    }
}

然后你可以在Python中加载这个dll,并调用那些暴露出来的方法(适用于2.7版本)。

import ctypes
a = ctypes.cdll.LoadLibrary(source)
a.add(3, 5)
11

关于如何在Python中使用ctypes调用DLL,很多建议都是基于DLL是用C或C++写的,而不是C#。因为用C#时,你需要引入整个CLR的机制,这样符号可能会被混淆,不符合ctypes的预期,这样就会遇到各种问题,比如输出数组的垃圾回收。

我在用Python和C#代码进行交互时,使用了python for dot net(http://pythonnet.sf.net),效果非常好,你可以试试这个方法。

另一方面,如果你追求纯粹的性能,可以考虑将你的代码重写为Python的本地C扩展,使用Python/C API(http://docs.python.org/c-api/)。

撰写回答