【问题标题】:Loading C# DLL with unmanaged exports into Python将具有非托管导出的 C# DLL 加载到 Python 中
【发布时间】:2015-10-16 16:12:29
【问题描述】:

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

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

我通过 ctypes DLL import 从 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,它不起作用。

【问题讨论】:

  • 我尝试将 C:\\Temp\\Test 添加到路径中,但没有帮助。 你真的使用了双反斜杠吗?也许这可能是问题所在。只是猜测。
  • 我已尽力编辑您的问答,使其更易于理解,并将 NoSenceDll 修复为 NoSenseDll。感谢您为本问答所做的努力!

标签: c# python .net dll unmanaged


【解决方案1】:

解决方案草案:

  1. 将 NoSenseDll 复制到 Python 的文件夹中,在我的情况下为 %HOMEPATH%\Anaconda
  2. 重启 IPython/Spyder。

最终解决方案:

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# 反射。

【讨论】:

  • 您能指出 ipython 或 spyder 中 pythonnet 的问题吗?
【解决方案2】:

您还可以查看 Costura.Fody。

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

【讨论】:

  • 你能提供这个案例的例子吗?这与您的非托管出口有何不同?
  • 您仍然会使用我的包,但使用 Costura,您的依赖项将作为资源添加到您的程序集中。 Costura 添加了钩子以从那里加载它们。
【解决方案3】:

您不能直接使用托管代码来执行此操作。 从你的 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-03
    • 1970-01-01
    • 1970-01-01
    • 2010-10-14
    • 2010-11-15
    相关资源
    最近更新 更多