Managed vs. Unmanaged Code


Code and data that live in the .NET world are called managed because locations and lifetimes are managed by the common language runtime (CLR). Code and data that exist outside of .NET are called unmanaged, because there is no central mechanism for managing their lifetimes. Sometimes you have to mix the two, calling existing unmanaged code from within .NET. This section introduces some of the issues and techniques that you’ll need to consider in this situation.

Mixed Classes

Although managed classes are normally composed of other managed types, it is possible to mix managed and unmanaged types as members of classes under some circumstances. It is also possible to have a pointer to an unmanaged object as a member of a managed class, as in this example:

__gc class ManagedClass { UnmanagedClass* puc; ... };

Because the member is unmanaged, it’s up to you to manage the lifetime of the object at the other end of the pointer. You should handle this carefully: unmanaged objects sometimes need explicit deletion at a particular point in the code, and this might not fit well with the .NET garbage collection model. However, you can declare destructors for managed classes and use delete on objects of managed types, so it’s possible to arrange for correct object deallocation in most circumstances.

You can’t have an unmanaged object as a member of a managed class:

__gc class ManagedClass { UnmanagedClass obj; // compiler error ... };

An unmanaged object will work as a member class only if the host object is explicitly deleted at some point: at the end of the enclosing block for an automatic variable, at the end of the process for a global variable, or when delete is called on a pointer. Managed objects don’t work in this way, and the garbage collector can’t collect an unmanaged object.

It’s impossible to have a pointer to a managed type as part of an unmanaged class:

class UnmanagedClass { ManagedClass* pObj; // compiler error ... };

Because the unmanaged object doesn’t exist in the .NET world, the pointer to the object is invisible to the garbage collector. Thus the garbage collector doesn’t know who has a reference to the object or when it can be collected.

GCHandle

There is a way to use a managed type as part of an unmanaged class by using the GCHandle type provided in the System::Runtime::InteropServices namespace. GCHandle asks the run time to give you a “handle” to refer to a managed object from unmanaged code. You use the GCHandle::Alloc static method to create the handle, and the handle’s Free method to release it again. Here’s how you’d use GCHandle if you wanted to pass a pointer to a managed object to unmanaged code:

  1. Create a GCHandle to refer to your object. GCHandles can be converted to and from integers for ease of passing them between functions.

  2. Pass the GCHandle to the unmanaged code. As long as the handle hasn’t been freed, the run time won’t collect the object.

    Call Free on the handle when the unmanaged code no longer needs it. At this point, the run time is free to collect the object if no one else is using it.

To help you use GCHandles within unmanaged code without you having to get into the details of using Alloc and Free, Microsoft provides a helper template class named gcroot. The following short exercise shows you how to use gcroot to include a pointer to a managed type as part of an unmanaged class.

  1. Create a new Visual C++ Console Application (.NET) project named Manage.

  2. Add an #include directive for the gcroot.h system include file just below the stdafx.h include directive:

    #include <gcroot.h>

    This system header file defines the gcroot helper class.

  3. Add a using directive to the top of the code to make it easier to use the System::Runtime::InteropServices namespace:

    using namespace System::Runtime::InteropServices;
  4. Add the definition of a simple managed class to the code:

    __gc class MClass { public: int val; MClass(int n) { val=n; } };

    This class simply wraps an integer, whose value is set in the constructor.

  5. Add the definition of an unmanaged class named UClass:

    class UClass { public: gcroot<MClass*> mc; UClass(MClass* pmc) { mc = pmc; } int getValue() { return mc->val; } };

    The definition of the mc variable is an example of using a template class. Templates aren’t as widely used in managed C++ as they are in traditional C++. If you want to know more about them, consult the following sidebar in this chapter, “Templates in C++.” The definition effectively creates a gcroot variable that wraps a GCHandle to an MClass pointer. The GCHandle is created when the gcroot object is created, and it is freed when the gcroot object is destroyed.

    A UClass object is passed a pointer to a managed MClass object when it is created, and this pointer is stored away in the gcroot object. The getValue function simply returns the public val member from the MClass object, so you can verify that the code really lets you access a managed object from an unmanaged context.

  6. Modify the _tmain function to use the classes:

    int _tmain() { Console::WriteLine(S"Testing..."); // Create a managed object MClass* pm = new MClass(3); // Create an unmanaged object UClass uc(pm); int v = uc.getValue(); Console::WriteLine(S"Value is {0}", __box(v)); return 0; } 

    The code first creates a managed object and initializes it with an integer. The pointer to this object is then used to initialize an unmanaged object, and the getValue function is used to extract the value from the managed object before printing it out. When the UClass object goes out of scope, the gcroot is destroyed, which frees the GCHandle and in turn frees up the managed object.

start sidebar
Templates in C++

Templates are an advanced feature of standard C++ that can be used in managed code but aren’t needed much in code written for .NET. This sidebar provides only a brief introduction to their use in the GCHandle exercise and doesn’t cover constructing template classes. If you’re interested in constructing template classes, consult an intermediate-to-advanced text on traditional C++.

Templates provide a way to write generic classes. What does this mean? Imagine you have a class that holds a pointer to some type. At times you might want to use an int*, at other times a double* or a char*. A pointer is simply a variable that holds an address, and all pointers are the same size. You can arrange for your class to hold a pointer to anything by using Templates. Templates give you a way to write classes that contain one or more wildcard types so that the actual type will be provided at run time.

The gcroot class has been written to accept a pointer to any type of object, and when you create a gcroot object, you must specify the type that this instance of the object will contain. You do this by specifying the type in angle brackets after the type name:

// Create a gcroot to hold an MClass* gcroot<MClass*> mc; // Create a gcroot to hold a Foo* gcroot<Foo*> ff;

You can’t specify the gcroot type without something in the angle brackets, because the declaration isn’t complete unless you’ve specified the type that’s going to take the place of the wildcard.

end sidebar




Microsoft Visual C++  .NET(c) Step by Step
Microsoft Visual C++ .NET(c) Step by Step
ISBN: 735615675
EAN: N/A
Year: 2003
Pages: 208

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net