Head-Spinning Continued: The Managed Wrapper

From Kate Gregory's Codeguru column, "Using Visual C++ .NET".

-->

In my last column I showed how It Just Works can enable you to recompile your old legacy C++ class as managed code with unmanaged data, creating a class that compiles to intermediate language but isnt garbage collected. You can use this class from a new Managed C++ application and reach all your legacy functionality from a shiny new application that runs on the .NET framework.

But lets say your legacy class is so amazing that all the developers in your organization want to use it, even the ones who use C# and VB. The techniques Ive shown in earlier columns, COM Interop and P/Invoke, result in a version of your class library that can be used from any managed language. The xcopy port does not. Only C++ can work with unmanaged data.

You could convert ArithmeticClass to a managed class, by adding the __gc keyword. However, this subjects it to all the rules that apply to managed data: for example it couldnt use multiple inheritance. If you need to change the class in any way to make it managed, you would then need to maintain two versions of the class; the managed and the unmanaged. Its a better plan to write a wrapper (in Managed C++, of course) for your unmanaged class. The wrapper class is managed code and managed data, and can be used from any managed language. As for your unmanaged class, you can compile it into intermediate language thanks to It Just Works, or you can leave it as native code and call it from the Managed C++ wrapper class, also thanks to It Just Works.

How can you leave your legacy class as native code? Well, you could leave it wherever it was (in a .lib file, say) and just call it from your managed code. (See Who Needs P/Invoke?) Or you could bring it into the project but turn off the /clr option for that particular file of source code. Just right-click the file name (LegacyArithmetic.cpp) in Solution Explorer and choose Properties. In the C/C++ properties folder, the General section includes a property called Compile As Managed. You can set this to Not Using Managed Extensions, and presto! You get native code in the middle of your .NET assembly.

Youll have to twiddle some other options, for example you must turn off pre-compiled headers for any file that is compiling to native code in a managed project, since the headers will be incompatible, but the compiler errors should lead you through the process pretty smoothly. Youll also probably have to remove any project references (which apply to all files in the project) and use the 2002-style #using approach in the files that are compiling to intermediate language.

Thats not to say you will always want to compile your legacy code as unmanaged. It all depends where you want the managed-unmanaged boundary to be. Since there is a performance cost to make that transition, you should give a little thought to putting the boundary in the place that lowers performance costs the most.

For example, say your unmanaged class exposes 5 or 10 properties, and is typically accessed through a chatty interface — one call for each property set, followed by a final call that actually does something. While youre writing the wrapper class, you decide to offer a chunky interface — one call that takes 5 or 10 parameters and uses them to do the property setting, then calls the action method. If you leave your old class as unmanaged code, this wrapper class will make the managed-unmanaged transition repeatedly as it sets the parameters. Move the old class to managed code and the boundary moves so that all the chatty calls happen entirely in managed code. Alternatively, perhaps your legacy class already has a chunky interface, and it uses some other legacy class in a chatty way. Now you want your class to stay as unmanaged code, so theres only one transition when you call it from the wrapper, and all the chattiness happens entirely in unmanaged code. You can only place this border properly when you understand the way your code operates: theres no simple cookbook solution.

Writing the wrapper class is simple enough. Heres one for good old ArithmeticClass:

public __gc class ArithmeticWrapper
{
private:
    ArithmeticClass* ac;
public:
    ArithmeticWrapper() {ac = new ArithmeticClass();}
    double Add(double num1, double num2) 
    {return ac->Add(num1,num2);}
    ~ArithmeticWrapper() {delete ac;}
};

As you can see, it simply keeps a pointer to an instance of the unmanaged class as a member variable. (It has to be a pointer; managed classes are not allowed to have member variables that are instances of an unmanaged class.) The constructor creates the instance; the destructor cleans it up, and a wrapper function for each member function in the unmanaged class just passes each call and parameters along. Every wrapper you write will be much like this one, except that since real classes have more than one method, youll have more than one wrapper method. You might also take this opportunity to tweak your interface a little, for example substituting a chunky interface for a chatty one by writing one wrapper method that calls many different methods of the wrapped legacy class.

Once you write this class, the wrapper is now managed data in managed code. C# and VB people can use it. The real guts are in the original legacy class, and both your old and new code are accessing the same guts. Youre not paying a performance penalty for COM Interop or PInvoke, though you are paying the managed-unmanaged transition costs. Since you can turn /clr on and off file-by-file, you can minimize those costs by choosing which classes compile to intermediate language and which compile to native code. It all works out.

Well, hows your head? Still attached? Ive reached the end of my list of ways to reuse old C++ code from new C++ (or even C# or VB) applications. There are plenty more interop possibilities to work through, of course, but the basic triumvirate of COM Interop, P/Invoke and It Just Works are all there is when it comes to calling old code from new. Now youve seen them all, why not try your hand at a little reuse of your own?

About the Author

Kate Gregory is a founding partner of Gregory Consulting Limited (www.gregcons.com). She is the MSDN Regional Director for Toronto, Canada. Kate's experience with C++ stretches back to before Visual C++ existed. She is a well-known speaker and lecturer at colleges and Microsoft events on subjects such as .NET, Visual Studio, XML, UML, C++, Java, and the Internet. Kate and her colleagues at Gregory Consulting specialize in combining software develoment with Web site development to create active sites. They build quality custom and off-the-shelf software components for Web pages and other applications. Kate is the author of numerous books for Que, including Special Edition Using Visual C++ .NET.


# # #



Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Wednesday, September 24, 2014 8:00 AM - 9:00 AM PDT According to a recent Forrester Research report, many companies are choosing low-code platforms over traditional programming platforms, due to the speed with which low-code apps can be assembled and tested. With customer-facing applications on the rise, traditional programming platforms simply can't keep up with the "short schedules and rapid change cycles" required to develop these applications. Check out this upcoming webinar and join Clay Richardson from …

  • As mobile devices have pushed their way into the enterprise, they have brought cloud apps along with them. This app explosion means account passwords are multiplying, which exposes corporate data and leads to help desk calls from frustrated users. This paper will discover how IT can improve user productivity, gain visibility and control over SaaS and mobile apps, and stop password sprawl. Download this white paper to learn: How you can leverage your existing AD to manage app access. Key capabilities to …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds