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

8

我使用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路径下,则无法正常工作。


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

4

解决方案草稿:

  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 Interop,
而你真的想让C#和Python一起工作,那么请使用上述解决方案和C#反射。


请问您能否指出在IPython或Spyder中使用Pythonnet时的问题? - denfromufa

2

您还可以查看Costura.Fody。

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


你能提供这个案例的示例吗?这与你们的非托管导出有多大不同? - denfromufa
1
你仍然可以使用我的软件包,但是使用Costura,你的依赖项将作为资源添加到你的程序集中。Costura添加了钩子来从那里加载它们。 - Robert Giesecke

2

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