64

Anyone can share a working example on how to call a simple C# library (actually its WPF) from python code? (I have tried using IronPython and had too much trouble with unsupported CPython library my python code is using so I thought of trying the other way around and calling my C# code from Python).

Here is the example I was playing with:

using System.Runtime.InteropServices;
using System.EnterpriseServices;

namespace DataViewerLibrary
{
    public interface ISimpleProvider
    {
       [DispIdAttribute(0)]
       void Start();
    }

    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.None)]
    public class PlotData : ServicedComponent, ISimpleProvider
    {
       public void Start()
       {
          Plot plotter = new Plot();
          plotter.ShowDialog();
       }
    }
}

Plotter is a WPF windows that plots an Ellipse

I don't know how to call this code from my python all. Any suggestions?

1

6 Answers 6

52

It is actually pretty easy. Just use NuGet to add the "UnmanagedExports" package to your .Net project. See https://sites.google.com/site/robertgiesecke/Home/uploads/unmanagedexports for details.

You can then export directly, without having to do a COM layer. Here is the sample C# code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using RGiesecke.DllExport;

class Test
{
    [DllExport("add", CallingConvention = CallingConvention.Cdecl)]
    public static int TestExport(int left, int right)
    {
        return left + right;
    }
}

You can then load the dll and call the exposed methods in Python (works for 2.7)

import ctypes
a = ctypes.cdll.LoadLibrary(source)
a.add(3, 5)
Sign up to request clarification or add additional context in comments.

6 Comments

Very cool if you're in control of the library. If not I imagine you can make a wrapper .dll with C-style calling convention.
I received next error: AttributeError: function 'add' not found
If you see the error AttributeError: function 'add' not found in Python, it is because it will not work with Any CPU. To fix, set the build type to x32 or x64 (depending on your version of Python). Then point at the right .dll, e.g. in the x64\Release folder.
I built my c# dll in x64. The dll has been created in two folders obj and bin. If i use the dll from bin I am getting clr exception "Exception has occurred: OSError [WinError -532462766] Windows Error 0xe0434352" If i use the dll from obj I dont get OSError but AttributeError: function 'add' not found error.
|
24

Since your post is tagged IronPython, if you want to use the sample C# the following should work.

import clr
clr.AddReference('assembly name here')
from DataViewerLibrary import PlotData 

p = PlotData()
p.Start()

1 Comment

The exact same code works with the last releases of Python for .Net. It's good to know that both solutions (IronPython and CPython+PythonNet) work the same way.
18

Python for .Net (pythonnet) may be a reasonable alternative to IronPython in your situation. https://github.com/pythonnet/pythonnet/blob/master/README.rst

From the site:

Note that this package does not implement Python as a first-class CLR language - it does not produce managed code (IL) from Python code. Rather, it is an integration of the CPython engine with the .NET runtime. This approach allows you to use use CLR services and continue to use existing Python code and C-based extensions while maintaining native execution speeds for Python code.

Also

Python for .NET uses the PYTHONPATH (sys.path) to look for assemblies to load, in addition to the usual application base and the GAC. To ensure that you can implicitly import an assembly, put the directory containing the assembly in sys.path.

This package still requires that you have a local CPython runtime on your machine. See the full Readme for more info https://github.com/pythonnet/pythonnet

Comments

13

This project has been developed for that exact purpose - use C# classes in regular Python

https://github.com/pythonnet/pythonnet/wiki

All you need to do is to install either MSI or EGG into your CPython. PyDotnet is Python module, so the executable stays regular python.exe from your installation of Python or Anaconda. Supported both 32bit and 64bit.

Unlimited access to all C# classes, methods with output and ref parameters, generic classes and generic methods, extension methods, private members.

Overloaded assembly loader with customized mechanics for searching assemblies.

.NET runtime type information convertible to class object, which can be instantiated as any other class.

Special import mode designed especially for Python interactive shell, which allows you to discover available assemblies, namespaces, classes, methods, etc.

I'm waiting for feedback:)

5 Comments

>>> import dotnet.seamless Traceback (most recent call last): File "<stdin>", line 1, in <module> File "C:\Program Files\Python35-32\lib\site-packages\dotnet_init_.py", line 21, in <module> import dotnet.moduleloader File "C:\Program Files\Python35-32\lib\site-packages\dotnet\moduleloader.py", line 24, in <module> from dotnet import PyDotnet as _dotnet ImportError: DLL load failed: The specified module could not be found.
Quick Solution Please rename boost_python3-vc141-mt-1_64.dll to boost_python3-vc150-mt-1_64.dll inside the sitepackages\dotnet. see this post for more detai: bitbucket.org/pydotnet/pydotnet/issues/26/…
bit bucket link is a 404 at this time
It is on pypi - but there it lacks a reference to numpy, so it works if pip install numpy before pip install pydotnet (in cmd as administrator, with just python3.9.1 installed).
Project is now on github.
5

Michael Baker already gave the correct answer.

Here is a working example:

The C# ClassLibrary code:
(don't forget the public keyword and mind the namespace, it becomes the python module name)

    namespace MyDotNetClassLib
    {
        public class Adder
        {
            public static int StaticAdd(int left, int right)
            {
                return left + right;
            }
            public int Add(int left, int right)
            {
                return left + right;
            }
        }
    }

The output is produced as ".\MyDotNetClassLib\bin\Debug\net7.0\MyDotNetClassLib.dll"

The python file: ".\PythonApplication\PythonApplication.py"
append the path to the dll file
AddReference the assembly name (usually dll filename without .dll)
import the module (which is the C# namespace)

    import clr
    from System import Console
    #from System import String
    #from System.Collections import *
    
    import sys
    sys.path.append('../MyDotNetClassLib/bin/Debug/net7.0/')
    clr.AddReference("MyDotNetClassLib")
    from MyDotNetClassLib import Adder 
    
    
    print()
    print("hello from python")
    Console.WriteLine("hello from C#")
    
    print(f"My C# Adder static: {Adder.StaticAdd(1,2)}")
    
    adder = Adder()
    print(f"My C# Adder method: {adder.Add(3,4)}")

The Console output when called:

    PS PythonApplication> python .\PythonApplication.py

    hello from python
    hello from C#
    My C# Adder static: 3
    My C# Adder method: 7

Compiled and run with:

  • VS 2022 / .NET 7.0
  • Python 3.10.11 (and don't forget to 'pip install pythonnet')

Comments

0

Calling a C# library from Python: use pythonnet to import C# libraries.

This fixes 'pythonnet cannot find module'.

pythonnet is the way to go but I have been struggling making it work for a long time!

By writing this new answer, I want to summarize how it finally worked for me with VS 2022 / .NET 8.0

Context: when you want more than just your simple class with functions - namely using functionality including a couple of nugets - my previous answer above is not enough.

Instead, the following procedure worked for me:

  1. Create your C# library as you always do, including required nugets (in my case class library, .Net 8.0, Target OS None). The namespace should be different from the library name (assembly name without dll ending). Make your classes public.

  2. Create an additional App that refers the library. This is required to get
    a) a runtimeconfig.json and
    b) a collection of all referred and therefore required assemblies.
    Just create a Console Application, call it 'ReferingApp' or similar and add a project reference to your library.

  3. Publish this App to a folder (context menu on Console Application Project, 'Publish...'). I didn't use "self-contained", it seams not necessary.

  4. Now you are ready on C# side. Install pythonnet.

    pip install pythonnet
    
  5. Start your python file with these lines and replace the < > parts.

    import os
    import sys
    from clr_loader import get_coreclr
    from pythonnet import set_runtime
    
    conf_path = <Path to the ReferingApp.runtimeconfig.json in the publish folder>
    
    conf_path = os.path.abspath(os.path.normpath(conf_path))
    if not os.path.exists(conf_path): 
        raise Exception(f"File does not exist: {conf_path}")
    
    rt = get_coreclr(runtime_config=conf_path)
    set_runtime(rt)
    
  6. Then continue with importing your library assembly (define assembly_path, again, use the publish directory so all dependencies can be found, ends with ".dll"):

    import clr
    
    assembly_path = <Path to the library assembly in the publish folder>
    
    assembly_path= os.path.abspath(os.path.normpath(assembly_path))
    if not os.path.exists(assembly_path): 
        raise Exception(f"Assembly not found at: {assembly_path}")
    
    sys.path.append(os.path.dirname(assembly_path))
    
    assembly_name = os.path.splitext(os.path.basename(assembly_path))[0]
    
    clr.AddReference(assembly_name)
    
  7. Finally import your C# class by using the C# namespace as module name (again, replace the namespace and class name):

    from ClassLibraryNS import Class1
    
  8. Use your library:

    result = Class1.hello()
    

Refer to the docs for dealing with Generics etc.
e.g.

from System.Collections.Generic import List
mylist = List[String]()

I have spent too long to find this solution, hope this helps the next ones.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.