IronPython C# linkage - c#

Can anyone tell me, how to use methods from C# in ironpython?
I know some basics but don't understand what to do if I don't want to build my C# project as .dll?
Code for the ConsoleApplication1.exe:
using System;
using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
namespace ConsoleApplication1 {
class Program
{
static void Main()
{
ScriptEngine engine = Python.CreateEngine();
engine.ExecuteFile("C:/Users/Vlad/Desktop/kal.py");
}
public int Plus(int a, int b)
{
int z = a + b;
Console.WriteLine(z);
return z;
}
}
}
//kal.py
print 'test1'
import clr
clr.LoadAssemblyFromFile("C://Users/Vlad/Documents/Visual Studio 2015/Projects/ConsoleApplication1/ConsoleApplication1/bin/Debug/ConsoleApplication1.exe")
import ConsoleApplication1
arith = ConsoleApplication1.Program.Plus(10,20)
print arith

just change type of C# application to Class library and reference it to your
python app.
from solution explorer right click on C# project note and click Properties and change appliction type to class library.
you can reference a library to the project from Project>add reference menu.

Related

Optional Argument of COM Add-in vs Automation Add-in Written in C#

I am working on a library of COM Add-in and Excel Automation Add-in, whose core codes are written in C#. I'd like to set an optional argument for the function and I know that this is legal for both C# and VBA, and even Excel WorksheetFunction. But I find that finally the optional argument works exclusively for COM and Automation add-in, meaning that if one add-in is run first, then works well but the optional argument of the other one will not work.
Below please see the example:
In the VS 2013 solution, I have two projects: one is called TestVBA and another one is called TestExcel.
TestVBA is for the COM add-in and built through the "Excel 2013 Add-in" and there are two .cs files:
ThisAddIn.cs
This file is generated automatically and modified a little bit. The codes are
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Excel = Microsoft.Office.Interop.Excel;
using Office = Microsoft.Office.Core;
using Microsoft.Office.Tools.Excel;
namespace TestVBA
{
public partial class ThisAddIn
{
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
}
private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
{
}
private ExcelVBA oExcelVBA;
protected override object RequestComAddInAutomationService()
{
if (oExcelVBA == null)
{
oExcelVBA = new ExcelVBA();
}
return oExcelVBA;
}
#region VSTO generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InternalStartup()
{
this.Startup += new System.EventHandler(ThisAddIn_Startup);
this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
}
#endregion
}
}
TestVBA.cs
This file is the main calculation file of COM add-in. The codes are
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;
using System.Reflection;
namespace TestVBA
{
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class ExcelVBA
{
public int TestAddVBA(int a = 1, int b = 1)
{
return a + b;
}
}
}
Another TestExcel is for the Excel Automation add-in and built through the C# "Class Library" and there are two .cs files either:
BaseUDF.cs
This file defines the decoration of two attributes. The codes are
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using Microsoft.Win32;
namespace BaseUDF
{
[ClassInterface(ClassInterfaceType.AutoDual)]
[ComVisible(true)]
public abstract class BaseUDF
{
[ComRegisterFunctionAttribute]
public static void RegisterFunction(Type type)
{
// Add the "Programmable" registry key under CLSID.
Registry.ClassesRoot.CreateSubKey(
GetSubKeyName(type, "Programmable"));
// Register the full path to mscoree.dll which makes Excel happier.
RegistryKey key = Registry.ClassesRoot.OpenSubKey(
GetSubKeyName(type, "InprocServer32"), true);
key.SetValue("",
System.Environment.SystemDirectory + #"\mscoree.dll",
RegistryValueKind.String);
}
[ComUnregisterFunctionAttribute]
public static void UnregisterFunction(Type type)
{
// Remove the "Programmable" registry key under CLSID.
Registry.ClassesRoot.DeleteSubKey(
GetSubKeyName(type, "Programmable"), false);
}
private static string GetSubKeyName(Type type,
string subKeyName)
{
System.Text.StringBuilder s =
new System.Text.StringBuilder();
s.Append(#"CLSID\{");
s.Append(type.GUID.ToString().ToUpper());
s.Append(#"}\");
s.Append(subKeyName);
return s.ToString();
}
// Hiding these methods from Excel.
[ComVisible(false)]
public override string ToString()
{
return base.ToString();
}
[ComVisible(false)]
public override bool Equals(object obj)
{
return base.Equals(obj);
}
[ComVisible(false)]
public override int GetHashCode()
{
return base.GetHashCode();
}
}
}
TestExcel.cs
This file is the main calculation file of Excel Automation add-in. The codes are
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Win32;
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;
using Extensibility;
namespace TestExcel
{
[Guid("7127696E-AB87-427a-BC85-AB3CBA301CF3")]
[ClassInterface(ClassInterfaceType.AutoDual)]
[ComVisible(true)]
public class TestExcel : BaseUDF.BaseUDF
{
public int TestAddExcel(int a = 1, int b = 1)
{
return a + b;
}
}
}
After building, the two add-ins have been registered in the system and in Excel we can use them successfully.
For the Automation add-in, we call them in the spreadsheet as =TestAddExcel(2,3) and =TestAddExcel() both of them work very well and give the right result 5 and 2. However, when I try to call the COM add-in via
Sub TestVBA_Click()
Dim addIn As COMAddIn
Dim TesthObj As Object
Set addIn = Application.COMAddIns("TestVBA")
Set TestObj = addIn.Object
Range("Output").Value2 = TestObj.TestAddVBA(2, 3)
Range("Output").Offset(1, 0).Value2 = TestObj.TestAddVBA()
End Sub
The first call with all arguments existing works well, but for the second one with arguments missing shows the error Type mismatch.
The interesting thing is, when I close the test excel file and open it again, this time I test the COM add-in first, still via the above VBA codes, both two calls work very well. Then when I test the two spreadsheet functions which used to work well, only the first one is good, the second one with arguments missing =TestAddExcel() fails with #VALUE!.
It would be very nice if someone can help with this strange issue.
I am not sure how you Referenced the class library without Registering for COM? I see now, you're using Late Binding. I didnt know you could do that (didn't think it would let you) and suspect that is the problem, it also matches the Type mismatch error.
Follow the second solution in my canonical answer here on the 3 methods to call .Net from Excel or VBA and make sure you Register for COM:
Click on the Build tab and check the check box that says “Register for COM Interop”. At this point you have an extra step if you are running on Windows Vista or higher. Visual Studio has to be run with administrator privileges to register for COM Interop. Save your project and exit Visual Studio. Then find Visual Studio in the Start menu and right click on it and choose “Run as Administrator”. Reopen your project in Visual Studio. Then choose “Build” to build the add-in.
Optionally if the above doesn't work, follow the third solution in my answer and reference the Automation Add-In and use Early Binding, I've tested this and it works perfectly:
Sub TestVBA1_Click()
Dim addIn As COMAddIn
Dim TesthObj As Object
Set addIn = Application.COMAddIns("TestVBA")
Set TestObj = addIn.Object
Debug.Print TestObj.TestAddVBA(2, 3)
Debug.Print TestObj.TestAddVBA()
Dim dotNetClass As TestExcel.TestExcel
Set dotNetClass = New TestExcel.TestExcel
Debug.Print dotNetClass.TestAddExcel(7, 3)
Debug.Print dotNetClass.TestAddExcel()
End Sub
This is a total stab in the dark, but can you create overloaded versions of the method to mimic the way you would have accomplished this before C# had optional parameters and see if that would work?
public int TestAddExcel(int a, int b)
{
return a + b;
}
public int TestAddExcel(int a)
{
return a + 1;
}
public int TestAddExcel()
{
return 2;
}

How can I compile a MATLAB exe with C#?

How can I use/start a MATLAB file, compiled to an exe-file, with C#? I have created a 3D plot in MATLAB, which I want to execute in C#. Is it possible?
This C# code I found:
using System;
using System.Collections.Generic;
using System.Text;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
// Create the MATLAB instance
MLApp.MLApp matlab = new MLApp.MLApp();
// Change to the directory where the function is located
matlab.Execute(#"cd c:\temp\example");
// Define the output
object result = null;
// Call the MATLAB function myfunc
matlab.Feval("myfunc", 2, out result, 3.14, 42.0, "world");
// Display result
object[] res = result as object[];
Console.WriteLine(res[0]);
Console.WriteLine(res[1]);
Console.ReadLine();
}
}
}
The Mathworks website from which you have your example also explains what you have to do to make it work.
1. Create a MATLAB function, myfunc, in the folder c:\temp\example.
function [x,y] = myfunc(a,b,c)
x = a + b;
y = sprintf('Hello %s',c);
Create the C# application.
In Microsoft® Visual Studio®, add a reference to your C# project to the MATLAB COM object. From the Project menu, select Add Reference.
Select the COM tab in the Add Reference dialog box.
Select the MATLAB application.

Exported functions from C# DLL not working

I have to export 3 basic methods from my DLL in C#, so it becomes accessible in C++:
OnPluginStart
OnPluginStop
PluginUpdate
So I found Unmanaged Exports a nice C# library that makes that easier.
So I went ahead with a sample code to test:
using System;
using System.IO;
using RGiesecke.DllExport;
namespace Plugins
{
public class Plugins
{
[DllExport("OnPluginStart", CallingConvention = CallingConvention.StdCall)]
public static void OnPluginStart()
{
using (var file = new StreamWriter(#"pluginLog.txt", true))
{
file.WriteLine("OnPluginStart");
}
}
[DllExport("OnPluginStop", CallingConvention = CallingConvention.StdCall)]
public static void OnPluginStop()
{
using (var file = new StreamWriter(#"pluginLog.txt", true))
{
file.WriteLine("OnPluginStop");
}
}
[DllExport("PluginUpdate", CallingConvention = CallingConvention.StdCall)]
public static void PluginUpdate(float dt)
{
using (var file = new StreamWriter(#"pluginLog.txt", true))
{
file.WriteLine("PluginUpdate");
}
}
}
}
However, when I compile my DLL and use DLL Exporter Viewer it doesn't list any of the exported functions and the application the DLL is loaded into also never runs my plugin.
What am I doing wrong here which makes my functions not being exported at all?
Your code works fine, apart from the fact that the code you posted does not compile. You omitted the using System.Runtime.InteropServices line. Dependency Walker for an x86 class library build of your (fixed) code says this:
The most obvious cause for the problem could be the following from the NuGet page for the library:
You have to set your platform target to either x86, ia64 or x64. AnyCPU assemblies cannot export functions.

Unity3D load an not exist DLL file , still work but no effect to the result

I tried to integrate my own c++ plugin with Unity3D project. But it doesn't work. So i wrote some small code to test, I found some strange problems, make me #$%##$^##$^#^
First I create a small dll project by code
#include "stdafx.h"
#define EXPORT_API __declspec(dllexport)
extern "C"
{
EXPORT_API void testFloatPoint(float *mFloat)
{
for(int i = 0 ; i<13;i++)
{
*mFloat = (float)i;
mFloat++;
}
mFloat = mFloat-13;
}
}
Then I tried to invoke this dll file in unity3D c# script by following code:
using UnityEngine;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Collections;
using System.IO;
public class TestDLL : MonoBehaviour
{
float[] pose_float;//
#region import c++ dll
[DllImport ("TestDLL2")]
public static extern void testFloatPoint ([In,Out] float[] pose_float);
#endregion
void Start ()
{
pose_float = new float[13];
testFloatPoint(pose_float);
print ("get pose_float data!!!");
for(int i=0;i<13;i++)
{
print(pose_float[i]);
}
}
}
but the print out msg are all zero. It's a bit strange, because before I got the result is "0,1,2,3....12". I can't figure out where the problem is . So i tried to remove the dll file from my unity3D project folder. Much more strange problem is, it can still run with all zero output result. There is no TestDLL2 file under the project folder and there is no dllnotfound problem.
Is there some advice from U? It bother me around one week , Thank you very much!

Would you share your idea how to call python command from embedded Python.Net?

I've been played with Python.Net for a week, but I can't find any sample code to use Python.Net in embedded way although Python.Net source has several embeddeding tests. I've searched many threads from the previous emailing list (Python.Net), the results are not consistent and are clueless.
What I'm trying to do is to get result (PyObject po) from C# code after executing python command such as 'print 2+3' from python prompt via Python.Net because IronPython doesn't have compatibility with the module that I currently using.
When I executed it from nPython.exe, it prints out 5 as I expected. However, when I run this code from embedded way from C#. it returns 'null' always. Would you give me some thoughts how I can get the execution result?
Thank you,
Spark.
Enviroments:
1. Windows 2008 R2, .Net 4.0. Compiled Python.Net with Python27, UCS2 at VS2012
2. nPython.exe works fine to run 'print 2+3'
using NUnit.Framework;
using Python.Runtime;
namespace CommonTest
{
[TestFixture]
public class PythonTests
{
public PythonTests()
{
}
[Test]
public void CommonPythonTests()
{
PythonEngine.Initialize();
IntPtr gs = PythonEngine.AcquireLock();
PyObject po = PythonEngine.RunString("print 2+3");
PythonEngine.ReleaseLock(gs);
PythonEngine.Shutdown();
}
}
}
It seems like PythonEngine.RunString() doesn't work. Instead, PythonEngine.RunSimpleString() works fine.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Reflection;
using Python.Runtime;
namespace npythontest
{
public class Program
{
static void Main(string[] args)
{
string external_file = "c:\\\\temp\\\\a.py";
Console.WriteLine("Hello World!");
PythonEngine.Initialize();
IntPtr pythonLock = PythonEngine.AcquireLock();
var mod = Python.Runtime.PythonEngine.ImportModule("os.path");
var ret = mod.InvokeMethod("join", new Python.Runtime.PyString("my"), new Python.Runtime.PyString("path"));
Console.WriteLine(mod);
Console.WriteLine(ret);
PythonEngine.RunSimpleString("import os.path\n");
PythonEngine.RunSimpleString("p = os.path.join(\"other\",\"path\")\n");
PythonEngine.RunSimpleString("print p\n");
PythonEngine.RunSimpleString("print 3+2");
PythonEngine.RunSimpleString("execfile('" + external_file + "')");
PythonEngine.ReleaseLock(pythonLock);
PythonEngine.Shutdown();
}
}
}

Categories