C++/CLI convert existing application do managed code - c#

I've got an existing application written in Borland C++ Builder. My client wants it rewritten in C#/WPF. This requires a lot of work and is a complex task because I need to rewrite the whole (huge) algorithm. I was thinking of a way to reuse the code and to be able to create only GUI in C#/WPF. Is this possible? Would that be easy? How can I make C++ classes visible to .NET ?
If you could give me brief answers with some links/examples I would be grateful.

You can wrap your old C++ code in a C++/CLI wrapper and build it into a DLL file. It should then be visible in any .NET project.
Depending on your algorithm/function structure, this may change a little, but the basic form, and the way I did this, is the following. Keep in mind this is a VERY basic example, but I tried to include the key points:
1. Define the CLI wrapper
using namespace System;
#include "myAlgorithmClass"
public ref class MyCLIWrapperClass //the 'ref' keyword specifies that this is a managed class
{
public:
MyCLIWrapperClass(); //Constructor
//function definitions are the same, though some types,
//like string, change a little. Here's an example:
String ^ GetAString(); //the "String" is the .NET "System.String" and the ^ is sort of a pointer for managed classes
//int, double, long, char, etc do not need to be specified as managed.
//.NET code will know how to handle these types.
//Here you want to define some functions in the wrapper that will call
//the functions from your un-managed class. Here are some examples:
//Say that your functions in the Algorithm class are int Func1, double Func2, and std::string Func3:
int Func1();
double Func2();
String ^ Func3();
private:
//All private functions and members here
myAlgorithmClass algor;
};
2. Implement the wrapper class
using namespace System;
#include <string> //For the string function ex. below
#include "myAlgorithmClass"
MyCLIWrapperClass::MyCLIWrapperClass()
{
algor = myAlgorithmClass(); //create instance of your un-managed class
}
int MyCLIWrapperClass::Func1()
{
return algor.Func1(); //Call the un-managed class function.
}
double MyCLIWrapperClass::Func2()
{
return algor.Func2(); //Call the un-managed class function.
}
String ^ MyCLIWrapperClass::Func3()
{
//Converting a std::string to a System.String requires some conversion
//but I'm sure you can find that somewhere on Google or here on SO
String ^ myString = "";
std::string myUnmanagedString = algor.Func3(); //Call the un-managed class function.
//Convert myUnmanagedString from std::string to System.String here
return myString;
}
After you write the wrapper class and compile it into a class library you can create a reference to the .DLL file in a C# project and C# should see the managed wrapper class and all its public functions.
Another thing to note is that if you are creating new managed objects in C++ you use the keyword "gcnew" instead of "new". So a new string would be String ^ someString = gcnew String();
Finally, here are some links to some things about CLI that may help:
CLI - Wikipedia
CLI - Code Project
CLI - FunctionX

Well, As far as i can remember by head, you can use interops for that matter.
It is in fact better to continue using a code that was for a long time tested, and is stable. With net Interop Services you can create a very good bridge between the new app and the old one.
examples:
http://www.daniweb.com/forums/thread136041.html
http://msdn.microsoft.com/en-us/library/aa645736%28v=vs.71%29.aspx
http://www.codeguru.com/cpp/cpp/cpp_managed/interop/article.php/c6867

Related

Access C# nuget package from C++, do I need to use COM? [duplicate]

Background: As part of a larger assignment I need to make a C# library accessible to unmanaged C++ and C code. In an attempt to answer this question myself I have been learning C++/CLI the past few days/ weeks.
There seems to be a number of different ways to achieve using a C# dll from unmanaged C++ and C. Some of the answers in brief appear to be: using Interlope services, Using .com. and regasm, Using PInvoke (which appears to go from C# to C++ only), and using IJW in the C++/CLR (which appears to be Interlope services). I am thinking it would be best to set up a library that is perhaps a CLR wrapper that uses IJW to call my C# dll on the behalf of native C++ and C code.
Specifics: I need to pass values of string as well as int to a C# dll from c++ code, and return void.
Relevance: Many companies have many excuses to mix and match C++, C and C#. Performance: unmanaged code is usually faster, interfaces: Managed interfaces are generally easier to maintain, deploy, and are often easier on the eyes, Managers tell us too. Legacy code forces us too. It was there (Like the mountain that we climbed). While examples of how to call a C++ library from C# are abundant. Examples of how to call C# libraries from C++ code are difficult to find via Googling especially if you want to see updated 4.0+ code.
Software: C#, C++/CLR, C++, C, Visual Studio 2010, and .NET 4.0
Question details: OK multi-part question:
Is there an advantage to using com objects? Or the PInvoke? Or some other method? (I feel like the learning curve here will be just as steep, even though I do find more information on the topic in Google Land. IJW seems to promise what I want it to do. Should I give up on looking for an IJW solution and focus on this instead?) (Advantage/ disadvantage?)
Am I correct in imagining that there is a solution where I write a wrapper that that utilizes IJW in the C++/CLR? Where can I find more information on this topic, and don’t say I didn’t Google enough/ or look at MSDN without telling me where you saw it there. (I think I prefer this option, in the effort to write clear and simple code.)
A narrowing of question scope: I feel that my true issue and need is answering the smaller question that follows: How do I set up a C++/CLR library that an unmanaged C++ file can use within visual studio. I think that if I could simply instantiate a managed C++ class in unmanaged C++ code, then I might be able work out the rest (interfacing and wrapping etc.). I expect that my main folly is in trying to set up references/#includes etc. within Visual Studio, thought clearly I could have other misconceptions. Perhaps the answer to this whole thing could be just a link to a tutorial or instructions that help me with this.
Research: I have Googled and Binged over and over with some success. I have found many links that show you how to use an unmanaged library from C# code. And I will admit that there have been some links that show how to do it using com objects. Not many results were targeted at VS 2010.
References:
I have read over and over many posts. I have tried to work through the most relevant ones. Some seem tantalizingly close to the answer, but I just can’t seem to get them to work. I suspect that the thing that I am missing is tantalizingly small, such as misusing the keyword ref, or missing a #include or using statement, or a misuse of namespace, or not actually using the IJW feature properly, or missing a setting that VS needs to handle the compilation correctly, etc. So you wonder, why not include the code? Well I feel like I am not at a place where I understand and expect the code I have to work. I want to be in a place where I understand it, when I get there maybe then I'll need help fixing it. I'll randomly include two of the links but I am not permitted to show them all at my current Hitpoint level.
http://www.codeproject.com/Articles/35437/Moving-Data-between-Managed-Code-and-Unmanaged-Cod
This calls code from managed and unmanaged code in both directions going from C++ to Visual Basic and back via C++CLR, and of course I am interested in C#.: http://www.codeproject.com/Articles/9903/Calling-Managed-Code-from-Unmanaged-Code-and-vice
You can do this fairly easily.
Create an .h/.cpp combo
Enable /clr on the newly create .cpp file. (CPP -> Right click -> Properties)
Set the search path for "additional #using directories" to point towards your C# dll.
Native.h
void NativeWrapMethod();
Native.cpp
#using <mscorlib.dll>
#using <MyNet.dll>
using namespace MyNetNameSpace;
void NativeWrapMethod()
{
MyNetNameSpace::MyManagedClass::Method(); // static method
}
That's the basics of using a C# lib from C++\CLI with native code. (Just reference Native.h where needed, and call the function.)
Using C# code with managed C++\CLI code is roughly the same.
There is a lot of misinformation on this subject, so, hopefully this saves someone a lot of hassle. :)
I've done this in: VS2010 - VS2012 (It probably works in VS2008 too.)
UPDATE 2018
It seems like as if the solution does not work for Visual Studio 2017 and onwards. Unfortunately I am currently not working with Visual Studio and therefore cannot update this answer by myself. But kaylee posted an updated version of my answer, thank you!
UPDATE END
If you want to use COM, here's my solution for this problem:
C# library
First of all you need a COM compatible library.
You already got one? Perfect, you can skip this part.
You have access to the library? Make sure it's COM compatible by following the steps.
Make sure that you checked the "Register for COM interop" option in the properties of your project. Properties -> Build -> Scroll down -> Register for COM interop
The following screenshots shows where you find this option.
All the interfaces and classes that should be available need to have a GUID
namespace NamespaceOfYourProject
{
[Guid("add a GUID here")]
public interface IInterface
{
void Connect();
void Disconnect();
}
}
namespace NamespaceOfYourProject
{
[Guid("add a GUID here")]
public class ClassYouWantToUse: IInterface
{
private bool connected;
public void Connect()
{
//add code here
}
public void Disconnect()
{
//add code here
}
}
}
So that's pretty much what you have to do with your C# code. Let's continue with the C++ code.
C++
First of all we need to import the C# library.
After compiling your C# library there should be a .tlb file.
#import "path\to\the\file.tlb"
If you import this new created file to your file.cpp you can use your object as a local variable.
#import "path\to\the\file.tlb"
int _tmain(int argc, _TCHAR* argv[])
{
CoInitialize(NULL);
NamespaceOfYourProject::IInterfacePtr yourClass(__uuidof(NamespaceOfYourProject::ClassYouWantToUse));
yourClass->Connect();
CoUninitialize();
}
Using your class as an attribute.
You will noticed that the first step only works with a local variable. The following code shows how to use it as a attribute. Related to this question.
You will need the CComPtr, which is located in atlcomcli.h. Include this file in your header file.
CPlusPlusClass.h
#include <atlcomcli.h>
#import "path\to\the\file.tlb"
class CPlusPlusClass
{
public:
CPlusPlusClass(void);
~CPlusPlusClass(void);
void Connect(void);
private:
CComPtr<NamespaceOfYourProject::IInterface> yourClass;
}
CPlusPlusClass.cpp
CPlusPlusClass::CPlusPlusClass(void)
{
CoInitialize(NULL);
yourClass.CoCreateInstance(__uuidof(NamespaceOfYourProject::ClassYouWantToUse));
}
CPlusPlusClass::~CPlusPlusClass(void)
{
CoUninitialize();
}
void CPlusPlusClass::Connect(void)
{
yourClass->Connect();
}
That's it! Have fun with your C# classes in C++ with COM.
The answer from 0lli.rocks is unfortunately either outdated or incomplete. My co-worker helped me get this working, and to be frank one or two of the implementation details were not remotely obvious. This answer rectifies the gaps and should be directly copyable into Visual Studio 2017 for your own use.
Caveats: I haven't been able to get this working for C++/WinRT, just an FYI. All sorts of compile errors due to ambiguity of the IUnknown interface. I was also having problems getting this to work for just a library implementation instead of using it in the main of the app. I tried following the instructions from 0lli.rocks for that specifically, but was never able to get it compiling.
Step 01: Create your C# Library
Here's the one we'll be using for the demo:
using System;
using System.Runtime.InteropServices;
namespace MyCSharpClass
{
[ComVisible(true)] // Don't forget
[ClassInterface(ClassInterfaceType.AutoDual)] // these two lines
[Guid("485B98AF-53D4-4148-B2BD-CC3920BF0ADF")] // or this GUID
public class TheClass
{
public String GetTheThing(String arg) // Make sure this is public
{
return arg + "the thing";
}
}
}
Step 02 - Configure your C# library for COM-visibility
Sub-Step A - Register for COM interoperability
Sub-Step B - Make the assembly COM-visible
Step 3 - Build your Library for the .tlb file
You probably want to just do this as Release for AnyCPU unless you really need something more specific.
Step 4 - Copy the .tlb file into the source location for your C++ project
Step 5 - Import the .tlb file into your C++ project
#include "pch.h"
#include <iostream>
#include <Windows.h>
#import "MyCSharpClass.tlb" raw_interfaces_only
int wmain() {
return 0;
}
Step 6 - Don't panic when Intellisense fails
It will still build. You're going to see even more red-lined code once we implement the actual class into the C++ project.
Step 7 - Build your C++ project to generate the .tlh file
This file will go into your intermediate object build directory once you build the first time
Step 8 - Assess the .tlh file for implementation instructions
This is the .tlh file that is generated in the intermediate object folder. Don't edit it.
// Created by Microsoft (R) C/C++ Compiler Version 14.15.26730.0 (333f2c26).
//
// c:\users\user name\source\repos\consoleapplication6\consoleapplication6\debug\mycsharpclass.tlh
//
// C++ source equivalent of Win32 type library MyCSharpClass.tlb
// compiler-generated file created 10/26/18 at 14:04:14 - DO NOT EDIT!
//
// Cross-referenced type libraries:
//
// #import "C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscorlib.tlb"
//
#pragma once
#pragma pack(push, 8)
#include <comdef.h>
namespace MyCSharpClass {
//
// Forward references and typedefs
//
struct __declspec(uuid("48b51671-5200-4e47-8914-eb1bd0200267"))
/* LIBID */ __MyCSharpClass;
struct /* coclass */ TheClass;
struct __declspec(uuid("1ed1036e-c4ae-31c1-8846-5ac75029cb93"))
/* dual interface */ _TheClass;
//
// Smart pointer typedef declarations
//
_COM_SMARTPTR_TYPEDEF(_TheClass, __uuidof(_TheClass));
//
// Type library items
//
struct __declspec(uuid("485b98af-53d4-4148-b2bd-cc3920bf0adf"))
TheClass;
// [ default ] interface _TheClass
// interface _Object
struct __declspec(uuid("1ed1036e-c4ae-31c1-8846-5ac75029cb93"))
_TheClass : IDispatch
{
//
// Raw methods provided by interface
//
virtual HRESULT __stdcall get_ToString (
/*[out,retval]*/ BSTR * pRetVal ) = 0;
virtual HRESULT __stdcall Equals (
/*[in]*/ VARIANT obj,
/*[out,retval]*/ VARIANT_BOOL * pRetVal ) = 0;
virtual HRESULT __stdcall GetHashCode (
/*[out,retval]*/ long * pRetVal ) = 0;
virtual HRESULT __stdcall GetType (
/*[out,retval]*/ struct _Type * * pRetVal ) = 0;
virtual HRESULT __stdcall GetTheThing (
/*[in]*/ BSTR arg,
/*[out,retval]*/ BSTR * pRetVal ) = 0;
};
} // namespace MyCSharpClass
#pragma pack(pop)
In that file, we see these lines for the public method we want to use:
virtual HRESULT __stdcall GetTheThing (
/*[in]*/ BSTR arg,
/*[out,retval]*/ BSTR * pRetVal ) = 0;
That means that the imported method will expect an input-string of type BSTR, and a pointer to a BSTR for the output string that the imported method will return on success. You can set them up like this, for example:
BSTR thing_to_send = ::SysAllocString(L"My thing, or ... ");
BSTR returned_thing;
Before we can use the imported method, we will have to construct it. From the .tlh file, we see these lines:
namespace MyCSharpClass {
//
// Forward references and typedefs
//
struct __declspec(uuid("48b51671-5200-4e47-8914-eb1bd0200267"))
/* LIBID */ __MyCSharpClass;
struct /* coclass */ TheClass;
struct __declspec(uuid("1ed1036e-c4ae-31c1-8846-5ac75029cb93"))
/* dual interface */ _TheClass;
//
// Smart pointer typedef declarations
//
_COM_SMARTPTR_TYPEDEF(_TheClass, __uuidof(_TheClass));
//
// Type library items
//
struct __declspec(uuid("485b98af-53d4-4148-b2bd-cc3920bf0adf"))
TheClass;
// [ default ] interface _TheClass
// interface _Object
First, we need to use the namespace of the class, which is MyCSharpClass
Next, we need to determine the the smart pointer from the namespace, which is _TheClass + Ptr ; this step is not remotely obvious, as it's nowhere in the .tlh file.
Last, we need to provide the correct construction parameter for the class, which is __uuidof(MyCSharpClass::TheClass)
Ending up with,
MyCSharpClass::_TheClassPtr obj(__uuidof(MyCSharpClass::TheClass));
Step 9 - Initialize COM and test the imported library
You can do that with CoInitialize(0) or whatever your specific COM initializer happens to be.
#include "pch.h"
#include <iostream>
#include <Windows.h>
#import "MyCSharpClass.tlb" raw_interfaces_only
int wmain() {
CoInitialize(0); // Init COM
BSTR thing_to_send = ::SysAllocString(L"My thing, or ... ");
BSTR returned_thing;
MyCSharpClass::_TheClassPtr obj(__uuidof(MyCSharpClass::TheClass));
HRESULT hResult = obj->GetTheThing(thing_to_send, &returned_thing);
if (hResult == S_OK) {
std::wcout << returned_thing << std::endl;
return 0;
}
return 1;
}
Once again, don't panic when Intellisense freaks out. You're in Black Magic, Voodoo, & Thar Be Dragons territory, so press onward!
The absolute best way I have found to do this is create a c++/cli bridge that connects the c# code to your native C++. You can do this with 3 different projects.
First Project: C# library
Second Project: C++/CLI bridge (this wraps the C# library)
Third Project: Native C++ application that uses the second project
I recently created a simple GitHub Tutorial for how to do this here. Reading through that code with a little grit and you should be able to hammer out creating a C++/CLI bridge that allows you to use C# code in your native C++.
As a bonus I added how to wrap a C# event down to a function pointer in C++ that you can subscribe to.
I found something that at least begins to answer my own question. The following two links have wmv files from Microsoft that demonstrate using a C# class in unmanaged C++.
This first one uses a COM object and regasm: http://msdn.microsoft.com/en-us/vstudio/bb892741.
This second one uses the features of C++/CLI to wrap the C# class: http://msdn.microsoft.com/en-us/vstudio/bb892742. I have been able to instantiate a c# class from managed code and retrieve a string as in the video. It has been very helpful but it only answers 2/3rds of my question as I want to instantiate a class with a string perimeter into a c# class. As a proof of concept I altered the code presented in the example for the following method, and achieved this goal. Of course I also added a altered the {public string PickDate(string Name)} method to do something with the name string to prove to myself that it worked.
wchar_t * DatePickerClient::pick(std::wstring nme)
{
IntPtr temp(ref);// system int pointer from a native int
String ^date;// tracking handle to a string (managed)
String ^name;// tracking handle to a string (managed)
name = gcnew String(nme.c_str());
wchar_t *ret;// pointer to a c++ string
GCHandle gch;// garbage collector handle
DatePicker::DatePicker ^obj;// reference the c# object with tracking handle(^)
gch = static_cast<GCHandle>(temp);// converted from the int pointer
obj = static_cast<DatePicker::DatePicker ^>(gch.Target);
date = obj->PickDate(name);
ret = new wchar_t[date->Length +1];
interior_ptr<const wchar_t> p1 = PtrToStringChars(date);// clr pointer that acts like pointer
pin_ptr<const wchar_t> p2 = p1;// pin the pointer to a location as clr pointers move around in memory but c++ does not know about that.
wcscpy_s(ret, date->Length +1, p2);
return ret;
}
Part of my question was: What is better? From what I have read in many many efforts to research the answer is that COM objects are considered easier to use, and using a wrapper instead allows for greater control. In some cases using a wrapper can (but not always) reduce the size of the thunk, as COM objects automatically have a standard size footprint and wrappers are only as big as they need to be.
The thunk (as I have used above) refers to the space time and resources used in between C# and C++ in the case of the COM object, and in between C++/CLI and native C++ in the case of coding-using a C++/CLI Wrapper. So another part of my answer should include a warning that crossing the thunk boundary more than absolutely necessary is bad practice, accessing the thunk boundary inside a loop is not recommended, and that it is possible to set up a wrapper incorrectly so that it double thunks (crosses the boundary twice where only one thunk is called for) without the code seeming to be incorrect to a novice like me.
Two notes about the wmv's. First: some footage is reused in both, don't be fooled. At first they seem the same but they do cover different topics. Second, there are some bonus features such as marshalling that are now a part of the CLI that are not covered in the wmv's.
Edit:
Note there is a consequence for your installs, your c++ wrapper will not be found by the CLR. You will have to either confirm that the c++ application installs in any/every directory that uses it, or add the library (which will then need to be strongly named) to the GAC at install time. This also means that with either case in development environments you will likely have to copy the library to each directory where applications call it.
I did a bunch of looking around and found a relatively recent article by Microsoft detailing how it can be done (there is a lot of old infomration floating around). From the article itself:
The code sample uses the CLR 4 hosting APIs to host CLR in a native C++ project, load and invoke .NET assemblies
https://code.msdn.microsoft.com/CppHostCLR-e6581ee0
Basically it describes it in two steps:
Load the CLR into a process
Load your assembly.

How to use a class defined in c# dll from c++ code

I have a dll that was written in c#, and I used this dll in my c++ code(exactly MFC). I can use dll function with return value int, string... But I need to use a function with return value c# class! I don't know what kind of data type in MFC for putting in the function's return value in c# dll. c# return value object is convert to _variant_t in MFC. Please help me. Thank you for reading my question
C# code (abstract)
[Guid(" - - - - ")]
public interface ITestClass
{
object Func();
}
[Guid(" - - - - ")]
public class TestClass : ITestClass
{
public object Func()
{
Project1.Class1 data = dataList[0];
return data ;
}
}
MFC code (abstract)
ITestClass *ptc = NULL;
CoInitialize(NULL);
HRESULT hr = CoCreateInstance(CLSID_TestClass, NULL, CLSCTX_INPROC_SERVER, IID_ITestClass, reinterpret_cast<void**>(&ptc));
int sum = ptc->Sum(5, 30); // It is perfect
data = ptc->Func(); // I don't know what data type is
you will have to have same class defined in C++. It's kind of hard and may be buggy. So I would try with CLI.
Or there is a simpler way. Don't do that :)
Exept, use old, good mechanism, like:
int objectHandle = ptc->CreateObject();
int data = ptc->GetSomeIntData(objectHandle);
ptc->DoSomeOperation(objectHandle);
ptc->ReleaseObject(objectHandle);
The idea is that you don't have a class on C++ side. The object is created on C# side and has its unique object handle (it may be hashcode). You return this handle in CreateObject and on C# side you can store your object in a dictionary:
Dictionary<int, MyClass>
Then on C++ side you call functions with your objectHandle.
It's far more easier way to acomplish the same result.
But if you really want to use this class, you can try this:
- make sure that you C# lib is com compatible (Register for COM interop option)
- After compiling C# lib, there should be a tlb file. Import it in c++:
#import "path\to\lib.tlb"
next you can use it like this:
CoInitialize(NULL);
ClassNamespace::ITestClassPtr myClass(__uuidof(ClassNamespace::TestClass));
myClass->Func();
CoUninitialize();
You can also wrap all that functionality in C++ class. That of course creates more work and makes you have to keep two similar codes (real class and wrapper class). But may be more friendly to use.
If you want to return other class, you would have to do it in similar way. Create com interface, use it in c++ and return it in c#.

Pass C# array of object to C++/CLI

I'm force to expose some methods in a C# library so that they can be used from an external program written in C++ (VC++ 6.0). I thus created a mixed assembly which works quite ok so far but I have some troubles with a method that returns an array of .NET objects.
The .NET signature of the method is:
public Results[] Measure(String model, String identifier);
Where Results itself is:
public class Results
{
public String[] ElementType;
public bool[] HasError;
}
To provide entry point from C++, I started to write a C++/CLI wrapper method like this:
std::vector<ResultsWrapper> Measure(char* model, char* identifier)
{
// Call .NET code
String^ gcmodel = gcnew System::String(model);
String^ gcidentifier = gcnew System::String(identifier);
cli::array<Results^>^ gcres = myNetInstance->Measure(gcmodel, gcidentifier);
// Convert results to C++ vector
std::vector<ResultsWrapper> ret;
for (int ki = 0; ki < res->Length; ki++)
{
ResultsWrapper r = ResultsWrapper(res[ki]->...., );
ret.push_back(r);
}
return ret;
}
but I must admit I'm a bit lost, this is very long time I haven't wrote a single line of C++ and very long time I haven't deal with by hand memory management...
What is the best solution to create ResultsWrapper class so that there will be no much need to care for memory management from C++ side. Maybe something like the following ?
class ResultsWrapper
{
public:
ResultsWrapper(vector<std::String> elementType, vector<bool> hasError)
{
this.ElementType = elementType;
this.HasError = hasError;
}
public:
vector<std:String> ElementType;
vector<bool> HasError;
}
NB: I don't think team on VC++ 6.0 side are aware of boost library or share_ptr types (and I'm not very knowledgable about them either). All C++ code is very classic C++ code style, not even using stdlib.
I could not pass safely/easily STL types across dll boundaries so I get back to old char**, and manual allocation/deallocation and It Just Worked...
IJW for a while only ... then I tried to check for when throwing exception from .NET back to the calling C++ application and then patatra ... need conversion to native exceptions ... that again cannot safely cross dll boundaries ...
Mixed mode assemblies sounded appealing to go from .NET to native but was an IJS experience in my case... I give up and will go through COM instead.
In C++/CLI I started to write a wrapper method like this:
No, you did not
public Results[] Measure(String model, String identifier);
has no resemblance in
std::vector Measure(char* model, char* identifier)
None.
There is no marshalling required in a C++/CLI reference class. Use String^ as pointer to string (instead of char*) and use array<ResultsWrapper>^ as pointer to a managed array.
There is no need to use a wrapper at all. Declare a class as managed reference (ref class) and you can call it from the .NET side because it is a .NET class.

Can a managed C++ assembly return an object to C#?

I'm writing a C# application that must read the video properties of video files. The only way I've found to do this is with the Microsoft Media Foundation which requires C++.
So far, I've made some progress:
I've created a managed C++ assembly which compiles to a DLL.
I can call it, with a parameter, from the C# code.
It executes and prints the video file properties.
What I would like to do next is have the DLL return an object of video properties (width, height, duration, etc.). Given I'm using C++ managed code, is there a simple way to define an object type and use it to pass data between C# and C++ or do I have to use the Marshal class?
Certainly! If you define a public object in your managed C++ (Also called C++/CLI):
public ref class MyManagedClass{
. . .
}
and then reference the dll from your c# project, you'll be able to use the object just like you had defined it in c#.
You can access C++ objects/dlls either by COM Interop or C++/CLI. Using C++/CLI you can create your own wrapper objects/classes which is directly usable in C#. Knowing both C++ and C#, the syntax will be somewhat familiar to you (and there are good resources online).
C++/CLI may require a bit more work as you need to write the CLI wrappers, but will keep things clearer in your C# code (my opinion).
This following article should get you started: Quick C++/CLI - Learn C++/CLI in less than 10 minutes
A more in-depth article: http://msdn.microsoft.com/en-us/magazine/cc163852.aspx
A code example (show casing the syntax) to make things more exciting, borrowed from above. Student is your C++ class, StudentWrapper is th CLI wrapper to be used in your C# code:
public ref class StudentWrapper
{
private:
Student *_stu;
public:
StudentWrapper(String ^fullname, double gpa)
{
_stu = new Student((char *)
System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(
fullname).ToPointer(),
gpa);
}
~StudentWrapper()
{
delete _stu;
_stu = 0;
}
property String ^Name
{
String ^get()
{
return gcnew String(_stu->getName());
}
}
property double Gpa
{
double get()
{
return _stu->getGpa();
}
}
};

Exposing a C++ API to C#

So what I have is a C++ API contained within a *.dll and I want to use a C# application to call methods within the API.
So far I have created a C++ / CLR project that includes the native C++ API and managed to create a "bridge" class that looks a bit like the following:
// ManagedBridge.h
#include <CoreAPI.h>
using namespace __CORE_API;
namespace ManagedAPIWrapper
{
public ref class Bridge
{
public:
int bridge_test(void);
int bridge_test2(api_struct* temp);
}
}
.
// ManagedBridge.cpp
#include <ManagedBridge.h>
int Bridge::bridge_test(void)
{
return test();
}
int Bridge::bridge_test2(api_struct* temp)
{
return test2(temp);
}
I also have a C# application that has a reference to the C++/CLR "Bridge.dll" and then uses the methods contained within. I have a number of problems with this:
I can't figure out how to call bridge_test2 within the C# program, as it has no knowledge of what a api_struct actually is. I know that I need to marshal the object somewhere, but do I do it in the C# program or the C++/CLR bridge?
This seems like a very long-winded way of exposing all of the methods in the API, is there not an easier way that I'm missing out? (That doesn't use P/Invoke!)
EDIT: Ok, so I've got the basics working now thanks to responses below, however my struct (call it "api_struct2" for this example) has both a native enum and union in the C++ native code, like the following:
typedef struct
{
enum_type1 eEnumExample;
union
{
long lData;
int iData;
unsigned char ucArray[128];
char *cString;
void *pvoid;
} uData;
} api_struct2;
I think I have figured out how to get the enum working; I've re-declared it in managed code and am performing a "native_enum test = static_cast(eEnumExample)" to switch the managed version to native.
However the union has got me stumped, I'm not really sure how to attack it.. Ideas anyone?
Yes, you are passing an unmanaged structure by reference. That's a problem for a C# program, pointers are quite incompatible with garbage collection. Not counting the fact that it probably doesn't have the declaration for the structure either.
You can solve it by declaring a managed version of the structure:
public value struct managed_api_struct {
// Members...
};
Now you can declare the method as
int bridge_test2(managed_api_struct temp); // pass by value
or
int bridge_test2(managed_api_struct% temp); // pass by reference
Pick the latter if the structure has more than 4 fields (~16 bytes). The method needs to copy the structure members, one-by-one, into an unmanaged api_struct and call the unmanaged class method. This is unfortunately necessary because the memory layout of a managed structure is not predictable.
This is all pretty mechanical, you might get help from SWIG. Haven't used it myself, not sure if it is smart enough to deal with a passed structure.
A completely different approach is to make the wrapper class cleaner by giving it a constructor and/or properties that lets you build the content of an api_struct. Or you could declare a wrapper ref class for the structure, much like you would in managed code.
as it has no knowledge of what a api_struct actually is
You need to define a managed version in a .NET assembly, that uses attributes (like StructLayoutAttribute) to ensure it marshals correctly.
This seems like a very long-winded [...]
The other approach is to create a COM wrapper (e.g. using ATL) around your API. This might be more effort, but at least you avoid the double coding of struct and function definitions needed for P/Invoke.
Correction: You have created a C++/CLI project: so just add correct '#pragma' to tell the compiler this is .NET code, and then the output is an assembly the C# project can just reference.
Yuo are trying to do this way more complicated that it really is. What you want is two different structs. One managed and one unmanaged. You expose the managed version externally (to your C# app). It will be all ".Net-ish" with no concepts of unions or so.
In your bridge you receive the managed version of the struct, manually create the unmanaged struct and write code to move your data, field by field over to the unmanaged struct. Then call your unmanaged code and finally move the data back to the managed struct.
The beautiful thing about C++/CLI is that the managed code also can work with unmanaged code and data (and include the unmanaged .h files).

Categories