Intro to Managed C++, Part 2: Mixing Managed and Unmanaged Code
Pages: 1, 2
Making Your Classes Managed
As I have stated previously, when native C++ code is recompiled with /clr, the
classes don't automatically become managed and are marked __nogc for the
reasons I cited. If your class does meet the requirements of the CLR, however,
you can make your class managed by marking it with the __gc modifier to
indicate that it is a garbage-collected class, or the __value modifier to
indicate that it is a CTS value type.
Embedding Unmanaged Types in Managed Classes
What if your class cannot be made managed by adding the __gc or __value
modifiers? You may have code that uses templates or multiple inheritance. You
may have code that uses inline assembly to reuse the functionality from
something you would usually inherit from that class. For obvious reasons
outlined earlier, you cannot inherit a managed class from an unmanaged one, and
vice versa. So what do you do if you want to reuse the functionality? For
problems like this, the general solution is to either "aggregate" or "embed a
pointer." Without going into a lot of low-level details, aggregating an
unmanaged class within a managed one causes a lot of problems, and the compiler
cannot convert an object from the fc heap to a non-GC reference. The way to do
this is to embed a pointer to an unmanaged type within the managed class. It
looks something like this (a contrived example):
#using <mscorlib.dll>
using namespace System;
#include <string>
__nogc class Container
{
int value_;
public:
Container() : value_(0) {}
void SetValue(int *val) { value_ = *val;}
const int& GetValue() { return value_; }
};
__gc class ManagedContainer
{
Container* pContainer;
public:
ManagedContainer()
{
pContainer = new Container();
}
void SetValue(int val)
{
int someValue = val;
pContainer->SetValue(&someValue);
}
~ManagedContainer()
{
delete pContainer;
}
};
void main()
{
ManagedContainer *mc = new ManagedContainer();
int someValue = 42;
mc->SetValue(someValue);
System::Console::WriteLine("The value is ",
someValue.ToString());
}
In this solution, I create an embedded pointer to the the unmanaged class type
Container inside ManagedContainer and control it explicitly. Will this code
work? Perhaps sometimes, but there is a big problem with the code as it stands.
Pinning Pointers
The problem is that the CLR, in its GC, moves object references around. This
is not a problem until such a reference is passed to an unmanaged function or
used in an unmanaged object. The CLR has no way to keep track of the reference
once it transitions to unmanaged code. So in order to prevent the value from
getting corrupted, we need to "pin" the pointer using the __pin keyword:
#using <mscorlib.dll>
using namespace System;
#include <string>
__nogc class Container
{
int value_;
public:
Container() : value_(0) {}
void SetValue(int *val) { value_ = *val;}
const int& GetValue() { return value_; }
};
__gc class ManagedContainer
{
Container* pContainer;
public:
ManagedContainer()
{
pContainer = new Container();
}
void SetValue(int val)
{
int someValue = val;
int __pin* pinnedInt = &someValue;
pContainer->SetValue(pinnedInt);
}
~ManagedContainer()
{
delete pContainer;
}
};
void main()
{
ManagedContainer *mc = new ManagedContainer();
int someValue = 42;
mc->SetValue(someValue);
System::Console::WriteLine("The value is ",
someValue.ToString());
}
This type of approach leads to the ability to wrap your C++ code with managed wrappers, enabling your C++ code to be used from other managed languages like C# and VB.NET. I will explore more of this in detail, in the next article of this series.
Now, what if we want to go the other way? That is, use managed types from unmanaged code?
Using Managed Types from Unmanaged Code
Managed types cannot directly be used from unmanaged types. This is again
related to the fact that the CLR must keep track of object references to implement
garbage collection. What if we did want to use an object reference in an unmanaged
type? The CLR provides a type, System::Runtime::InteropServices::GCHandle, that
treats object references as integers from unmanaged code. The technique for
using this function is quite simple: call GCHandle::Alloc() to generate a handle
and GCHandle::Free() to free it.
However, this pattern can get quite messy, so there is a better way. In the file
gcroot.h, the VC++ team has provided a smart pointer, called gcroot<>, to
simplify the use of GCHandle in unmanaged types. With this template, we are
able to use a System::String in an unmanaged class, like so:
#using <mscorlib.dll>
#include <vcclr.h>
using namespace System;
class CppClass {
public:
gcroot<String*> str; // can use str as if it were String*
CppClass() {}
};
int main() {
CppClass c;
c.str = new String("hello");
Console::WriteLine( c.str ); // no cast required
}
What's Next?
In this article, I have only scratched the surface of what's possible. The next step is to further look at what IJW accomplishes and where it falls short, and how to make functions managed, how to make your data managed, and how to write managed wrappers around unmanaged functions.
Sam Gentile is a well-known .NET consultant and is currently working with a large firm, using Visual C++ .NET 2003 to develop both unmanaged and managed C++ applications.
Return to ONDotnet.com
-
pin pointer always needed?
2007-10-19 02:28:26 niemaz [View]
-
Managed C++ with Unmanaged C++
2007-04-04 22:46:10 mugaland [View]
-
nice article
2006-03-12 11:55:58 himanshuabc [View]
-
Correction: Inheriting from unmanaged type
2005-08-04 01:57:00 Chanveda [View]
-
Inheriting from unmanaged code
2005-08-04 01:53:35 Chanveda [View]
-
Unmanaged to managed c++
2005-05-30 01:14:49 lubo [View]
- Trackback from http://dotnetjunkies.com/WebLog/josephcooney/archive/2004/03/25/10001.aspx
ManagedZilla - A Failed First Attempt
2004-03-25 08:48:13 [View]
-
c++ threads using .net libraries
2004-02-12 12:31:05 lukedickens [View]
-
Object browsing
2003-12-05 07:41:39 anonymous2 [View]
-
Specialized C++ DLLs Too?
2003-08-05 10:31:40 anonymous2 [View]
-
base type array problems
2003-07-24 02:08:56 anonymous2 [View]
-
base type array problems
2003-09-03 03:56:50 anonymous2 [View]
-
some questions
2003-07-01 08:32:30 barriegreen@msn.com [View]
-
managed C++ example
2003-06-25 06:46:47 anonymous2 [View]
-
managed C++ example
2009-12-14 15:27:24 hvete [View]
-
managed C++ example
2003-07-18 03:05:40 francesca [View]
-
Exception handling in mixed mode
2003-04-30 06:21:38 anonymous2 [View]
-
Exception handling in mixed mode
2003-04-30 06:25:36 c1sbc [View]
-
Calling MFC from .NET
2003-04-20 23:36:00 anonymous2 [View]
-
Pinning a stack variable?
2003-04-10 10:47:39 anonymous2 [View]
-
great article !
2003-03-17 03:36:34 anonymous2 [View]
-
calling a C# function in a C++ program
2003-03-13 08:01:19 anonymous2 [View]
-
Error with example
2003-03-05 19:14:50 anonymous2 [View]
-
Error with example
2003-05-09 12:08:35 anonymous2 [View]
-
Sam - Not shipped with .NET
2003-05-09 12:16:44 anonymous2 [View]
-
No error
2003-03-07 13:07:11 samgentile [View]
-
Why C#?
2003-03-05 14:34:52 anonymous2 [View]

