Calling .NET from Unmanaged C++

My coworker Julie Nelson and I work on a legacy app written in C++/MFC. We wanted to take advantage of some managed devices such as .NET controls and web services, but the app is too big to rewrite in C#, so we needed a way to call these devices from the existing code. This article describes what we came up with: a fairly general method for calling managed code from unmanaged. The technique is demonstrated by a little dialog app that can communicate with the ADO.NET data source of your choice.

The idea is simple, and the work involved is mostly editing. For any .NET class or web service you want to use, you develop a set of wrapper functions exposing the methods and properties you need. The wrappers are in three parts: public interface, private implementation, and translation layer between the two. These parts are distributed in a particular way between .cpp and .h files, so that the latter can be included in unmanaged code while the former can call managed classes. There is a systematic naming convention, so you can look at a call and know which system function it wraps.

The client app for demonstrating the technique takes an ADO.NET connect string, connects to a data source, and retrieves schema data or query results into a grid. It uses a few System.Data.Oledb classes—Command, Connection, DataAdapter, and DataReader—wrapped and callable from the standard MFC dialog app.


  • Public interfaces (.h file). These specify the routines your app is allowed to call. The interfaces are in terms of friendly types (like CString), and the .h file can be included in your normal MFC app. Class names all start with "NL_" (for NetLib), followed by the name of the system class being wrapped. Each NL class has a member pointer to its implementation.
  • Private implementations (.cpp file). Each NL class has a corresponding "NLI_" (NetLib Implementation) class in the .cpp file. These classes convert arguments as necessary and call the managed system routines. Each NLI class has a garbage-collected pointer to a system class object.
  • Public implementations (.cpp file). The NL interfaces in the .h file just pass their arguments to the equivalent NLI functions.


One of the classes you want to use is System::Data::OleDb::OleDBConnection. You need capabilities to establish a connection, obtain a command object, and access some schema information.

The public interface in NETLib.h exposes these functions in the form of an NL wrapper class:

class NL_OleDbConnection
   NL_OleDbConnection(NLI_OleDbConnection *p);
   NL_OleDbConnection(const CString& conn);
   virtual ~NL_OleDbConnection();

   CString             DataSource();
   CString             ConnectionString();
   void                Open();
   void                Close();
   NL_OleDbCommand*    CreateCommand();
   NL_DataTable*       GetSchema();

   friend class NLI_OleDbCommand;
   NLI_OleDbConnection *m_pi;

Each NL class has a default constructor, plus one that takes a pointer to the implementation, plus possible others for convenience (in this case, one for constructing from a string). This example has two read-only properties returning strings, Open/Close methods, plus two properties that return other objects as NL pointers.

Forward declarations are necessary for the compiler. There are many, so we collected them in a separate header file (class_defs.h), which has two lines per class:

class NL_OleDbConnection;
class NLI_OleDbConnection;

The private implementation in NETLib.cpp looks like this:

class NLI_OleDbConnection
   NLI_OleDbConnection() : m_OleDbConnection(gcnew OleDbConnection) { }
   NLI_OleDbConnection(OleDbConnection^ g) : m_OleDbConnection(g) { }
   NLI_OleDbConnection(const CString& conn)
   : m_OleDbConnection(gcnew OleDbConnection(gcnew String(conn))) { }

   CString             DataSource()
      { return m_OleDbConnection->DataSource; }
   CString             ConnectionString()
      { return m_OleDbConnection->ConnectionString; }
   void                Open()
      { m_OleDbConnection->Open(); }
   void                Close()
      { m_OleDbConnection->Close(); }
   NL_OleDbCommand*    CreateCommand();
   NL_DataTable*       GetSchema();

   gcroot<OleDbConnection^> m_OleDbConnection;

The actual system object is the member m_OleDbConnection. Most NLI functions are simply inline calls to members or properties of that object. More complicated functions are implemented elsewhere in the .cpp file.

Connecting the two parts is a series of translation calls in NETLib.cpp:

   : m_pi(new NLI_OleDbConnection) { }
NL_OleDbConnection::NL_OleDbConnection(NLI_OleDbConnection *p)
   : m_pi(p) { }
NL_OleDbConnection::NL_OleDbConnection(const CString& s)
   : m_pi(new NLI_OleDbConnection(s)) { }

   { delete m_pi; }
CString NL_OleDbConnection::DataSource()
   { return m_pi->DataSource(); }
CString NL_OleDbConnection::ConnectionString()
   { return m_pi->ConnectionString(); }
void NL_OleDbConnection::Open()
   { m_pi->Open(); }
void NL_OleDbConnection::Close()
   { m_pi->Close(); }
NL_OleDbCommand* NL_OleDbConnection::CreateCommand()
   { return m_pi->CreateCommand(); }
NL_DataTable* NL_OleDbConnection::GetSchema()
   { return m_pi->GetSchema(); }

where each NL routine calls the corresponding routine of the implementation object, m_pi.


The included project is for VS2005, and requires the .NET Framework 2.0. Assuming it builds and works, what you get is a little data source browser, which is fairly handy but not intended to serve any real purpose. It demonstrates a few wrappers for a few classes, and should have enough code to show how you can go on to develop your own.

-- Jim Dill

About the Author

Jim Dill

Developer at CambridgeSoft Corp. Hobby: TrainPlayer Software.



  • Make the paths relative

    Posted by jonp71 on 03/20/2007 07:35am

    You'll want to set WSClient's "Additional Include Directories" setting to "$(ProjectDir)NETLib";"$(ProjectDir)JLib" instead of the hard-coded ones that are in the project now. Other than that, this is a really nice hack!

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

Top White Papers and Webcasts

  • Hybrid cloud platforms need to think in terms of sweet spots when it comes to application platform interface (API) integration. Cloud Velocity has taken a unique approach to tight integration with the API sweet spot; enough to support the agility of physical and virtual apps, including multi-tier environments and databases, while reducing capital and operating costs. Read this case study to learn how a global-level Fortune 1000 company was able to deploy an entire 6+ TB Oracle eCommerce stack in Amazon Web …

  • Mobile is introducing sweeping changes throughout your workplace. As a senior stakeholder driving mobile initiatives in your organization, you may be lost in a sea of technologies and claims from vendors promising rapid delivery of applications to your employees, customers, and partners. To help explain some of the topics you will need to be aware of, and to separate the must-haves from the nice-to-haves, this reference guide can help you with applying a mobile strategy in the context of application …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds