Introduction
.NET framework is one of the better development and execution environments for software these days. But there is a very huge amount of software components already developed and being developed out of unmanaged code. So, there needs to be an easy way for managed code to call the unmanaged code and the other way round.
I have seen some articles on this, but I did not find them giving a complete solution of what I was looking for. So here is one.
Background
Microsoft lets you call COM code from .NET code using RCW (Runtime Callable Wrappers). The RCW, a managed wrapper code, wraps the COM component. The .NET code then interacts with the RCW which in turn interacts with the COM component inside it. The reverse communication can be done using CCW (COM callable wrapper).
This article shows a way of manually creating a wrapper. It was fairly easy to call the unmanaged code from the managed code but not the other way around.
Code
The code that I have specified below consists of:
- Unmanaged class:
UnManaged_Class
- Managed Wrapper class:
Managed_Wrapper_Class
This class wraps the unmanaged class. This means that it “contains” an object of the unmanaged type which it uses to call the exposed methods in the unmanaged type.
- Managed code:
Managed_Class
This is how the managed code calls the unmanaged code:
For every exposed method in the unmanaged class, there should be a corresponding method in the Managed_Wrapper_Class
. The managed code instantiates an object of theManaged_Wrapper_Class
and calls the exposed methods in that class using this instance. These methods in the Managed_Wrapper_Class
then call the corresponding methods in the unmanaged code. This is done using pInner
as shown in the code:
#ifndef UNMANAGED
#define UNMANAGED
class Unmanaged_Class
{
public:
Unmanaged_Class();
void methodToBeCalledInUnmanaged(int data);
};
#endif
*
#include "StdAfx.h"
#using <mscorlib.dll>
#include "Unmanaged.h"
Unmanaged_Class::Unmanaged_Class()
{
}
void Unmanaged_Class::methodToBeCalledInUnmanaged(int data)
{
scallback(data+1);
}
#pragma once
#include "stdafx.h"
#using <mscorlib.dll>
#include "Unmanaged.h"
using namespace System::Runtime::InteropServices;
using namespace System;
namespace Managed_Wrapper
{
public __gc class Managed_Wrapper_Class
{
public:
Managed_Wrapper_Class();
Unmanaged_Class * pInner;
void CallUnmanaged(int data);
};
}
#include "stdafx.h"
#include "Managed_Wrapper.h"
#using <mscorlib.dll>
namespace Managed_Wrapper
{
Managed_Wrapper_Class::Managed_Wrapper_Class(void)
{
pInner = new Unmanaged_Class();
}
void Managed_Wrapper_Class::CallUnmanaged(int data)
{
pInner->methodToBeCalledInUnmanaged (data);
}
}
Imports Managed_Wrapper
Dim forwardCaller As Managed_Wrapper_Class = New Managed_Wrapper_Class
forwardCaller.CallUnmanaged(nudNumber.Value)
This was the easy part. Now is the hard part. Calling managed code from the unmanaged code.
The unmanaged code has a function pointer. The address of the function pointer is the address of a method in the managed wrapper class. Also, the function pointer is initialized by the wrapper class and not in the unmanaged code. So this way, when the function pointer is called, the method in the managed wrapper code is called. Half of the task is done. The way of assigning the function pointer in the unmanaged code is not easy because the function has to point to a method which is managed. So, we use the wrapper delegate struct
as shown in the code. Then convert this delegate struct
to a function pointer of unmanaged type using Marshal::StructureToPtr (_Instance_Of_Delegate_Wrapper, &type_unmanaged_functionptr, false);
The managed wrapper code declares a delegate (a .NET way of a function pointer). The delegate is instantiated by the managed code. So when the method in the managed wrapper class is called (by the unmanaged code), it in turn calls the delegate in the same class (which is initialized by the managed code). As the delegate points to a function in the managed code, the method in the managed code gets called. This was the hard part.
#ifndef UNMANAGED
#define UNMANAGED
#using <mscorlib.dll>
typedef void (*w_CallBack) (int status);
class Unmanaged_Class
{
public:
Unmanaged_Class();
w_CallBack scallback;
void setCallBackInUnmanaged(w_CallBack ptr2F);
void methodToBeCalledInUnmanaged(int data);
};
#endif
#include "StdAfx.h"
#using <mscorlib.dll>
#include "Unmanaged.h"
Unmanaged_Class::Unmanaged_Class()
{
}
void Unmanaged_Class::setCallBackInUnmanaged(w_CallBack ptr2F)
{
scallback = ptr2F;
}
void Unmanaged_Class::methodToBeCalledInUnmanaged(int data)
{
scallback(data+1);
}
#pragma once
#include "stdafx.h"
#using <mscorlib.dll>
#include "Unmanaged.h"
using namespace System::Runtime::InteropServices;
using namespace System;
namespace Managed_Wrapper
{
public __delegate void CallbackDelegate(int data);
[StructLayoutAttribute( LayoutKind::Sequential, CharSet = CharSet::Ansi )]
public __gc struct Managed_Delegate_Wrapper
{
[MarshalAsAttribute(UnmanagedType::FunctionPtr)]
CallbackDelegate* _Delegate;
};
public __gc class Managed_Wrapper_Class
{
public:
Managed_Wrapper_Class();
Unmanaged_Class * pInner;
Managed_Delegate_Wrapper * _Status_Delegate;
void ActualMethodInWrapper(int );
__delegate int statusDelegate(int status);
statusDelegate *statD;
void CallUnmanaged(int data);
};
}
#include "stdafx.h"
#include "Managed_Wrapper.h"
#using <mscorlib.dll>
namespace Managed_Wrapper
{
Managed_Wrapper_Class::Managed_Wrapper_Class(void)
{
pInner = new Unmanaged_Class();
_Status_Delegate = new Managed_Delegate_Wrapper();
_Status_Delegate->_Delegate =
new CallbackDelegate(this, &Managed_Wrapper_Class::ActualMethodInWrapper);
w_CallBack callback;
Marshal::StructureToPtr (_Status_Delegate, &callback, false);
pInner->setCallBackInUnmanaged(callback);
}
void Managed_Wrapper_Class::ActualMethodInWrapper(int status)
{
statD(status);
}
void Managed_Wrapper_Class::CallUnmanaged(int data)
{
pInner->methodToBeCalledInUnmanaged (data);
}
}
Imports Managed_Wrapper
Dim forwardCaller As Managed_Wrapper_Class = New Managed_Wrapper_Class
Dim statDelg As New Managed_Wrapper_Class.statusDelegate(AddressOf status_Recd)
Public Function status_Recd(ByVal status As Integer) As Integer
MessageBox.Show("Status received is " + status.ToString(), "Status" +
" received from the Unmanaged code")
End Function
forwardCaller.CallUnmanaged(nudNumber.Value)
forwardCaller.statD = statDelg
Compiling and Linking
Choose a C++ .NET Class Library project to wrap your unmanaged code and compile it to generate the DLL. Then in your VB.NET code, add a reference to this DLL using Add Reference->Projects (Browse to the DLL). Also you would need to have your Project properties as in the demo project on the top.
Demo
Open the Managed_VBdotNET.sln solution and start it. Bingo.
Summary
I found this technique particularly useful for one of my projects in which I had some code which was already written in C++ and was a good idea to have it written in C++. I needed to add a GUI to it, for which VB.NET was a very straightforward choice. Through this way I could invoke C++ methods through VB.NET and VB.NET methods through C++.
Any suggestions are welcome and will be appreciated. Please feel free to ask any questions.