Click here to Skip to main content
16,016,775 members
Articles / Programming Languages / Visual Basic
Article

Calling Managed Code from Unmanaged Code and vice-versa

Rate me:
Please Sign up or sign in to vote.
4.30/5 (32 votes)
21 Mar 20053 min read 321.1K   4.6K   110   42
This article shows you how to call managed code from unmanaged code and also the other way round.

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 the Managed_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:

C++
//
/*//////////////////////////////////////////////////
//Unmanaged_Class.cpp
//////////////////////////////////////////////////*/


#ifndef UNMANAGED
#define UNMANAGED

class Unmanaged_Class
{
public:

    Unmanaged_Class();
    
    /*This is the method that is to be called from Managed code*/
    void methodToBeCalledInUnmanaged(int data);
};

#endif


*//////////////////////////////////////////////////
//Unmanaged_Class.cpp
//////////////////////////////////////////////////*/

#include "StdAfx.h"
#using <mscorlib.dll>
#include "Unmanaged.h"

Unmanaged_Class::Unmanaged_Class()
{
}

void Unmanaged_Class::methodToBeCalledInUnmanaged(int data)
{
    /*Here is the place where the Managed Wrapper code is called. */
    scallback(data+1);
}


/*//////////////////////////////////////////////////
//Managed_Wrapper.h 
//////////////////////////////////////////////////*/

#pragma once

#include "stdafx.h"
#using <mscorlib.dll>
#include "Unmanaged.h"

using namespace System::Runtime::InteropServices;
using namespace System;

namespace Managed_Wrapper
{

    /*Managed Wrapper Class */
    public __gc class Managed_Wrapper_Class
    {
    public: 
    
        //constructor
        Managed_Wrapper_Class();

        /* pInner is used to invoke the exposed 
        methods in the unmanaged class. */
        Unmanaged_Class * pInner; 


        /* An exposed function corresponding 
        to the exopsed function in Unmanaged*/
        void CallUnmanaged(int data);

    };
}

/*//////////////////////////////////////////////////
//Managed_Wrapper.cpp
//////////////////////////////////////////////////*/

#include "stdafx.h"
#include "Managed_Wrapper.h"
#using <mscorlib.dll>

namespace Managed_Wrapper
{
    Managed_Wrapper_Class::Managed_Wrapper_Class(void)
    {
        /* define the pInner object */
        pInner = new Unmanaged_Class();
    }


    void Managed_Wrapper_Class::CallUnmanaged(int data)
    {
        pInner->methodToBeCalledInUnmanaged (data);
    }

}
VB
'/*//////////////////////////////////////////////////
'//Managed Code
'//VB.NET code
'//////////////////////////////////////////////////*/


'Import the Managed Wrapper Namespace
Imports Managed_Wrapper


'Create an instance of the Managed Wrapper class.
Dim forwardCaller As Managed_Wrapper_Class = New Managed_Wrapper_Class


'To call a method in the Managed_Wrapper_Class. This method in 
'turn will call the method in the unmanaged code
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.

C++
/*///////////////////////////////////////////////////
/*Unmanaged_Class.h */
///////////////////////////////////////////////////*/

#ifndef UNMANAGED
#define UNMANAGED

#using <mscorlib.dll>

typedef void (*w_CallBack) (int status);

class Unmanaged_Class
{
public:

 Unmanaged_Class();
 w_CallBack scallback;

 /* To set the callback function. The address in ptr2F will be the
 address of a method in the Managed Wrapper class and will be assigned
 there. In this case it will be address of ActualMethodInWrapper(int );*/
 void setCallBackInUnmanaged(w_CallBack ptr2F);

 /*This is the method that is to be called from Managed code*/
 void methodToBeCalledInUnmanaged(int data);
};

#endif
C++
/*///////////////////////////////////////////////////
//Unmanaged_Class.cpp
///////////////////////////////////////////////////*/

#include "StdAfx.h"
#using <mscorlib.dll>
#include "Unmanaged.h"

Unmanaged_Class::Unmanaged_Class()
{
}

void Unmanaged_Class::setCallBackInUnmanaged(w_CallBack ptr2F)
{
 /*scallback now points to ActualMethodInWrapper(int) in
 Managed_Wrapper_Class*/
 scallback = ptr2F;
}

void Unmanaged_Class::methodToBeCalledInUnmanaged(int data)
{
 /*Here is the place where the Managed Wrapper code is called. */
 scallback(data+1);
}
C++
/*///////////////////////////////////////////////////
//Managed_Wrapper.h
///////////////////////////////////////////////////*/

#pragma once

#include "stdafx.h"
#using <mscorlib.dll>
#include "Unmanaged.h"

using namespace System::Runtime::InteropServices;
using namespace System;

namespace Managed_Wrapper
{

 /*Declare a delegate. It is to be invoked from the unmanaged code*/
 public __delegate void CallbackDelegate(int data);

 /* Declare a wrapping struct that wraps an object of the above 
 declared delegate. The delegate that this struct contains will 
 point to a method that will be called when this delegate is 
 invoked from the unmanaged code*/
 [StructLayoutAttribute( LayoutKind::Sequential, CharSet = CharSet::Ansi )]
 public __gc struct Managed_Delegate_Wrapper
 {
 [MarshalAsAttribute(UnmanagedType::FunctionPtr)]
 CallbackDelegate* _Delegate;
 };

 /*Managed Wrapper Class */
 public __gc class Managed_Wrapper_Class
 {
 public:

 //constructor
 Managed_Wrapper_Class();

 /* pInner is used to invoke the exposed methods in the unmanaged class. */
 Unmanaged_Class * pInner;

 /* Declare an instance of the wrapping struct */
 Managed_Delegate_Wrapper * _Status_Delegate;

 /* A method that will be called when the callback function in the unmanaged
 code is called. It is this method who’s pointer is passed to the unmanaged
 code.*/
 void ActualMethodInWrapper(int );

 /*A delegate type. To be used for calling managed code from here.*/
 __delegate int statusDelegate(int status);

 /*An object of the above delagate type is declared.
 It will be initialized in the managed code.*/
 statusDelegate *statD;

 /* An exposed function corresponding to the exopsed function in Unmanaged*/
 void CallUnmanaged(int data);

 };
}
C++
/*///////////////////////////////////////////////////
//Managed_Wrapper.cpp
///////////////////////////////////////////////////*/

#include "stdafx.h"
#include "Managed_Wrapper.h"
#using <mscorlib.dll>

namespace Managed_Wrapper
{
 Managed_Wrapper_Class::Managed_Wrapper_Class(void)
 {
 /* define the pInner object */
 pInner = new Unmanaged_Class();

 /* define the wrapping struct instance declared in Managed_Wrapper_Class */
 _Status_Delegate = new Managed_Delegate_Wrapper();

 /* This is the actual delegate that is contained in the wraping struct */
 _Status_Delegate->_Delegate = 
   new CallbackDelegate(this, &Managed_Wrapper_Class::ActualMethodInWrapper);

 /* declare a function pointer of the same type as in unmanaged code */
 w_CallBack callback;

 /*convert the wrapping struct to a function pointer of the above type. */
 Marshal::StructureToPtr (_Status_Delegate, &callback, false);

 /* set this function pointer in the unmanaged code using pInner.*/
 pInner->setCallBackInUnmanaged(callback);
 }

 /*This is the method in the Managed_Wrapper_Class that is called
 when the function pointer in the unmanaged code is called.*/
 void Managed_Wrapper_Class::ActualMethodInWrapper(int status)
 {
 /*This method in turn calls the delegate in the managed code.
 The method that statD is actually poiting is specified in the
 managed code itself.*/
 statD(status);
 }

 void Managed_Wrapper_Class::CallUnmanaged(int data)
 {
 pInner->methodToBeCalledInUnmanaged (data);
 }

}
VB
'/*//////////////////////////////////////////////////
'//Managed Code
'//VB.NET code
'//////////////////////////////////////////////////*/

'Import the Managed Wrapper Namespace
Imports Managed_Wrapper

'Create an instance of the Managed Wrapper class.
Dim forwardCaller As Managed_Wrapper_Class = New Managed_Wrapper_Class

'Create an instance of the delegate declared in the Managed Wrapper
'class. Initialize it with the address of the method that is supposed
'to be called when the delegate in the Managed_Wrapper_Class is called
Dim statDelg As New Managed_Wrapper_Class.statusDelegate(AddressOf status_Recd)

'This function gets called when the unmanaged code calls the
'managed wrapper code and which in turn calls the the delegate
'in there
Public Function status_Recd(ByVal status As Integer) As Integer

 'use status for something now. It took so much effort to get it :)
 MessageBox.Show("Status received is " + status.ToString(), "Status" + 
                 " received from the Unmanaged code")

End Function

 'To call a method in the Managed_Wrapper_Class. This method in
 'turn will call the method in the unmanaged code
 forwardCaller.CallUnmanaged(nudNumber.Value)

 'statD is called from the managed code. And statD in turn
 'will call the method status_Recd
 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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
Tarun is a Computer Science Grad. He believes in
".......In between and after is glorious coding"

Comments and Discussions

 
QuestionCalling win32 .dll from C# Pin
Member 1621051626-Feb-24 5:33
Member 1621051626-Feb-24 5:33 
QuestionGet an error with VS 2015 Pin
Eddy Quicksall14-Oct-15 15:32
Eddy Quicksall14-Oct-15 15:32 
QuestionCan we add more than one method in the struct? if yes, than how? Pin
saurabh saini28-Nov-13 8:12
professionalsaurabh saini28-Nov-13 8:12 
QuestionUsing the DLL from C/C++ and VB Pin
Wes Milleer15-Apr-13 9:02
Wes Milleer15-Apr-13 9:02 
GeneralManagedCode in C#--Error converting Pin
SreeluMandiga23-Feb-10 8:58
SreeluMandiga23-Feb-10 8:58 
GeneralRe: ManagedCode in C#--Error converting Pin
Steve Kirchmer5-Sep-10 23:51
Steve Kirchmer5-Sep-10 23:51 
QuestionCan i get an explanation of the above code? Pin
learningV23-Nov-09 7:45
learningV23-Nov-09 7:45 
GeneralCalling Managed function from Unmanaged code Pin
jimit4u21-Jul-09 20:57
jimit4u21-Jul-09 20:57 
GeneralRe: Calling Managed function from Unmanaged code Pin
pyth cm4-Jun-10 6:52
pyth cm4-Jun-10 6:52 
Generalpass by reference arraylist from C# to unmanaged C++ Pin
chosuenzaid041120-Sep-08 17:33
chosuenzaid041120-Sep-08 17:33 
QuestionThis Seems Awesome, only backwards from what i'm expecting to find Pin
Jeff Rasmussen25-Jun-08 18:38
Jeff Rasmussen25-Jun-08 18:38 
QuestionCalling unmanaged code from managed code Pin
sarada_k17-Mar-08 23:35
sarada_k17-Mar-08 23:35 
GeneralC# interop issue - corrupted heap on function return Pin
InstallerGuy11-Sep-07 6:38
InstallerGuy11-Sep-07 6:38 
GeneralRe: C# interop issue - corrupted heap on function return Pin
yixuuu14-Nov-07 11:26
yixuuu14-Nov-07 11:26 
GeneralModified version for VC2005 [modified] Pin
millerize6-Sep-07 21:27
millerize6-Sep-07 21:27 
First thanks for this great article!
I've tried to modified the source code(Managed_Wrapper.h and Managed_Wrapper.cpp) to work for vs2005 with /clr option, and now it works, this this the new source code(sorry I don't konw how to format the code with different colors, if someone can modify my post, please help me to change the color for reading, thanks.

//-------------------------------------------------------------------------------------------
//Managed_Wrapper.h
//-------------------------------------------------------------------------------------------

/*///////////////////////////////////////////////////
//MANAGED WRAPPER CLASS
///////////////////////////////////////////////////*/


#pragma once

#include "stdafx.h"
#using <mscorlib.dll>
#include "Unmanaged.h"

using namespace System::Runtime::InteropServices;
using namespace System;

namespace Managed_Wrapper
{

     /*Declare a delegate. It is to be invoked from the unmanaged code*/
     public delegate void CallbackDelegate(int data);

     /* Declare a wrapping struct that wraps an object of the above declared delegate. 
     The delegate that this struct contains will point to a method that will be called
     when this delegate is invoked from the unmanaged code*/
     [StructLayoutAttribute( LayoutKind::Sequential, CharSet = CharSet::Ansi )]
     public ref struct Managed_Delegate_Wrapper
     {
          [MarshalAsAttribute(UnmanagedType::FunctionPtr)]
          CallbackDelegate^ _Delegate;
     };



     /*Managed Wrapper Class */
     public ref class Managed_Wrapper_Class
     {
          public:

          //constructor
          Managed_Wrapper_Class();

          /* pInner is used to invoke the exposed methods in the unmanaged class. */
            Unmanaged_Class * pInner;

          /* Declare an instance of the wrapping struct */
          Managed_Delegate_Wrapper^ _Status_Delegate;


          /* A method that will be called when the callback function in the unmanaged
          code is called. It is this method who&#25263; pointer is passed to the unmanaged 
          code.*/
          void ActualMethodInWrapper(int );



          /*A delegate type. To be used for calling managed code from here.*/
          delegate int statusDelegate(int status);

          /*An object of the above delagate type is declared.
          It will be initialized in the managed code.*/
          statusDelegate^ statD;


          /* An exposed function corresponding to the exopsed function in Unmanaged*/
          void CallUnmanaged(int data);

     };
}


//-------------------------------------------------------------------------------------------
//Managed_Wrapper.cpp
//-------------------------------------------------------------------------------------------

/* Managed_Wrapper.cpp*/

#include "stdafx.h"
#include "Managed_Wrapper.h"
#using <mscorlib.dll>

namespace Managed_Wrapper
{
     Managed_Wrapper_Class::Managed_Wrapper_Class()
     {
          /* define the pInner object */
      pInner = new Unmanaged_Class();

          /* define the wrapping struct instance declared in Managed_Wrapper_Class */
          _Status_Delegate = gcnew Managed_Delegate_Wrapper();

          /* This is the actual delegate that is contained in the wraping struct */
          _Status_Delegate->_Delegate = gcnew CallbackDelegate(this, &Managed_Wrapper_Class::ActualMethodInWrapper);

          /* declare a function pointer of the same type as in unmanaged code */
          w_CallBack callback;

          /*convert the wrapping struct to a function pointer of the above type. */
          Marshal::StructureToPtr (_Status_Delegate, (System::IntPtr)&callback, false);

          /* set this function pointer in the unmanaged code using pInner.*/
          pInner->setCallBackInUnmanaged(callback);
     }
     


     /*This is the method in the Managed_Wrapper_Class that is called 
     when the function pointer in the unmanaged code is called.*/
     void Managed_Wrapper_Class::ActualMethodInWrapper(int status)
     {     
          /*This method in turn calls the delegate in the managed code. 
          The method that statD is actually poiting is specified in the 
          managed code itself.*/
          statD(status);     
     }


     void Managed_Wrapper_Class::CallUnmanaged(int data)
     {
          pInner->methodToBeCalledInUnmanaged (data);
     }

}



-- modified at 4:13 Friday 7th September, 2007
PS: Remember to add msvcrt.lib and msvcmrt.lib to Additional Dependencies in the linker properties

modified on Monday, December 7, 2009 4:13 AM

GeneralRe: Modified version for VC2005 [modified] Pin
jgwinner19-Oct-07 11:10
jgwinner19-Oct-07 11:10 
QuestionDelegate in Wrapping Struct Pin
ismare9-Jul-07 7:47
ismare9-Jul-07 7:47 
GeneralC# C++ Pin
xbromy21-Jul-06 1:30
xbromy21-Jul-06 1:30 
GeneralRe: C# C++ Pin
xbromy21-Jul-06 3:07
xbromy21-Jul-06 3:07 
GeneralRe: C# C++ Pin
Tarun Kapoor31-Jul-06 6:28
Tarun Kapoor31-Jul-06 6:28 
GeneralRe: C# C++ Pin
pfkk24-Nov-06 5:22
pfkk24-Nov-06 5:22 
QuestionHow about calling MFC classes? Pin
gunag18-Jul-06 22:24
gunag18-Jul-06 22:24 
AnswerRe: How about calling MFC classes? Pin
Sergey Alexandrovich Kryukov15-Jun-11 12:32
mvaSergey Alexandrovich Kryukov15-Jun-11 12:32 
Generalloader lock in net2005 Pin
EGS-WSCAD30-May-06 23:06
EGS-WSCAD30-May-06 23:06 
GeneralRe: loader lock in net2005 Pin
Tarun Kapoor2-Jun-06 5:25
Tarun Kapoor2-Jun-06 5:25 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.