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

11

我想在我的Python程序的关键部分中实现C#代码,以使其更快。Python文档和此网站上都提到可以按如下方式加载动态链接库 (PyDocs也是如此):

cdll.LoadLibrary("your-dll-goes-here.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个回答

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;
    }
}

你可以加载DLL文件并在Python中调用其中暴露的方法(适用于2.7版本)

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

如果库无法从Python中执行,小提示是:“您必须将平台目标设置为x86、ia64或x64。任何CPU程序集都无法导出函数。” - Ostap Elyashevskyy

11

关于使用ctypes从Python调用DLL的技巧,大多数情况下是基于DLL是用C或C++编写的,而不是C#。如果使用C#,则需要引入整个CLR机制,符号很可能会被重编码且不符合ctypes的预期,从输出数组的垃圾回收中你将会遇到各种麻烦。

我在使用pythonnet(http://pythonnet.sf.net)将Python和C#代码进行接口时取得了非常好的成功,您可以尝试一下。

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


3

有人指出,C# DLL不能像本地DLL一样处理。

您可以将C#功能导出为COM对象,Python可以轻松使用。

个人认为应考虑使用本地代码解决方案,但您可能已经过于执着于C#而不能改变目前的课程。


1

ctypes 用于加载用 C 编写的共享库(或者至少是直接可执行的 CPU 共享库,并按照标准 C 约定导出其符号和函数参数)。C# DLL 不是“普通”的 DLL,而是旨在在 .NET 环境中运行。

您有以下选项:

  1. 使用某种 Python 到 .NET 的桥接。
  2. 使用 C 编写 DLL 并使用 ctypes 调用函数并处理数据转换。
  3. 使用 Python/C API 并创建可加载的 Python 模块。

我无法对选项 #1 进行评论,因为我没有尝试过。选项 #2 通常比选项 #3 更容易,因为您可以编写纯 C 代码并使用 ctypes 进行粘合。选项 #3 将提供所有三个选项中最佳的性能,因为它具有最低的调用开销,并在处理器上运行本地代码。


1

C# DLL实际上被称为程序集,因此与其它语言有很大的不同。 除非有非常重要的理由,否则建议您使用C++。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接