How do you call a managed (C#) function from C++? - c#

I have a C# DLL file project (my_cs_dll.dll) which defines a static class with a static member function.
namespace Foo
{
public static class Bar
{
public static double GetNumber() { return 1.0; }
}
}
I also have a C++ DLL project which is using /clr.
#using <my_cs_dll.dll>
double get_number_from_cs() { return Foo::Bar::GetNumber(); }
I've added a reference to 'my_cs_dll.dll' in the C++ project Common Properties references section (copy local/copy dependencies are both True).
And I've also added the path to 'my_cs_dll.dll' in the C++ project Configuration Properties C/C++ General 'Resolve#using References' section.
Everything builds without error, however at runtime I keep getting a 'System.IO.FileNotFound' exception from the system claiming it can't find the my_cs_dll.dll assembly.
Both DLL files are definitely present in the same directory from which I'm running.
I have tried all sorts of variations on the settings mentioned above and read everything I could find on manged/unmanaged interop, but I can't seem to get my brain around what is wrong...
I'm using Visual Studio 2008 and .NET 3.5.

It sounds like your C# assembly is not being resolved at runtime. Is your C# dll in the same directory as (or a subdirectory of) your executable? It's been a while since I did this, but my recollection is that unless your assembly is installed in the GAC, it must be in the directory (or a subdirectory) where your executable is located, as opposed to the location of the dll that's using it. This has to do with the .NET security features.
If you are still having problems, you can try using resolving the assembly yourself. In your clr-enabled C++ project, try adding the following:
using namespace System;
using namespace System.Reflection;
void Resolve()
{
AppDomain::CurrentDomain->AssemblyResolve +=
gcnew ResolveEventHandler(OnAssemblyResolve);
}
Assembly ^OnAssemblyResolve(Object ^obj, ResolveEventArgs ^args)
{
#ifdef _DEBUG
String ^path = gcnew String(_T("<path to your debug directory>"));
#else
String ^path = gcnew String(_T("<path to your release directory>"));
#endif
array<String^>^ assemblies =
System::IO::Directory::GetFiles(path, _T("*.dll"));
for (long ii = 0; ii < assemblies->Length; ii++) {
AssemblyName ^name = AssemblyName::GetAssemblyName(assemblies[ii]);
if (AssemblyName::ReferenceMatchesDefinition(gcnew AssemblyName(args->Name), name)) {
return Assembly::Load(name);
}
}
return nullptr;
}
You may have to tweak the code a little bit to get it to compile in your project. In my case, I made the two functions static methods of a class in my clr-enabled project. Just make sure you call the Resolve() function early on in your code, i.e., before you try to call get_number_from_cs().
While using COM is an option, it is not necessary. You're on the right path with your current approach. If you want some hand-holding, take a look at this CodeProject example. It's the one I following to get my unmanaged application to use my managed assemblies.

Related

Retrieve <Version> tag written in csproj file from dll [duplicate]

I am trying to get the executing assembly version in C# 3.0 using the following code:
var assemblyFullName = Assembly.GetExecutingAssembly().FullName;
var version = assemblyFullName .Split(',')[1].Split('=')[1];
Is there another proper way of doing so?
Two options... regardless of application type you can always invoke:
Assembly.GetExecutingAssembly().GetName().Version
If a Windows Forms application, you can always access via application if looking specifically for product version.
Application.ProductVersion
Using GetExecutingAssembly for an assembly reference is not always an option. As such, I personally find it useful to create a static helper class in projects where I may need to reference the underlying assembly or assembly version:
// A sample assembly reference class that would exist in the `Core` project.
public static class CoreAssembly
{
public static readonly Assembly Reference = typeof(CoreAssembly).Assembly;
public static readonly Version Version = Reference.GetName().Version;
}
Then I can cleanly reference CoreAssembly.Version in my code as required.
In MSDN, Assembly.GetExecutingAssembly Method, is remark about method "getexecutingassembly", that for performance reasons, you should call this method only when you do not know at design time what assembly is currently executing.
The recommended way to retrieve an Assembly object that represents the current assembly is to use the Type.Assembly property of a type found in the assembly.
The following example illustrates:
using System;
using System.Reflection;
public class Example
{
public static void Main()
{
Console.WriteLine("The version of the currently executing assembly is: {0}",
typeof(Example).Assembly.GetName().Version);
}
}
/* This example produces output similar to the following:
The version of the currently executing assembly is: 1.1.0.0
Of course this is very similar to the answer with helper class "public static class CoreAssembly", but, if you know at least one type of executing assembly, it isn't mandatory to create a helper class, and it saves your time.
using System.Reflection;
{
string version = Assembly.GetEntryAssembly().GetName().Version.ToString();
}
Remarks from MSDN http://msdn.microsoft.com/en-us/library/system.reflection.assembly.getentryassembly%28v=vs.110%29.aspx:
The GetEntryAssembly method can return null when a managed assembly has been loaded from an unmanaged application. For example, if an unmanaged application creates an instance of a COM component written in C#, a call to the GetEntryAssembly method from the C# component returns null, because the entry point for the process was unmanaged code rather than a managed assembly.
Product Version may be preferred if you're using versioning via GitVersion or other versioning software.
To get this from within your class library you can call System.Diagnostics.FileVersionInfo.ProductVersion:
using System.Diagnostics;
using System.Reflection;
//...
var assemblyLocation = Assembly.GetExecutingAssembly().Location;
var productVersion = FileVersionInfo.GetVersionInfo(assemblyLocation).ProductVersion
This should do:
Assembly assem = Assembly.GetExecutingAssembly();
AssemblyName aName = assem.GetName();
return aName.Version.ToString();
I finally settled on typeof(MyClass).GetTypeInfo().Assembly.GetName().Version for a netstandard1.6 app. All of the other proposed answers presented a partial solution. This is the only thing that got me exactly what I needed.
Sourced from a combination of places:
https://msdn.microsoft.com/en-us/library/x4cw969y(v=vs.110).aspx
https://msdn.microsoft.com/en-us/library/2exyydhb(v=vs.110).aspx

Java JNI call to C# COM fails, when COM is registered without codebase option of regasm

Function calls from Java to C# through JNI-C++/CLI are failing when the C# COM is not registered using regasm with the codebase option. I've built a sample following the instructions in P2: Calling C# from Java with some changes.
Numero uno: C#
Change the C# dll into a COM by creating an interface, IRunner, and making the library assembly COM-visible.
namespace RunnerCOM
{
public interface IRunner
{
String ping();
}
public class Runner:IRunner
{
static void Main(string[] args)
{
}
public Runner() { }
public String ping()
{
return "Alive (C#)";
}
}
}
Numero due: Java
No changes made to the Java section.
Numero tre: C++
This part was changed to create a new instance of the RunnerCOM.Runner class and use that result. Here is a good tutorial on how to call managed code from unmanaged code: http://support.microsoft.com/kb/828736
#include "stdafx.h"
#include "Runner.h"
#pragma once
#using <mscorlib.dll>
#import "RunnerCOM.tlb"
JNIEXPORT jstring JNICALL Java_Runner_ping(JNIEnv *env, jobject obj){
RunnerCOM::IRunnerPtr t = RunnerCOM::IRunnerPtr("RunnerCOM.Runner");
BSTR ping = t->ping();
_bstr_t temp(ping, true);
char cap[128];
for(unsigned int i=0;i<temp.length();i++){
cap[i] = (char)ping[i];
}
return env->NewStringUTF(cap);
}
Now to my questions,
The code above fails with a _com_error exception, Class not registered (0x80040154) unless the codebase option is enabled during regsitration of RunnerCOM.dll, with regasm.exe. Why is this? If the code is not ran from JNI, I tested it as an exe, it works fine. The RunnerCOM.dll is simply found in the working directory.
Type casting _bstr_t temp to char* fails. For example, char *out = (char*) temp; Similar to the issue above, it works fine when it's built and executed as an exe but crashes the JVM when it's a JNI call.
By the way this is what I used to run it as an executable:
int main(){
RunnerCOM::IRunnerPtr t = RunnerCOM::IRunnerPtr("RunnerCOM.Runner");
BSTR ping = t->ping();
_bstr_t temp(ping, false);
printf(temp);
return 0;
}
Codebase creates a Codebase entry in the registry. The Codebase entry specifies the file path for an assembly that is not installed in the global assembly cache, so when you specify the codebase, the system will find the DLL based on the path. If not, it will try to locate the dll in the GAC and current working directory. In JNI, I think the current working directory is not the folder where the DLL is. You can use process explorer to find what is the current working directory, also, you can use process monitor to find out which directories the exe is looking into to find the dll.
The code converting _bstr_t to char*, the char* string cap is not ended with '\0', I think this might cause problem in JNI. Uses the _bstr_t operator (char *), you can obtain a null terminated string from the _bstr_t object. Please check the msdn example for details.
You mentioned C++/CLI, C++/Cli and COM warpper are two different ways to interop with C# code. If you're using C++/CLI as a bridge, you doesn't need to register C# DLL as COM, please see this: Calling .Net Dlls from Java code without using regasm.
If you're using COM, you should call CoInitialize() to init COM first in your code.

C# external library (Lua) call problem

I'm new to programming in C# (VS2010) .Net (4.0) and I'm encountering I couldn't solve by myself since some days already.
I'm using an external scripting language (Lua) in my C# code.
To do so I use LuaInterpreter built for .Net 4.0
First try:
The project is a console application -> the program works fine when I try to call a Lua class.
Second try:
The project is a class Librrary COM used from Excel -> The class library compile fine and my user defined functions work fine within Excel. But when I try to call a Lua class it crashed saying that the Lua assembly is missing.
Could not load file or assembly 'lua51, Version=0.0.0.0, Culture=neutral, PublicKeyToken=1e1fb15b02227b8a' or one of its dependencies. Strong name validation failed. (Exception from HRESULT: 0x8013141A)
To reproduce the problem :
1- You need to get LuaInterface .Net 4.0 from
http://www.mdome.org/2011/05/16/luainterface-for-csharp-net-4-custom-build/
2- Add LuaInterface as a reference in your project
3- Copy the Lua51 DLL in the building directory (I put my Excel sheet there too)
4- Copy the code for the Class Library
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using Excel = Microsoft.Office.Interop.Excel;
using LuaInterface;
namespace POC
{
[ClassInterface(ClassInterfaceType.AutoDual)]
[ComVisible(true)]
public class Functions
{
public int test()
{
Lua lua = new Lua();
return 0;
}
#region Class in Excel
[ComRegisterFunctionAttribute]
public static void RegisterFunction(Type type)
{
Registry.ClassesRoot.CreateSubKey(
GetSubKeyName(type, "Programmable"));
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)
{
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();
}
#endregion
}
}
The function that crashed is the test function when called from Excel
I would take any help on that
Thanks
SInce it appears to be signed, try to put Lua51 into the GAC and see if it works. Probably you can try even by putting Lua15.dll in the same path of excel.exe.
I've had lots of issues with .NET, LuaInterface, and Lua5.1 interracting on 64-bit machines. Lua5.1 only compiles 32-bit and this requires you to (I believe) build the LuaInterface project as 32-bit as well. Try changing "Project -> Properties -> Build -> Platform Target" to "x86" in your .NET projects.

IronPython Excel-Dna Add-In - Exception regarding Microsoft.Dynamic reference

I am getting started with developing an Excel-DNA addin using IronPython with some C# as a wrapper for the calls to IronPython. With the generous help of the Excel-DNA developer, I have worked through some of the initial kinks of getting a sample up and running, but now I am trying to debug the addin in SharpDevelop, and I'm running into some problems. As I'm completely new to most of this, I'm not really sure if it is an issue with SharpDevelop, .NET, Excel-DNA or IronPython.
I have created two projects in one solution, one is a C# class library. The other is a python class library. I setup the project to debug following a tutorial I found on a blog. I am able to step through the first few lines of C# code, so that is progress, but when I get to the following line:
pyEngine.Runtime.LoadAssembly(myclass);
I get an exception:
"Could not load file or assembly
'Microsoft.Dynamic, Version=1.0.0.0,
Culture=neutral,
PublicKeyToken=31bf3856ad364e35' or
one of its dependencies. The located
assembly's manifest definition does
not match the assembly reference.
(Exception from HRESULT: 0x80131040)"
But I'm pretty sure I have added the Microsoft.Dynamic reference to my project. It is version 1.1.0.20. This is included in the IronPython distribution but also in another location on my computer. I have tried setting the reference to both, but they both have the same version number and appear to be the same file size. Neither one works. Do I need version 1.0.0.0 or am I doing something else wrong? I don't really understand why anything pyEngine (the ScriptEngine returned by Python.CreateEngine()) would try to load a different version than the one included with the distribution.
Code is below. Let me know if you need any other information.
MyAddin.cs
/*
Added these references all as Local Copies - probably not necessary?
System.Windows.Forms
Microsoft.CSharp
ExcelDna.Integration (from Excel-DNA distribution folder)
IronPython (from IronPython folder)
IronPython.Modules (from IronPython folder)
Microsoft.Dynamic (from IronPython folder)
Microsoft.Scripting (from IronPython folder)
Microsoft.Scripting.Metadata (from IronPython folder)
mscorlib (I don't really know why I added this, but it was one of the references in my IronPython class library)
MyClass (this is the reference to my IronPython class - I checked to see that it gets copied in every time I rebuild the solution and it does)
These were automatically added by SharpDevelop when I created the project.
System
System.Core
System.Windows.Forms
System.Xml
System.Xml.Linq
*/
using System;
using System.IO;
using System.Windows.Forms;
using ExcelDna.Integration;
using System.Reflection;
using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
public class MyAddIn : IExcelAddIn
{
public void AutoOpen()
{
try
{
string xllDirectory = Path.GetDirectoryName(#"C:/Users/myname/Documents/SharpDevelop Projects/IronPythonExcelDNATest/MyAddIn/bin/Debug/");
string dllPath = Path.Combine(xllDirectory,"MyClass.dll");
Assembly myclass = Assembly.LoadFile(dllPath);
ScriptEngine pyEngine = Python.CreateEngine();
pyEngine.Runtime.LoadAssembly(myclass);
ScriptScope pyScope = pyEngine.Runtime.ImportModule("MyClass");
object myClass = pyEngine.Operations.Invoke(pyScope.GetVariable("MyClass"));
IronTest.AddSomeStuff = pyEngine.Operations.GetMember<Func<double, double,double>>(myClass, "AddSomeStuff");
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
public void AutoClose()
{
}
}
public class IronTest
{
public static Func<double, double, double> AddSomeStuff;
public static double TestIPAdd(double val1, double val2)
{
try
{
return AddSomeStuff(val1, val2);
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
return double.NaN;
}
}
}
MyClass.py
class MyClass:
def __init__(self):
pass
def AddSomeStuff(self,x,y):
return x + y
Your IronPython stuff probably needs to run under the .NET 4 runtime. To tell Excel-DNA to load .NET 4, you have to explicitly add a RuntimeVersion attribute in the main .dna file. Your .dna file will start with something like:
<DnaLibrary RuntimeVersion="v4.0"> ... </DnaLibrary>
The default behaviour, if the attribute is omitted, is to load the .NET 2.0 version of the runtime (which is also used by .NET 3.0 and 3.5).
It might be possible to host IronPython under the .NET 2.0 runtime, but then you need to deal with the DLR libraries yourself, whereas they are built-in and already installed with .NET 4.

warning MSB3391: <DLL> does not contain any types that can be unregistered for COM Interop

I've made a simple C# DLL (that's part of a much larger project) using VS2005. I need to use the DLL in Excel via VBA code so I am using COM Interop on the assembly. I am trying to make the build process automatically generate the necessary TLB file so that I don't need to go to the command line and use regasm after every build.
My problem is that although the DLL compiles and builds fine, it does not generate a TLB file. Instead, the error in the title prints out in the output box.
I've gotten other DLLs to build TLB files by going to the project's properties in VS2005 -> Build -> Output -> Check "Register for COM interop". Also I have [assembly: ComVisible(true)] in the AssemblyInfo.cs.
Here's the summary of the source for the problem DLL and the DLL that it references for a return type:
using System;
using System.IO;
using System.Runtime.InteropServices;
using SymbolTable;
namespace ProblemLibrary
{
public class Foo
{
public Foo(string filename)
{
...
}
// method to read a text file into a SymbolTable
public SymbolTable BuildDataSet(string[] selected)
{
...
}
}
}
Here is a summary of SymbolTable.dll. It holds a return type that ProblemLibrary uses.
using System;
using System.Collections.Generic;
namespace SymbolTable
{
public class SymbolTable
{
readonly Dictionary<SymbolInfoStub, string> _symbols = new Dictionary<SymbolInfoStub, string>();
/*methods that interact with Dictionary snipped*/
}
}
You need to have ctor without any params.
You should have GuidAttribute and ProgIdAttribute around the classes.
Its better to mark the assembly as ComVisible(false) and mark explicitly the classes that need export.
Use interfaces for your classes.
Make sure the you have GuidAttribute in the assembly level.
[Guid("<PUT-GUID-HERE-1>")]
[ComVisible(true)]
interface IFoo
{
void DoFoo();
}
[Guid("<PUT-GUID-HERE-2>")]
[ComVisible(true)]
[ProgId("ProgId.Foo")]
class Foo : IFoo
{
public void DoFoo()
{
}
}
In the AssemblyInfo.cs file, make sure you have the following:
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(true)]
UPDATE:
Read: How can I make use of .NET objects from within Excel VBA?
Which links to:
http://richnewman.wordpress.com/2007/04/15/a-beginner%E2%80%99s-guide-to-calling-a-net-library-from-excel/
I saw a similar problem. I got an error like:
warning MSB3391: does not contain any
types that can be unregistered for COM
Interop.
I followed all the rules (ComVisible, etc.) but nothing worked.
Solution: I had to put something in the default constructor so that it would not be optimized away. The moment I had something there, the registration finished with no message and the component was visible in the registry.
Interesting note: a friend of mine managed to register the original DLL with the empty default constructor on his machine (64-bit Windows-7, VS2008-Professional, like mine). However, his REGASM.EXE was:
C:\Windows\Microsoft.NET\Framework64\v2.0.50727\regasm.exe
while mine was:
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe
So it could be some difference between versions of the .NET framework - maybe the later version is optimizing too much and the REGASM does not account for that.

Categories

Resources