expose c++-cli dll to c# - c#

I am generating a dll using c++-cli.
The class in the dll looks like this:
ref class Locator
{
public:
Locator(int HP) : m_HP(HP) { }
~Locator()
{ }
Dictionary<String^, array< Byte >^>^ Locate(Dictionary<String^, String^>^ imgParms)
{ ..... }
private:
int m_HP;
How can I expose it to use it in c#? The class and the members are not being exposed in c#

You need to use the public keyword to make managed types visible to consumers of the assembly. Try
public ref class Locator { ... };

A question I asked a few months ago, regarding a similar issue :
How to use a C callback from C#?
Here are a few extra hints in my answer about the problem :
Calling C++ DLL from C++ works, but not from C#
I read you're using OpenCV, do you know Emgu CV: OpenCV in .NET (C#, VB, C++ and more) ? :-)

C++/CLI generates standard .NET managed assembly. That's the whole point of C++/CLI => compile an existing C++ code into a managed assembly without rewriting it to a CLS language.
So simply reference this assembly as any other .NET assembly in your consuming project. And then consume:
var imgParms = new Dictionary<string, string> { { "foo", "bar" }, { "baz", "bazinga" } };
var locator = new Locator(123);
var result = locator.Locate(imgParams);
// TODO: do something with the result dictionary

Related

Is there a way to use a VB Dll in c++ (preferably cross-plattform)

I'm trying to interact with a piece of hardware using my own code. The vendor offers a dll on his website with some sample code in C#, the DLL seems to be compiled from Visual Basic.
I'm wondering how to use this DLL in C++. Is C++/CLI the only way to do this? Or is there some other way that allows me to keep my code Cross-Platform compatible?
simple example: suppose we have a managed class Hello in a .NET assembly (hello.dll).
//This is c# but it could be also vb. Compilator of c# and VB.net translate it to same thing .Net assembly dll. Which is internaly exactly the same as c# dll.
class Hello
{
public void HelloWorld()
{
Console.Writeline("Hello World!");
}
}
Now we need to define a native proxy class for Hello:
class Hello
{
public:
Hello() : wrapper_("hello.dll", "namespace name") {}
void HelloWorld()
{
wrapper_("Hello");
}
private:
nativeAdapter::NativeProxy wrapper_;
};
}
In our main function, we can use the proxy as if it were the managed class:
int main(int, char **)
{
Namespace::Hello hello;
hello.Hello();
return 0;
}

Using a C# library in C++/CLI project: compile-time error "undefined class"

I'm trying out using a C# library from a managed C++ project (CLR) for the first time and am experiencing compile-time problems.
The C# library in question is the Optional library from Nils Lück (https://github.com/nlkl/Optional).
I created a simple demo project so I can paste the code here for you to see. Basically, I have a C# class that has a public method which returns an Option<string, MyExceptionType> which is a generic type from the Optional library. Then I call this method from the C++/CLR project.
First, this are the compile-time error messages I get:
And here the code:
C# class
namespace OptionalAndCpp
{
using Optional;
public class MyException
{
public string Message { get; set; }
}
public class Something
{
public Option<string, MyException> DoStuff()
{
return Option.Some<string, MyException>("Hello");
}
}
}
Wrapper header
#pragma once
class TheWrappingClass
{
public:
void DoStuffFromWrapper();
};
Wrapper implementation
#include "stdafx.h"
#include "TheWrapper.h"
#using <OptionalAndCpp.dll>
#using <Optional.dll>
void TheWrappingClass::DoStuffFromWrapper()
{
OptionalAndCpp::Something^ sth = gcnew OptionalAndCpp::Something();
auto result = sth->DoStuff();
}
That's all. And when I hit compile, I get the errors shown in the screenshot above.
I took a look at the source code of the library and saw that it relies extensively on generics and extension methods, so it made me wonder whether it's a similar problem as with exporting C++ function templates from a DLL (which you can't), but I'm sincerely clueless.
Is it possible that some C# libraries are not usable from a C++/CLR project at all for some reason? As I already mentioned this is the first time I'm doing this and I have a lot more experience with C# than with C++.
Has anyone ever encountered a similar problem?
I'm grateful for any help and/or insights you may have.

Passing objects between C# library and C++ (CLR)

How to pass objects from C# library to C++.
I can call a function which returns void or int without any issue.
Now consider the following function in C#,
List<CSharpClass> CSharpFunction(string Input)
where my C# class contains,
public class CSharpClass
{
string mystring = string.Empty;
byte[] bytearray = null;
public byte[] bytearray
{
get { return bytearray ; }
set { bytearray = value; }
}
public string mystring
{
get { return mystring ; }
set { mystring = value; }
}
}
Now, I want use this List in my C++. So I have created,
typedef std::vector<class CSharpClass> MyDetailList;
Is it the right way ?? If not what I need to use in C++?
If you want to call a C# dll from C++ code, you can follow this article.
In a nutshell, you're going to have to:
- Write a Managed DLL containing your CSharpClass
- Register the Managed DLL for Use with COM or with Native C++
- Call the Managed DLL from Native C++ Code
This SO question is also relevant, and contains alternative solutions if you want to avoid using COM
Initial misguided answer:
You can check this article for a fairly good tutorial.
In a nutshell, you're going to have to:
- Compile a dll from your c++ code
- marshall ("translate") your class between C# and C++
- load the dll from C#, by using DllImport declaration
- call the imported method

Managed byte[] to unmanaged byte array using ATL/COM

I want to pass some image data from C# code to unmanaged C++ using ATL/COM
From C# code side i do something like this:
void SendFrame([In, MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_UI1)] ref byte[] frameData);
But I'm not sure how should I handle this function in my C++ code.
For now I have something like this:
_ATL_FUNC_INFO OnSendFrameDef = { CC_STDCALL, VT_EMPTY, 1, { VT_SAFEARRAY | VT_UI1 } };
void __stdcall OnSendFrame(SAFEARRAY* ppData)
{
BYTE* pData;
SafeArrayAccessData(ppData, (void **) &pData);
// Doing some stuff with my pData
SafeArrayUnaccessData(ppData);
}
Can anyone give me some suggestions how I can make this thing work?
Thanks.
Since the SAFEARRAY is already marshaled to your unmanaged code, and you're using ATL, you can use the CComSafeArray class, as it handles all the cleanup when working with SAFEARRAY instances:
_ATL_FUNC_INFO OnSendFrameDef = { CC_STDCALL, VT_EMPTY, 1,
{ VT_SAFEARRAY | VT_UI1 } };
void __stdcall OnSendFrame(SAFEARRAY* ppData)
{
// Wrap in a CComSafeArray.
// On the stack means all calls to cleanup
// will be cleaned up when the stack
// is exited.
CComSafeArray<byte> array;
array.Attach(ppData);
// Work with the elements, get the first one.
byte b = array.GetAt(0);
// And so on. The destructor for CComSafeArray
// will be called here and cleaned up.
}
I've managed to achieve my goal! For those who are interested:
My event handler descriptor looks like this:
_ATL_FUNC_INFO Stream::OnStreamFrameCallbackDef = { CC_STDCALL, VT_EMPTY, 1, { VT_DISPATCH } };
My C++ function:
void __stdcall Stream::OnStreamFrameCallback(IDispatch* pFrame)
{
// NOTE that this "IStreamFramePtr" is COM's Ptr of my "IStreamFrame"
MyCOM::IStreamFramePtr pStreamFrame = pFrame;
// Thanks casperOne♦ for this:
CComSafeArray<byte> array;
array.Attach(pStreamFrame->GetBuffer());
// Now I can do stuff that I need...
byte* pBuffer = &array.GetAt(0);
}
My "IStreamFrame" in my .tlh file looks like this:
struct __declspec(uuid("1f6efffc-0ac7-3221-8175-5272a09cea82"))
IStreamFrame : IDispatch
{
__declspec(property(get=GetWidth))
long Width;
__declspec(property(get=GetHeight))
long Height;
__declspec(property(get=GetBuffer))
SAFEARRAY * Buffer;
long GetWidth ( );
long GetHeight ( );
SAFEARRAY * GetBuffer ( );
};
In my C# code I have something like this:
[ComVisible(true)]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
public interface IStreamFrame
{
int Width { [return: MarshalAs(UnmanagedType.I4)] get; }
int Height { [return: MarshalAs(UnmanagedType.I4)] get; }
byte[] Buffer { [return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_UI1)] get; }
};
[ComVisible(false)]
public delegate void StreamFrameCallback(IStreamFrame frame);
[ComVisible(true)]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
public interface IMyCOMStreamEvents
{
[DispId(1)]
void OnStreamFrameCallback([In, MarshalAs(UnmanagedType.IDispatch)] IStreamFrame frame);
}
Things seems to work just fine. But if anyone have any suggestions or noticed that I'm doing something wrong please let me know.
Thanks.
I strongly recommend you to create an IDL file to design your COM interfaces.
Given your example in your answer, a rather minimal IDL file could be like this:
import "oaidl.idl";
[object,
uuid(1f6efffc-0ac7-3221-8175-5272a09cea82),
dual,
oleautomation]
interface IStreamFrame : IDispatch {
[propget]
HRESULT Width([out, retval] long *pWidth);
[propget]
HRESULT Height([out, retval] long *pHeight);
[propget]
HRESULT Buffer([out, retval] SAFEARRAY(byte) *pBuffer);
};
[uuid(1f6efffc-0ac7-3221-8175-5272a09cea83)]
library ContosoStreamFrame {
importlib("stdole32.tlb");
interface IStreamFrame;
};
You then use midl.exe to generate a .h with C/C++ interfaces, a _i.c for the CLSID and IID constants for C/C++ linking, a dlldata.c for RPC registering, a _p.c with proxy and stub marshalling stuff and a .tlb which is in general terms the parsed representation of the .idl file. This is all better described in the documentation.
EDIT: There seems to be no way to avoid the C/C++ file generation.
EDIT2: I just found a workaround, use nul as the output file for what you don't want. For instance, the following command only generates file.tlb:
midl.exe /header nul /iid nul /proxy nul /dlldata nul file.idl
Note: if your IStreamFrame interface is not meant to be used across processes, add the local attribute to the interface.
In C/C++, you can use the specific files that are generated, or #import the TLB file. In .NET, you can run tlbimp.exe on the TLB file, which generates a .NET assembly.
You can also use tlbexp.exe if your project is .NET centric. However, that'll require you to know the .NET COM annotations and what they mean in terms of IDL, so I'm not sure if there's any gain on saving one extra source-file in another language at the expense of lots of decoration noise in your interface and class definitions. It's perhaps a good option if you want to have full control of the classes and interfaces at the source level and you want to make it as easy as possible (read, optimized for usability and maybe speed) on .NET code.
Finally, you can automate all of this in Visual Studio by creating a project. If you use the IDL approach, add a custom build step that invokes midl.exe and tlbimp.exeand make the dependant projects depend on this project for a correct build order. If you use the .NET approach, add a custom build step that invokes tlbexp.exe and make the dependant C/C++ projects depend on this project.
EDIT: If you don't need the generated C/C++ files from midl.exe, you may add del commands to your custom build step for the specific output files.
EDIT2: Or use the nul workaround described above.
A common approach used when the type library already exists is to use Visual Studio to import it into .NET. But this way, you'll have to remember to regenerate the TLB and import it again if you update your IDL file.
Have you considered C++/CLI? In your C++/CLI ref class you'd write a member function:
void SendFrame(cli::array<System::Byte> frameData)
{
pin_ptr<System::Byte> p1 = &frameData[0];
unsigned char *p2 = (unsigned char *)p1;
// So now p2 is a raw pointer to the pinned array contents
}

How to Use C++/CLI Within C# Application

I am trying to call my C++ library from my C# application (via C++/CLI). I followed the example from this question (for my specific application). The setup of my application is:
Project1: C++ Project (I compile this to a DLL)
Project2: C++ Project (my CLR wrapper; just the header file per the example above; references Project1)
Project3: C# Project (references Project2)
Unfortunately, when I actually go to access the CLR wrapper object in my C# application, I receive the following error:
The type or namespace name 'YourClass'
could not be found (are you missing a
using directive or an assembly
reference?)
Do I have the project setup incorrectly, or is there something else I should be looking into? (Unfortunately, I cannot post the code for proprietary reasons, but it is a very simple bit of code and easily follows the above example.)
Update:
So I did exactly what Chris said to do (see answer below), but I am still receiving a message from my C# application that "The type or namespace name 'MyProgram' could not be found (are you missing a using directive or an assembly reference?). Here is a (mock-up) of my code.
Project1 - This is my C++ application. It compiles/works. I have used it elsewhere. (I get a DLL out of this build.)
Project2 - Here is my code for my wrapper.
MyWrapper.h
#pragma once
#include "myorigapp.h"
using namespace System;
namespace MyProgram
{
public ref class MyWrapper
{
private:
myorigapp* NativePtr;
public:
MyWrapper()
{
NativePtr = new myorigapp();
}
~MyWrapper()
{
delete NativePtr;
NativePtr = NULL;
}
void dostuff()
{
NativePtr->dostuff();
}
}
}
Project3 - This is my C# application.
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MyProgram;
namespace Testing
{
class Program
{
static void Main(string[] args)
{
MyWrapper p = new MyWrapper();
p.dostuff();
}
}
}
Project3 references Project2 which references Project1. Everything builds without errors (except the error I described above in the C# code on the using MyProgram line).
Just including the header from a pure C++ application isn't good enough. You need to wrap your unmanaged objects with managed ones in Project2 (i.e. public ref class YourClassDotNet)
#include "YourHeader.h"
namespace MyManagedWrapper
{
public ref class YourClassDotNet
{
private:
YourClass* ptr;
public:
YourClassDotNet()
{
ptr = new YourClass();
}
~YourClassDotNet()
{
this->!YourClassDotNet();
}
!YourClassDotNet()
{
delete ptr;
ptr = NULL;
}
void SomeMethod()
{
ptr->SomeMethod();
}
}
}
Okay, well, I now feel dumb.
It turns out that the problem I was having (which I solved a couple weeks ago - just got around to updating this answer) was that I had included the header file (see Chris' answer for that), but I hadn't actually included the CPP file (which is empty other than including the header file).
Once I did this, the DLL compiled correctly and I could call the C++ functions (using C++/CLI) from my C# code.
Chris showed you the way to create a managed class that uses unmanaged code inside. There is a lot of that that you can do in C# using unsafe (it's just that hardly anyone does).
However, the reverse is also possible: using .NET types directly from a native type/function.
The thing to watch out for is that any managed pointer has to be marked as such. For this purpose, C++/CLI defines a special type of smartpointer gcroot<T> (mimicking boost::shared_pointer or std::auto_ptr in a way). So to store a managed string inside your C++ class, use the following:
#include <string>
#include <vcclr.h>
using namespace System;
class CppClass {
public:
gcroot<String^> str; // can use str as if it were String^
CppClass(const std::string& text) : str(gcnew String(text.c_str())) {}
};
int main() {
CppClass c("hello");
c.str = gcnew String("bye");
Console::WriteLine( c.str ); // no cast required
}
Note that (if it hasn't been fixed these days) you'll run into a bit of friction with the mismatch between managed null and C/C++ NULL.
You can't easily type, as you would expect:
gcroot<Object^> the_thing;
...
if (the_thing != nullptr)
...
}
Instead you'd have to use the native style (the smart wrapper gcroot handles this)
gcroot< Object^ > the_thing;
if ( the_thing != NULL ) {} // or equivalently...
if ( the_thing ) {}
// not too sure anymore, but I thought the following is also possible:
if ( the_thing != gcroot<Object>(nullptr) ) {}
Note: I don't have access to a windows machine anywhere near these days, so I've quoted from memory

Categories

Resources