Microsoft & .NETVisual C#Visual C++ and CodeDom

Visual C++ and CodeDom

The types within the System.CodeDom namespace are extremely important to most Managed C++ applications, regardless of whether the code actually uses these types directly. Wizard-generated code has always formed a large part of Visual C++ applications and, by virtue of its multi-language support, .NET needed a technology that separated the definition of logical code graphs from the actual production of source code. CodeDom is the technology that provides this capability, and through the use of language-specific code providers, you can convert an abstract CodeDom graph into a source code file in any language that has a provider available.

This article covers CodeDom from a C++ perspective. (For an introduction to CodeDom from a language-agnostic perspective, check out Paul Kimmel’s “Writing Code Generators with the Code Document Object Model“.) An article on CodeDom from the perspective of a particular language may seem a little strange, but the Managed C++ code provider did not have the same good start that code providers for other Microsoft language such as C# and VB.NET did. This has had a significant impact on Visual C++.NET as a product today (as you will soon learn).

Before going into the specifics of CodeDom, a quick trip into recent history would be worthwhile to examine the relationship between Visual C++ and .NET. These reflections are entirely my own and don’t necessarily represent Microsoft’s view of the product. When .NET was publicly previewed in 2000 and 2001, C# was The .NET Language. It had the most mature compiler and a great wealth of sample code—and the fact that the Framework libraries were written in C# gave a pretty good indication that it was going to be the language of choice for .NET development. Visual Basic had a massive user base, a reasonably weak native code offering, and a very vocal community that had pockets of strong resistance to change. These factors created a situation that Microsoft is still dealing with today, but despite some problems, Visual Basic.NET has been a very adequate tool for developing .NET code from day one.

C++ and Visual C++ were different. They had a strong and mature user community, reasonable libraries for native Windows and COM development, great standard libraries for containers, strings, streams, and algorithms, and a massive existing code base that couldn’t be migrated to the .NET runtime in an automated way (even if the migration made sense, which it didn’t for many applications). These factors meant that C++ required preserving the native compiler, and Visual C++ had to be very good at interoperability between managed and native code. This interoperability work succeeded remarkably well, and placed Visual C++ in the position as the tool of choice for any interoperability task.

Work on native and interoperability features did come at a cost, however. Many features that programmers take for granted in other managed languages were missing in Visual C++. The original version of Visual C++.NET lacked many features, including designer support for Windows Forms and ASP.NET.

The root cause of many of the missing features in the original release of Visual C++.NET was the lack of a Managed C++ CodeDom code provider. Visual Studio.NET and the Framework tools use CodeDom for nearly all their Code-generation needs. Therefore, the lack of CodeDom support obviously locks a tool out of a wide range of functionality, or at least makes implementing these features extremely difficult. Some of the places where CodeDom is used include WSDL wrappers, strongly-typed DataSet generation, and the various forms designers.

The instructions for the command-line WSDL utility illustrate the difficulty in using Managed C++ with the Framework tools. They state (in part):

/language:<language>

The language to use for the generated proxy class. Choose from ‘CS’, ‘VB’, ‘JS’, ‘VJS’ or provide a fully-qualified name for a class implementing System.CodeDom.Compiler.CodeDomProvider. The default is ‘CS’ (CSharp). Short form is ‘/l:’.

Conspicuously absent is Managed C++. For the programmer who wants to use the output of this tool in a C++ project, an understanding of CodeDom is required, as well as knowledge of how to find and provide the name of the Managed C++ CodeDomProvider. This is hardly the ideal situation for a language that would like to include itself as a first-class .NET language.

Fortunately, Visual C++.NET 2003 does include a CodeDom provider. However, the MSDN Library does not properly document it, it does not live in the System assembly like the CSharpCodeProvider and VBCodeProvider, and Visual C++.NET 2003 does not include an ASP.NET Forms designer. The full name of the CodeDom provider is Microsoft.MCpp.MCppCodeProvider, and it lives in the MCppCodeDomProvider assembly (MCppCodeDomProvider.dll), which is installed in the folder C:Program FilesMicrosoft Visual Studio .NET 2003Common7IDEPublicAssemblies and in the GAC.

The lack of documentation is not an overly serious problem because CodeDom is well documented in the MSDN Library and in many independent articles and books. All CodeDom providers implement a common set of interfaces, which is well documented, and using a specific language is simply a matter of changing a few letters after the new operator.

The lack of ASP.NET designer support is an unfortunate omission, but it is worth emphasizing that this feature does not mean than Managed C++ cannot produce an ASP.NET Web site. A C++ site won’t have the same code-behind abilities as C# or VB.NET, but by deriving from the appropriate types within the System.Web namespace, a Managed C++ ASP.NET site is possible. A much easier solution is to use C# for the front-end and call C++ assemblies from the code-behind page. Language similarities make using C# in this manner easy, even if the language is entirely new to the developer, and allows the ASP.NET designer to be utilized.

Using the Managed C++ Code Provider

Despite a number of shortfalls, the Managed C++ Code Provider is still very useful. The following syntax enables you to use it with command-line tools, such as XSD.exe and WSDL.exe, that can accept the type name of a CodeDomProvider-derived class:

wsdl
/l:Microsoft.MCpp.MCppCodeProvider,MCppCodeDomProvider,
Version=7.0.5000.0,
Culture=Neutral,PublicKeyToken=b03f5f7f11d50a3a
        http://awsbeta.amazon.com/AWSAlexa/AWSAlexa.wsdl

This statement generates a header file that allows the Web service to be called in a strongly typed, simple manner. The example parses the Amazon.com webservice WSDL to produce an abstract code-graph, which the MCppCodeDomProvider type then converts into Managed C++ code. Many C++ programmers will object to placing declaration and implementation in the one, but this problem is largely a consequence of the interfaces that the C++ code DOM types are implementing (in particular ICodeGenerator.GenerateCodeFromCompileUnit, which is the method used to actually write a compile unit to a stream). The method accepts only a single stream, and the CodeDom model has no concept of separate header and source files.

Using the Managed C++ Code Provider with code is just as easy as using it with tools. Simply reference the MCppCodeDomProvider assembly, and in the places a code provider is required, use the MCppCodeProvider type. Rather than build a full code graph from scratch, this article uses the reasonably complex graph Paul Kimmel built in the sample code accompanying “Writing Code Generators with the Code Document Object Model.” In that sample, Paul built a simple Windows Forms form. His code outputted VB source code, but by changing the two lines that create the VB code provider to use the Managed C++ code provider (after adding a reference to it) and changing the file extension of the output file from “vb” to “h”, the sample can produce Managed C++ code. The changes to the original code are in italics:

' Write the source code
Dim Stream As StreamWriter = New StreamWriter("out.h")
Try
   Dim Generator As ICodeGenerator = _
      (New Microsoft.MCpp.MCppCodeProvider).CreateGenerator

   Generator.GenerateCodeFromCompileUnit( _
      Unit, Stream, Nothing)
Finally
   Stream.Close()
End Try

' Create the compiler
Dim Compiler As ICodeCompiler = _
   (New Microsoft.MCpp.MCppCodeProvider).CreateCompiler()

By running the VB.NET sample with these changes, the following code is produced:

#pragma once

#using <mscorlib.dll>

namespace codeguru {namespace com {
   using namespace System;
   using namespace System::Drawing;
   using namespace System::Windows::Forms;
   using namespace System::Xml;
   using namespace System::Data;
   using namespace System;
   public __gc class Form1;
   public __gc class Form1 : public System::Windows::Forms::Form {
      private: System::ComponentModel:: IContainer * components;
      public: Form1();
      protected: virtual System::Void Dispose
         (System::Boolean disposing);
      private: [System::Diagnostics::DebuggerStepThrough]
      System::Void InitializeComponent();
   };
   inline Form1::Form1() {
      this->InitializeComponent();
   }
   inline System::Void Form1::Dispose(System::Boolean disposing) {
      if (disposing) {
         if ((components != 0)) {
            components->Dispose();
            }
         }
      __super::Dispose(disposing);
   };
   inline System::Void Form1::InitializeComponent() {
      this->components = new System::ComponentModel::Container();
   };
}}

The original VB output was:

Option Strict Off
Option Explicit On

Imports System
Imports System.Data
Imports System.Drawing
Imports System.Windows.Forms
Imports System.Xml

Namespace codeguru.com
   Public Class Form1
      Inherits System.Windows.Forms.Form
         Private components As System.ComponentModel.IContainer
      Public Sub New()
         MyBase.New
            Me.InitializeComponent
      End Sub
      Protected Overloads Overrides Sub Dispose( _
         ByVal disposing As Boolean)
            If disposing Then
               If (Not (components) Is Nothing) Then
                  components.Dispose
               End If
            End If
            MyBase.Dispose(disposing)
      End Sub
      <System.Diagnostics.DebuggerStepThrough()> _
      Private Sub InitializeComponent()
         Me.components = New System.ComponentModel.Container
      End Sub
   End Class
End Namespace

The original code sample went on to create an actual compiler and generate an executable file. The call to ICodeCompiler.CreateCompiler on the MCppCodeProvider type returns a null reference, which means the code cannot generate an actual executable. This shortcoming is understandable considering the greater complexity of the C/C++ compiler compared with simpler managed code-only compilers such as VB.NET and C#. You can add the generated header file to a standard Managed C++ Windows Forms application, and after including the generated header file into a source file, the application will compile without any modifications to the generated code.

Visual C++.NET 2005

Using code generation via Code DOM really pays off during the transition from Managed C++ to C++/CLI. In Visual C++.NET 2005, Managed C++ is deprecated in favor of a new, simpler .NET binding officially entitled C++/CLI. Thankfully, Microsoft didn’t repeat the less-than-optimal story of the original Visual C++.NET release, and included a CodeDom provider for C++/CLI in Beta 1. In addition, the MSDN Library documented the provider.

You need to reference the CppCodeProvider assembly to use the new CodeDom provider. CppCodeProvider.dll is located in the same location under the main Visual Studio.NET install path as the previous MCppCodeDomProvider assembly. The name of the provider is now CppCodeProvider. By changing the reference and substituting CppCodeProvider for MCppCodeProvider in the CodeDom sample, you can produce the following code:

#pragma once

#using <mscorlib.dll>

using namespace System::Security::Permissions;
[assembly:SecurityPermissionAttribute(
SecurityAction::RequestMinimum, SkipVerification=false)];
namespace codeguru {namespace com {
   using namespace System;
   using namespace System::Drawing;
   using namespace System::Windows::Forms;
   using namespace System::Xml;
   using namespace System::Data;
   using namespace System;
   ref class Form1;
   public ref class Form1 : public System::Windows::Forms::Form {
      private: System::ComponentModel:: IContainer^ components;
      public: Form1();
      protected: virtual System::Void Dispose(
         System::Boolean disposing);
      private: [System::Diagnostics::DebuggerStepThrough]
      System::Void InitializeComponent();
   };
   }}
namespace codeguru {namespace com {
   inline Form1::Form1() {
      this->InitializeComponent();
   }
   inline System::Void Form1::Dispose(System::Boolean disposing) {
      if (disposing) {
         if ((components != nullptr)) {
            components->Dispose();
         }
      }
      __super::Dispose(disposing);
   };
   inline System::Void Form1::InitializeComponent() {
      this->components = gcnew System::ComponentModel::Container();
   };
}}

Notice that while using the CodeDom interfaces still results in the output being emitted to a single file, the declaration and implementation of the form are emitted individually, which allows you to create separate header and source files if you desire. Hopefully, Visual C++.NET 2005 will ship with an option to emit the files separately. Also, the generated file uses the new C++/CLI syntax.

If the old Managed C++ syntax is required, a provider capable of generating this code also ships in the CppCodeProvider assembly. The provider, implemented in the CppCodeProvider7 type, splits the declaration and implementation in the same way that CppCodeProvider does. If a separate header and source file is required for a Managed C++ application, generating the code using Visual C++.NET 2005 and compiling with Visual C++.NET 2003 is a viable option.

CodeDom is great technology that hasn’t gotten the same level of attention in C++ circles as VB.NET and C# have. This is largely due to the lack of documentation and the delay in shipping the Managed C++ provider. Despite these problems, CodeDom is just as relevant in C++ as it is in the other languages. With the major syntax changes between Managed C++ and C++/CLI, CodeDom is definitely the best technology for code generation because it allows the switch to C++/CLI to occur with the minimum amount of effort.

Biography

Nick Wienholt is an independent Windows and .NET consultant based in Sydney, Australia. He is the author of Maximizing .NET Performance from Apress, and specialises in system-level software architecture and development with a particular focus on performance, security, interoperability, and debugging. Nick can be reached at NickW@dotnetperformance.com.

Get the Free Newsletter!
Subscribe to Developer Insider for top news, trends & analysis
This email address is invalid.
Get the Free Newsletter!
Subscribe to Developer Insider for top news, trends & analysis
This email address is invalid.

Latest Posts

Related Stories