![]() |
Languages »
C++ / CLI »
P/Invoke
Intermediate
Using managed code in an unmanaged applicationBy Steeve MorinHow to use your managed class libraries in your unmanaged application, using IJW. |
C++, C++/CLI, Windows, .NET2.0VS2005, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||

Please keep in mind that the code of this article is for the .NET Framework 2.0 Beta 1 (C++/CLI). To make this sample work under .NET 1.1, basically use * instead of ^ for the managed types.
When we talk about the .NET platform, people often complain about the same things:
While the first two topics are very well discussed, today I will talk about a relatively unknown feature of the .NET platform: IJW.
First, IJW stands for "It Just Works", and, at the very first level, it enables you to compile existing C/C++ projects in MSIL, and "It Just Works" ;). To use it, simply add the /clr compiler option, or set the "Common Language Runtime support" option of your project to "Common Language Runtime Support (/clr)". It's as simple as that.
Of course, why recompile an entire C++ project into MSIL when it's working already? Well, the force of IJW is more in the managed/unmanaged interoperability. Let's take a look at what it can offer you with a simple case: mine.
At work, we heavily use remoting to abstract the network transportation layer, and we were very happy with it. Then one problem came: we use some software to handle our sensors and processing dataflow. This software basically allows you to make "components" that you load in it, and then use them. The problem is, the format for a component is very specific and has to be made in C++ (using the provided source generator). So, to overcome this problem, I designed a simple .NET 2.0 library (using generics) and component that use global shared memory and Win32 events to allow IPC between the software process and the .NET process. While this technique works very well, the drawbacks were huge because you couldn't use the remoting functions you needed when you wanted to and were forced to adopt a certain communication convention. Really, it was a pain sometimes, especially when dealing with pointers. Then I came to see IJW.
Well, with IJW, you can make an executable image (.exe or .dll) with both unmanaged and managed code inside, and allows interoperability between them. For instance, you make an IJW DLL with managed code, and export an unmanaged class or function that uses the managed code. When you will load this DLL in your unmanaged process (via __declspec(dllimport) or LoadLibrary()), the CLR will load the managed image inside and will allow you to use the managed code from your unmanaged process. So, to use your managed code from the unmanaged process, you just have to make the proper wrapper. But then one problem comes: you can't have managed type members in an unmanaged class.
And then I already see some of you saying: "Okay great, but how can I make a wrapper around a managed type if I can't store the managed instance in my wrapper? After all, unmanaged classes are not aware of the .NET Platform". And you would be right. However (I bet you suspected), there is a workaround for this.
.NET provides a way to overcome this via the System.Runtime.InteropServices.GCHandle class. We won't go into GCHandle deeply because, fortunately for us, Microsoft has designed a templated class that wraps GCHandle: gcroot<T>, declared in vcclr.h. So, to use a managed type in an unmanaged class, just declare it as the typed gcroot<T> template. You can then access all its members using the -> symbol.
Note also that you have to use the gcnew keyword to instantiate the managed type into the managed heap, this type will then be marked collectable at the gcroot<T> destructor. Here's a sample:
#include <vcclr.h>
#using <mscorlib.dll>
using namespace System;
class Unmanaged
{
private:
gcroot<String ^> _myString;
public:
Unmanaged()
{
_myString = gcnew String();
}
int GetHashCode()
{
return (_myString->GetHashCode());
}
};
But then another problem comes: the unmanaged client of the DLL wrapper mustn't see any managed stuff (like gcroot<>). So, we'll have to make an interface to the wrapper, and a class factory for it. Let's make a class that wraps a managed class System.Windows.Forms.MessageBox.
First, let's make a managed class called MessageBoxShower in C#:
// MessageBoxShower.cs: a managed class that shows a message box.
using System;
using System.Collections.Generic;
using System.Text;
namespace ManagedClasses
{
public class MessageBoxShower
{
private string _message = "";
public MessageBoxShower(string message)
{
_message = message;
}
public void Show()
{
System.Windows.Forms.MessageBox.Show(_message,
"System.Windows.Forms.MessageBox");
}
}
}
Then, let's make the interface to the C++ wrapper: IMessageBoxWrapper. Of course, we don't forget to add a reference to the DLL containing MessageBoxShower.
// IMessageBoxWrapper.h: interface to hide
// the managed implementation from the dll client
#pragma once
#include <string>
#ifdef MANAGEDWRAPPER_EXPORTS
#define DLLAPI __declspec(dllexport)
#else
#define DLLAPI __declspec(dllimport)
#pragma comment (lib, "ManagedWrapper.lib") // if importing, link also
#endif
class DLLAPI IMessageBoxWrapper
{
public:
virtual void Show(std::string message) = 0;
// Class factory
static IMessageBoxWrapper *CreateInstance();
static void Destroy(IMessageBoxWrapper *instance);
};
After that, we have to make the MessageBoxWrapper class itself:
// MessageBoxWrapper.h: actually wrapper to the managed class
#pragma once
#include <vcclr.h>
#include <string>
#include "IMessageBoxWrapper.h"
using namespace ManagedClasses;
class DLLAPI MessageBoxWrapper : IMessageBoxWrapper
{
private:
gcroot<MessageBoxShower ^> _managedObject;
public:
MessageBoxWrapper() { }
void Show(std::string message);
};
And finally, implement the class factory code, and the MessageBoxWrapper class:
// MessageBoxWrapper.cpp: implementation of MessageBoxWrapper and class factory
#include "IMessageBoxWrapper.h"
#include "MessageBoxWrapper.h"
void MessageBoxWrapper::Show(std::string message)
{
_managedObject = gcnew MessageBoxShower(gcnew System::String(message.c_str()));
_managedObject->Show();
}
IMessageBoxWrapper *IMessageBoxWrapper::CreateInstance()
{
return ((IMessageBoxWrapper *)new MessageBoxWrapper());
}
void IMessageBoxWrapper::Destroy(IMessageBoxWrapper *instance)
{
delete instance;
}
And here we are! We can now use our managed class directly in our unmanaged code without the need of any assembly referencing or .NET stuff! Here's a sample file, remember linking is done via __declspec(dllimport) in IMessageBoxWrapper.h:
// main.cpp: a totaly unmanaged application
#include "IMessageBoxWrapper.h"
int main(int argc, char **argv)
{
IMessageBoxWrapper *wrapper = IMessageBoxWrapper::CreateInstance();
wrapper->Show("hey!");
IMessageBoxWrapper::Destroy(wrapper);
return (0);
}
Data conversion between managed world and unmanaged world is your job; this means you'll have to make a wrapper for each managed type you use between the two worlds. And that's a big drawback of IJW since this can't be automated, because obviously, it depends on your code. However, it can be done anywhere you want. Also, I suggest you to take a look at the System.Runtime.InteropServices.Marshal class, it can be really useful.
Talking about collections, the conversion is also up to you: for instance ArrayList or List<> to std::vector<> or Hashtable to std::map<>, and back.
With this article, we demonstrated that managed code can interoperate very well with unmanaged code, in both directions. And well, I hope, for once, it will help you decide to switch to managed code.
Oh, and don't forget, a common mistake is to forget to put the .NET references needed by your wrapper DLL in the same directory as the executable (not the project!).
03/30/2005 - Initial release.
General
News
Question
Answer
Joke
Rant
Admin
Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads.
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 4 Apr 2005 Editor: Smitha Vijayan |
Copyright 2005 by Steeve Morin Everything else Copyright © CodeProject, 1999-2010 Web22 | Advertise on the Code Project |