C++ .NET Solutions


Building .NET Components with Managed C++

Nick Hodapp

A guide to building .NET components, with a useful example and some insider tips.


Given the high level of attention focused on C# (brand new) and Visual Basic .NET (overhauled) by Microsoft and the media, C++ (proven) is often overlooked as a “real” language for .NET. Each of these languages has its own unique strengths and weaknesses, and developers need to understand these when selecting the best tool for the job. For C++ developers, Visual C++ with Managed Extensions offers a compelling route to begin leveraging the .NET Framework.

Creating .NET components in Visual C++ is simple. You can start clean with a new project (select “Managed C++ Class Library” in the New Project dialog in Visual Studio) or decide to migrate or wrap existing code. If you choose to take existing code and enable it for .NET, you’ll have to manually update your project settings to specify /CLR for compilation of one or more modules. /CLR is a new compiler switch that tells the compiler to generate MSIL (Microsoft Intermediate Language) rather than x86 instructions. It is turned off by default, so your existing projects will continue to build as expected. There are a number of restrictions and behaviors to be aware of when using /CLR (see the Visual C++ .NET documentation [1]).

Once an application is compiled to MSIL, it can begin consuming .NET Framework provided types, or any other .NET managed types. And there are a lot of types available for consumption — roughly 7,000 classes across the hundreds of namespaces in the .NET Framework alone. C++ developers currently using MFC and ATL should take note: the .NET Framework is the place to find ready-made application infrastructure, and it is all consumable and extendable from C++. Combined with powerful features like templates and high-performance native-code interop, C++ developers have a lot of options and flexibility not available in C# or VB.

Using the new compiler directive #using to consume preexisting managed types is easy: specify a .NET DLL assembly containing the desired types, and begin writing code using those types. Think of #using as similar to #import or #include — it makes visible to the compiler all the information necessary to write C++ code that references types and methods described elsewhere. Unlike #import, #using doesn’t generate a header file behind the scenes — instead the compiler reads the assembly’s metadata and populates its internal data structures directly:

// use types in the .NET Framework's
// System::Windows::Forms namespace
#using <system.windows.forms.dll>

While the compiler will generate managed code when /CLR is specified, it does nothing to magically transform existing C++ data types into CLR (Common Language Runtime) managed data types enabled with features like automatic garbage collection. To create types whose lifetimes are managed by the GC (garbage collector) and that are consumable by other .NET languages, the developer must employ the new Managed Extension keywords. For example, to create a managed class that is exported from the compiled assembly for public consumption, decorate the class definition with public __gc:

public __gc class MyClass
{
};

GC classes must be allocated with new, implying they cannot be instantiated as stack-based, or automatic, variables. Once allocated, there is no need to call delete; the GC will free the object at some point in the future when it is no longer being referenced by the application:

// no need to ever delete this managed object
MyClass* mc = new MyClass();

In addition to the obvious disparity in lifetime management schemes, GC classes and traditional C++ classes have several additional incongruities. The reason for this is that the .NET object model is quite different from the C++ object model. In practice, it isn’t hard to obey the additional rules, many of which are concisely defined in the Microsoft documentation [2] for __gc.

Perhaps the most obvious constraint on __gc classes is that they cannot multiply inherit from other classes. (Many would ask “Is this really a bad thing?”) They may singly inherit from another __gc class, and they may multiply inherit from interfaces. Another constraint states that __gc classes cannot declare friend classes or functions. These and others are restrictions of the .NET runtime, not of C++. There is simply no mapping of C++ multiple inheritance, or of friend classes, to the CLR. However, there are several CLR features that are not available in traditional C++ that are exposed through the Managed Extensions.

A good example of a CLR feature exposed in Managed C++ is object properties. You may understand properties because COM has them too; syntactically, properties behave like fields, but behind the scenes properties are implemented as methods on an object. Because C++ doesn’t have native support for COM properties, a naming convention (get_, set_) is generally used to implement IDL-defined properties as methods on C++ classes.

Managed Extensions for C++ introduces a new __property keyword that marks a method as a .NET property. (A naming convention still exists to “get” and “set” methods for a property).

public __gc class MyClass
{
public:

    __property int get_Value(void);
    __property void set_Value(int);
};

I mentioned that friend classes and functions are incompatible with .NET objects. While this seems quite an insult, .NET does have a similar notion of “internal access.” Portions of a C++ class that are marked private public: are made accessible to any type also defined in the same assembly, but are not accessible by any code that consumes the assembly.

.NET enables the concept of “declarative programming” with attributes. Attributes are applied to code constructs to specify special behavior for that construct or denote additional information associated with that construct. They serve much the same purpose as IDL attributes for COM programming. The .NET Framework provides many predefined attributes, but because attributes are simply classes, you can create your own.

The .NET Framework provides classes and attributes that make it very easy to create a managed component in C++ that is consumable by other managed languages and also that has tight integration with RAD programming tools like Visual Studio .NET. Just as you would have created ActiveX controls before that were consumable by Visual Basic, C++ developers can create .NET controls that are consumable by any number of languages. To demonstrate this, let’s examine the parts of a sample component, WindowSnapper (available for download at <www.cuj.com/code>.

WindowSnapper is a .NET component written in C++. Yes, it could have been written in VB.NET or C# and as such could have been largely implemented using .NET Framework APIs. (A single P/Invoke call to the Windows API BitBlt() would be necessary.) But this is an example to demonstrate technique. One of the primary strengths of Managed C++ is that it is very easy to keep using the unmanaged programming techniques and APIs, which you already know and understand, for writing .NET components. In this example, I rely on plain-Jane Windows GDI calls — something I’m very familiar with.

WindowSnapper enables a developer to programmatically obtain screenshots. The component is a public __gc class that derives from System::ComponentServices::Component. This grants the type a default implementation of the IComponent managed interface, enabling it to talk to containers like the Visual Studio .NET toolbox.

As long as the component is capable of sitting in the Visual Studio toolbox [3], it may as well have an icon associated with it.

An attribute named ToolboxBitmapAttribute is used on the class to specify the particular bitmap resource associated with the component. In this example, the attribute is initialized with two parameters (there are other overloads available). The first parameter specifies a type that lives in the same assembly as the resource; the second parameter specifies the name of the resource. Note that the specified type lives in a particular namespace within the assembly, and the fully qualified name of the resource must mirror this namespace hierarchy. For example, if the type is in the NickHod.Drawing namespace, the resource’s textual name is going to be NickHod.Drawing.WindowSnapper.bmp.

When you look at the sample code (available at <www.cuj.com/code>), you’ll see the attribute being initialized with __typeof(ThisAssembly). __typeof is a new Managed Extensions keyword that returns a System::Type reference for the specified type. Ideally you would simply use __typeof(WindowSnapper) here to denote that the resource lives in the same assembly and namespace as the component. Unfortunately, a known bug prevents __typeof from evaluating types that haven’t yet been defined during compilation. I use a workaround to define a private class ThisAssembly ahead of the component and specify it instead.

The second parameter denotes the name of the resource itself. There are two types of managed resources. Manifest resources are simply files that are bound to the assembly with a strong name. .NET .resx files are special XML files compiled by the .NET resgen.exe utility and bound to the assembly. ToolboxBitmapAttribute requires the bitmap to be a manifest resource.

Use the /ASSEMBLYRESOURCE compiler switch to bind a manifest resource to the assembly. The resource name will be the same as the filename. In the Visual Studio .NET IDE, this switch can be set in the Project Properties dialog under Linker\Input\Embed Managed Resource File.

namespace NickHod
{
namespace Drawing
{
    // work-around for __typeof(), below.
    private __gc class ThisAssembly
    {
    };

    // The bitmap for this component lives in file
// NickHod.Drawing.WindowSnapper.bmp
    // It is bound to the assembly with the compiler switch
// /ASSEMBLYRESOURCE:NickHod.Drawing.WindowSnapper.bmp
    [ToolboxBitmapAttribute(__typeof(ThisAssembly),
        S"WindowSnapper.bmp") ]
    public __gc class WindowSnapper :
        public System::ComponentModel::Component
    {
...

WindowSnapper has two managed properties. The first, CurrentSnapshot, is a read-only property that returns the Bitmap object that is the last snapshot taken.

[BrowsableAttribute(false)]
__property Bitmap* get_CurrentSnapshot(void);

Note the BrowsableAttribute(false) attribute; it provides a hint to designers like Visual Studio that this property shouldn’t be listed in a designer property grid. Remove the attribute temporarily and recompile to see how Visual Studio displays the CurrentSnapshot property in the property grid of the Forms designer.

The second property simply sets and retrieves a Boolean value.

In order for the component to be recognized by the toolbox in Visual Studio .NET, it must reside in the GAC (Global Assembly Cache). The GAC is a repository where assemblies that are shared by multiple applications are registered. To reside in the GAC, the component’s assembly must have a strong name, which is analogous to a GUID in COM.

The sn.exe utility creates a strong-name key file:

C:\>sn -k NickHod.Drawing.key

Microsoft (R) .NET Framework Strong Name Utility Version 1.0.3705.0
Copyright (C) Microsoft Corporation 1998-2001. All rights reserved.

Key pair written to NickHod.Drawing.key

A global, assembly-level attribute is used to reference the key file and embed the strong name in the assembly. In the AssemblyInfo.cpp module:

[assembly:AssemblyKeyFileAttribute("NickHod.Drawing.WindowSnapper.key")];

some magic happens when the compiler encounters the AssemblyKeyFileAttribute: the key file is accessed and bound to the assembly.

Now that the assembly has a strong name, it can be registered in the GAC. In a deployment scenario, the installer handles this operation. In a development environment, the gacutil.exe utility is used. In a C++ project, you can use a custom build step (in Project Properties) to register the assembly. The command line to specify for the custom build step looks like this:

gacutil /if $(TargetPath)

The /if switch specifies that gacutil should overwrite any preexisting occurrences of the assembly in the GAC and is useful if you don’t want to register multiple versions of your assembly while testing.

Also, specify for Additional Dependencies:

$(TargetPath)

The contents of the GAC can be viewed in the assembly folder found in the Windows directory. To remove an item from the GAC, simply delete the entry in this folder or unregister it with gacutil /u.

The sample project includes a test client in C# demonstrating how WindowSnapper is used.

Conclusion

Here are some additional tips and tricks to consider when writing .NET components in C++:

  1. When hard-coding managed strings into your code, use the new S prefix. The compiler will place the string in the assembly’s metadata for more optimal performance. For example:
  2.   String* s = S"Hello, World";
    
  3. There are about 35 predefined attribute classes that enable different functionality for components (see the documentation [4] for their capabilities).
  4. Experiment with #pragma managed and #pragma unmanaged. These new compiler directives enable you to control on a per-method basis what methods are compiled to MSIL or x86 (only for unmanaged data types).
  5. Update your project’s linker settings to generate a managed assembly with a name that corresponds to the namespace of the objects it implements. This convention is followed by the .NET Framework and makes it easier to see what assemblies are yours in the GAC. For example, the sample project assembly is named NickHod.Drawing.dll.

Notes

[1] <http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccore/
html/vcrefEECOMCompilation.asp>

[2] <http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmxspec/
html/vcManagedExtensionsSpec_4.asp>

[3] To add WindowSnapper to the Visual Studio .NET toolbox, select the “Components” tab of the toolbox and select “Customize Toolbox...”. Select “.NET Framework Components,” and then browse for the WindowSnapper.dll assembly. Selecting OK will place the component on the toolbox, ready to be used in a project.

[4] < http://msdn.microsoft.com/library/en-us/cpref/html/
frlrfsystemattributeclasshierarchy.asp>

Nick Hodapp is Microsoft’s product manager for Visual C++ .NET, a developer tool for creating best-in-class applications for Windows and .NET using the C++ language. Previously Nick enjoyed an eight-year career in the Midwest developing Windows-based GIS software for telecommunication and power utilities.