使用非托管导出将C#DLL加载到Python中

时间:2015-10-16 16:12:30

标签: c# python .net dll unmanaged

我使用NuGet包UnmanagedExports构建了一个C#DLL(MyTestDll):

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

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

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添加到路径中,但这没有帮助。

我写了一个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,它就不起作用。

3 个答案:

答案 0 :(得分:4)

解决方案草案:

  1. 将NoSenseDll复制到Python文件夹中,在我的情况下为%HOMEPATH%\Anaconda
  2. 重启IPython / Spyder。
  3. 最终解决方案:

    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 Interop只是因为,
    你真的想让C#和Python一起工作,使用上面的解决方案和C#反射。

答案 1 :(得分:2)

您还可以查看Costura.Fody。

这是一个构建任务,它将依赖项作为资源添加到程序集中,甚至连接模块初始化程序以在运行时加载它们。

答案 2 :(得分:2)

您无法直接使用托管代码执行此操作。 注册你的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