Introduction

This article is written for an audience of C++ programmers. Specifically, C++ programmers wanting to write a client that uses a DLL written in a .NET language.

We discuss here two approaches to writing a C++ client using Visual Studio …

  1. A C++/CLR client that calls the DLL directly
  2. A two-stage implementation that has
    1. C++ client that knows nothing about .NET
    2. An intermediate CLR wrapper class

Scroll to the end of this article if you want to download the example project.

C++ clients diagram

A third approach using COM is discussed elsewhere. If you create a COM Server using Visual Studio, you can use its functionality via the Microsoft C++ #import directive.

XSL Transformer DLL

A simple .NET DLL project is the XSL Transformer. This project is a DLL having both a .NET and a COM interface. The C++ clients discussed here use only the .NET interface.

C++/CLR Client

A C++ project compiled with the /CLR switch can use .NET CLR assemblies. The technology for this approach is Microsoft-specific. If that doesn't concern you, then the C++/CLR route is simple to implement and has little overhead.

Here's the source code of the C++/CLR client …

using namespace System;
using namespace XslTransformer;

int main(array<System::String ^> ^args)
{
    if (2 < args->Length)
    {
        Console::WriteLine (L"XSL Transformer C++ Client xml: {0} xsl:{1} html:{2}", args [0], args [1], args [2]);
        XslTransformer::Engine^ engine = gcnew XslTransformer::Engine ();
        engine->XmlFile = args [0];
        engine->XslFile = args [1];
        engine->HtmlFile = args [2];
        if (engine->Validate (true))
        {
            if (engine->Transform ())
            {
                engine->Display ();
            }
        }
    }
    else
    {
        Console::WriteLine(L"XSL Transformer: insufficent args.  Usage: CPlusPlusClient [in] xml, [in] xsl, [out] html");
    }
    return 0;
}

C++ Client using a Wrapper Interface to .NET

A C++ project compiled without the /CLR switch can not use .NET CLR assemblies. However, you may want to use exactly such a project to avoid clashes between the CLR and other standard C++ technology.

The solution shown here, and illustrated above, uses a double pointer-to-implementation (PIMPL) idiom …

  1. The C++ client has a PIMPL to the CLR wrapper
  2. The CLR wrapper implements the façade design pattern. It uses a PIMPL to an internal class that is itself a façade to the DLL

Acknowledgement: the approach described here was published by Jim Dill in his article Calling .NET from Unmanaged C++.

The PIMPL idiom lets us hide the CLR details from the C++ client. That is, the client is plain C++ and is compiled without the /CLR switch …

Wrapper Library

Here's the wrapper header (.h) file seen by the C++ client …

//  XslTransformerWrapper.h
//  Forward declaration of our PIMPL exposes no internals
class XslTransformerWrapperImpl;

class XslTransformerWrapper
{
    XslTransformerWrapperImpl* pimpl_;

public:
    //  Get/Set properties
    CString     XmlFile      () const;
    void        XmlFile      (CString const&  	xml);
    CString     XslFile      ()	const;
    void        XslFile      (CString const&  	xsl);
    CString     HtmlFile     () const;
    void        HtmlFile     (CString const&  	html);
    CString     DotNetString () const;
    void        DotNetString (CString const&  	html);
    //  Methods
    bool        Validate     (bool            alert = false) const;
    bool        Display      () const;
    bool        Transform    () const;
    //  Construction
    XslTransformerWrapper    ();
    ~XslTransformerWrapper   ();
};

Here's the wrapper implementation (.cpp) file. It is not seen by the C++ client and is compiled with the /CLR switch …

//  XslTransformerWrapper.cpp
#include "XslTransformerWrapper.h"
#include <vcclr.h>        //  Microsoft CLR extensions for Visual C++

using namespace System;
using namespace XslTransformer;

////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>	Xsl Transformer façade implementation class.
///             The class definition and implementation are both in this file.</summary>
////////////////////////////////////////////////////////////////////////////////////////////////////
class XslTransformerWrapperImpl
{    //  ^ (CLR handle) is a Microsoft C++ extension
    gcroot<XslTransformer::Engine ^> engine_;

public:
    ////////////////////////////////////////////////////////////////////////////////////////////////////
    /// <summary>	Pass-through methods </summary>
    ////////////////////////////////////////////////////////////////////////////////////////////////////
    bool            Display         ()  const              {  return engine_->Display ();       }
    bool            Transform       ()  const              {  return engine_->Transform ();     }
    bool            Validate        (bool    alert)  const {  return engine_->Validate (alert); }
    ////////////////////////////////////////////////////////////////////////////////////////////////////
    /// <summary>	Pass-through properties </summary>
    ////////////////////////////////////////////////////////////////////////////////////////////////////
    CString         XmlFile         ()  const;
    void            XmlFile         (CString const& s);
    CString         XslFile         ()  const;
    void            XslFile         (CString const& s);
    CString         HtmlFile        ()	const;
    void            HtmlFile        (CString const& s);
    CString         DotNetString    ()  const;
    void            DotNetString    (CString const& s);

    XslTransformerWrapperImpl	()
    {    //  gcnew is a Microsoft C++ extension
        engine_ = gcnew XslTransformer::Engine ();
    }
    ~XslTransformerWrapperImpl	()
    {
    }
};
////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>	The C++ wrapper class. </summary>
////////////////////////////////////////////////////////////////////////////////////////////////////
XslTransformerWrapper::XslTransformerWrapper    ()
:  pimpl_  (new XslTransformerWrapperImpl ())
{
    //  The Implemention class is constructed in the wrapper constructor
}
XslTransformerWrapper::~XslTransformerWrapper   ()
{
    //  The Implemention class is destroyed in the wrapper destructor
    delete pimpl_;
}

// Façade implementation of other properties elided for clarity
 …

C++ Simple Client

The client of the wrapper solution is plain old C++. It doesn't use the CLR and knows nothing of it, because all it can see is the wrapper header file shown above.

Here's the C++ client implementation (.cpp) file …

//  CPlusPlusSimpleClient.cpp
#include <XslTransformerWrapper.h>
#include <iostream>

int _tmain(int argc, _TCHAR* argv[])
{
    enum StringControl { BufferSize = 128, };
    TCHAR buffer [BufferSize];
    _sntprintf_s (buffer, BufferSize, BufferSize, _T("%s: argc %d\n"), argv [0], argc);
    std::wcout << buffer;
    for (int i = 0; i != argc; ++i)
    {
        _sntprintf_s (buffer, BufferSize, BufferSize, _T("arg [%d] %s\n"), i, argv [i]);
        std::wcout << buffer;
    }

    XslTransformerWrapper   wrapper;
    if (3 < argc)
    {
         //	With C++, argv [0] is the application name
        wrapper.XmlFile		(argv [1]);
        wrapper.XslFile		(argv [2]);
        wrapper.HtmlFile	(argv [3]);
        if (wrapper.Validate (true)
            &&
            wrapper.Transform ())
        {
            wrapper.Display ();
        }
    }
    return 0;
}

Download

Download

You can download the XslTransformer Visual Studio solution. It requires Visual Studio 2008 or later. The solution includes several projects …

  1. The XslTransformer project that creates the DLL
  2. A C#.NET client project
  3. A VBA client implemented in an Excel workbook
  4. A C++/CLR client project
  5. A C++ client project with two components …
    1. A C++/CLR wrapper library around the DLL interface
    2. A standard C++ client

The last two projects are described above. The first three projects are described here.

Download the ZIP archive and unpack it to a suitable location in your Visual Studio project folder. Build the solution and run the various clients to verify that it works. Open the Excel workbook, acknowledge the security check that enables macros to work, then view & test the VBA client.


Return to .NET articles index.