简体   繁体   中英

Loading C# DLL with unmanaged exports into Python

I've built a C# DLL (MyTestDll) using the NuGet package UnmanagedExports :

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

I use it from Python via ctypes DLL import:

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

It's just a fantasy, it works.

Then, I added one more DLL (NoSenseDll):

namespace NoSenseDll
{
    public class NoSenseClass
    {
        public static int Sum(int a, int b)
        {
            return a + b;
        }
    }
}

I started to use this NoSenseDll to implement MyTestDll:

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

Unfortunately, it does not work. Python says:

WindowsError: [Error -532462766] Windows Error 0xE043435

I've tried to add C:\\\\Temp\\\\Test to path, but that did not help.


I've written a C++ test:

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

It works if the second DLL (NoSenseDll) is in the same folder as the C++ executable. It does not work if I just add NoSenseDll folder to PATH.

Draft solution:

  1. Copy NoSenseDll to the folder of Python, in my case %HOMEPATH%\\Anaconda .
  2. Restart IPython/Spyder.

Final solution:

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

Final note:

If you can't use IronPython because of matplotlib or pandas,
if you can't use python.net because of IPython or spyder,
if you don't want to use COM Interop just because,
and you really want to get C# and Python to work together, use the solution above and C# reflection.

You can also check out Costura.Fody.

This is a build task that will add your dependencies as resources to your assembly and even hooks up a module initializer to load them at runtime.

you cant do it directly with managed code. register COM object from you dll:

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

and make com call from python. see here examples: http://www.codeproject.com/Articles/73880/Using-COM-Objects-in-Scripting-Languages-Part-Py

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM