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
Related
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;
}
I am following the example in How to call a managed DLL from native Visual C++ code in Visual Studio.NET or in Visual Studio 2005 to call a .NET DLL from native C++ code. The code for C# looks like this:
public class StringExample: IStringExample
{
public string returnString(string input)
{
return input;
}
}
I followed the example's steps to build, register the COM assembly, and export the type library (.tlb) from the C# code.
In C++ code, I am trying to use code similar to the following:
#import "..\StringExample\bin\Debug\StringExample.tlb" raw_interfaces_only
using namespace StringExample;
void abc()
{
// Initialize COM.
HRESULT hr = CoInitialize(NULL);
// Create the interface pointer.
IStringExample ptr(__uuidof(StringExample));
BSTR bstrInput = L"hello world";
BSTR bstrOutput =L"";
ptr->returnString(bstrInput, &bstrOutput);
TCHAR* szOutput = (TCHAR *)_bstr_t(bstrOutput);
// Uninitialize COM.
CoUninitialize();
}
However, bstrOutput is empty. Moreover, I need to convert the bstrOutput to TCHAR* to pass it to a different api.
Is there an error in the variable initialization? Is there a different way to pass string variables between .NET and C++?
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.
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
}
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