将带有非托管导出的c# DLL加载到Python中

本文关键字:DLL 加载 Python | 更新日期: 2023-09-27 18:17:58

我已经建立了一个c# DLL (MyTestDll)使用NuGet包UnmanagedExports:

[DllExport("Test", CallingConvention = CallingConvention.Cdecl)]
public static string Test(string name)
{
    return "hi " + name + "!";
}

我使用它从Python通过ctypes DLL导入:

path = "C:''Temp''Test"
os.chdir(path)
dll = ctypes.WinDLL("MyTestDll.dll")
f = dll.Test
f.restype = ctypes.c_char_p
print f('qqq')

这只是一个幻想,它成功了。

然后,我又添加了一个DLL (NoSenseDll):
namespace NoSenseDll
{
    public class NoSenseClass
    {
        public static int Sum(int a, int b)
        {
            return a + b;
        }
    }
}

我开始使用这个NoSenseDll来实现MyTestDll:

[DllExport("Test", CallingConvention = CallingConvention.Cdecl)]
public static string Test(string name)
{
    return NoSenseDll.NoSenseClass.Sum(4, 5).ToString();
}

不幸的是,它不起作用。Python说:

WindowsError: [Error -532462766] Windows Error 0xE043435

我已经尝试将C:''Temp''Test添加到path,但是没有帮助。


我写了一个c++测试:

#include "stdafx.h"
#include "windows.h"
#include <iostream>
#include <string>
#include "WinBase.h"
typedef char*(__stdcall *f_funci)(const char*);
int _tmain(int argc, _TCHAR* argv[])
{
    int t;
    std::string s = "C:''Temp''Test''MyTestDll.dll";
    HINSTANCE hGetProcIDDLL = LoadLibrary(std::wstring(s.begin(), s.end()).c_str());
    f_funci funci = (f_funci)GetProcAddress(hGetProcIDDLL, "Test");
    std::cout << "funci() returned " << funci(std::string("qqq").c_str()) << std::endl;
    std::cin >> t;
    return EXIT_SUCCESS;
}

如果第二个DLL (NoSenseDll)与c++可执行文件在同一文件夹中,则可以工作。它不工作,如果我只是添加NoSenseDll文件夹到PATH.

将带有非托管导出的c# DLL加载到Python中

解决方案草案:

  1. 复制NoSenseDll到Python的文件夹,在我的例子中是%HOMEPATH%'Anaconda
  2. 重启IPython/世爵。

最终解决方案:

static MyTestDllClass() // static constructor
{
    AppDomain currentDomain = AppDomain.CurrentDomain;
    currentDomain.AssemblyResolve += new ResolveEventHandler(LoadFromSameFolder);
}
static Assembly LoadFromSameFolder(object sender, ResolveEventArgs args)
{
    string folderPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
    string assemblyPath = Path.Combine(folderPath, new AssemblyName(args.Name).Name + ".dll");
    if (File.Exists(assemblyPath) == false) return null;
    Assembly assembly = Assembly.LoadFrom(assemblyPath);
    return assembly;
}
最终注意:

如果因为matplotlib或pandas而不能使用IronPython,
如果因为IPython或spyder而无法使用python.net,
如果你不想使用COM互操作只是因为,
如果你真的想让c#和Python一起工作,使用上面的解决方案和c#反射。

您也可以查看库斯托拉.福迪。

这是一个构建任务,它将把你的依赖项作为资源添加到你的程序集中,甚至挂起一个模块初始化器来在运行时加载它们。

您不能直接使用托管代码。从dll中注册COM对象:

%SystemRoot%'Microsoft.NET'Framework'v2.0.50727'regasm.exe my.dll /tlb:my.tlb /codebase

并从python发出com调用。示例如下:http://www.codeproject.com/Articles/73880/Using-COM-Objects-in-Scripting-Languages-Part-Py