Click here to Skip to main content
15,886,639 members
Articles / Programming Languages / C++/CLI

C++/CLI: Accessing a managed type from unmanaged code

Rate me:
Please Sign up or sign in to vote.
4.40/5 (4 votes)
21 Jan 2015CPOL4 min read 33.6K   9   6
This article aspires to explain one of the ways a managed type can be invoked from a native type and more importantly from unmanaged C/C++ code.

Background

When I started dabbling in C++/CLI, I often got confused by these words like managed code, unmanaged code, managed type, native types etc, so for someone who happens to be in the same boat, lets try to get that out of the way. Simply put, managed code would be any code that is compiled with the /clr (and its variants like /clr:pure) flag, and Unmanaged code would be the one that is not compiled with /clr (or any of its variants). The concept of managed and native types applies to managed code only. There are no such concepts in the unmanaged world. Everything is "native" inunmanaged code... but the types are not referred to "native types" as such. Again, simply put, a Managed type is a type that is defined with the "ref" keyword (ManagedType in Listing 1 below, for example). A Native type is the one that is declared without the "ref" keyword (NativeType in Listing 2, for example).
Managed types are allocated on managed stack or managed heap. Native types are allocated on native stack or native heap. The managed and native stack are much similar as far as the way memory gets cleaned up. Once the object goes out of scope, the memory is reclaimed. Memory in managed heap is managed by the garbage collector. Memory in native heap is managed by the programmer.

Accessing Managed type from Native type

First, lets briefly see how to access a managed type from a native type, but the main purpose of this post is to see how one can access a managed type from purely unmanaged code (compiled without /clr). The method is sort of explained here (http://msdn.microsoft.com/en-us/magazine/cc300632.aspx) but I found it somewhat difficult to follow so here is my attempt to simplify it a little.
As an example, lets invoke the DoSomething method of ManagedType from NativeType, and then invoke the same method from a global UnmanagedFunction (see Listing 6)
 

Listing 1 - ManagedType.h

public ref class ManagedType
{
   void DoSomething();
};  

To use the ManagedType from NativeType, one could simply try to include the reference type as a member of native type as shown in Listing 1.1 below. It will cause a compiler error. Native types are allocated on native stack/heap but managed types are not. Including a managed type as a member would mean that it will also get allocated on the native stack/heap, which would violate the C++/CLI design.
 

Listing 1.1 - NativeType.h

class NativeType
{
 public:
     ManagedType^ managedType; // error...not allowed
} 

Operator overloads make its use much the same as the underlying managed type it wraps. See the implementation of NativeType in Listing 3, for example.
 

Listing 2 - NativeType.h

#include <vcclr.h>

class NativeType
{
 public:
   NativeType();
   ~NativeType();

   void proxy();
   gcroot<ManagedType^> virtualManagedType;
}  
Listing 3 - NativeType.cpp (compile with /clr)

#include "NativeType.h"

NativeType::NativeType()
{
   //one way of assigning managed reference to gcroot<T> handle
   virtualManagedType = gcnew ManagedType();
}

void NativeType::proxy()
{
   if (virtualManagedType)
   {
     virtualManagedType->DoSomething();
   }
} 

As you can see virtualManagedType can be treated just the same as a reference to ManagedType.

Accessing Managed type from Unmanaged code

Now, lets see how to access the ManagedType from unmanaged code. It has no idea of what ManagedType is, but how about a NativeType? After all, isn't native type a simple C/C++ type albeit compiled with /clr ? The answer is not really...
 

Listing 4 - UnmanagedCode.cpp (compile without /clr)

#include "NativeType.h" // error...
... 

Doing so produces an error. The reason is the gcroot<T> member of NativeType. Remember that it wraps GCHandle which is a managed type, and unmanaged code has no idea of what that means.
Here, we can use the _MANAGED precompiler directive and intptr_t to trick the unmanaged code into believing that the referenced type is an integer type (unmanaged code) called intptr_t. _MANAGED is set when the compilation mode is /clr. We wrap the gcroot<T> handle in _MANAGED to make it invisible to unmanaged code, and expose an intptr_t instead.
 

Listing 5 - NativeType.h

#include <vcclr.h>

class NativeType
{
  public:
    NativeType();
    ~NativeType();

    void proxy();

    #ifdef _MANAGED
      gcroot<ManagedType^> virtualManagedType;
    #else
      intptr_t virtualManagedType;
    #endif

} 

We can replace the gcroot<T> handle with intptr_t because the memory semantics for both are exactly the same. The intptr_t (http://msdn.microsoft.com/en-us/library/323b6b3k.aspx) is simply an integer whose size is either 32-bit or 64-bit depending on the platform. The gcroot<T> has a void pointer (void* _handle) as its only data member, which is also 32-bit or 64-bit integer depending on the platform.
Now, we can simply include NativeType.h anywhere and use NativeType
 

Listing 6 - UnmanagedCode.cpp (compile without /clr)

#include "NativeType.h"

void UnmanagedFunction()
{
   NativeType nativeType;
   nativeType.proxy(); // calls ManagedType::DoSomething()
}

Its important to keep it in mind that what we did with the _MANAGED and intprt_t is just a trick to get the unmanaged code to "know" about the a member of NativeType called virtualManagedType that occupies a certain size memory, so that it can compile and link correctly. In both cases, the linker links to the same implementation of NativeType which is in fact managed code (remember it is /clr compiled)

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionCLR Access Pin
Member 139761575-Aug-22 7:08
Member 139761575-Aug-22 7:08 
Questionreplacing gcroot by intptr_t is not safe Pin
Yves Florido-Monnier13-Nov-18 10:22
Yves Florido-Monnier13-Nov-18 10:22 
Hi Vivek,

Replacing gcroot by intptr_t is not safe.
Please, have a look at my article wich proposes an alternative based on a safe_intptr_t, or a more functionnal gcref class (gcroot like).

Article: class gcref[^]

Best regards.
AnswerRe: replacing gcroot by intptr_t is not safe Pin
Vivek Rathod18-May-23 11:45
Vivek Rathod18-May-23 11:45 
QuestionDLL export of the Native class Pin
alex leahu11-Dec-17 22:35
alex leahu11-Dec-17 22:35 
GeneralMy vote of 5 Pin
nplumridge17-Sep-15 3:03
nplumridge17-Sep-15 3:03 
GeneralRe: My vote of 5 Pin
Vivek Rathod27-Apr-16 4:44
Vivek Rathod27-Apr-16 4:44 

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.