The Wayback Machine - https://web.archive.org/web/20090214004732/http://www.codeguru.com:80/cpp/cpp/cpp_mfc/classes/article.php/c13201/

CodeGuru
Earthweb Search
Forums Wireless Jars Gamelan Developer.com
CodeGuru Navigation
RSS Feeds

RSSAll

RSSVC++/C++

RSS.NET/C#

RSSVB

See more EarthWeb Network feeds

follow us on Twitter

Member Sign In
User ID:
Password:
Remember Me:
Forgot Password?
Not a member?
Click here for more information and to register.

jobs.internet.com

internet.commerce
Partners & Affiliates
















Home >> Visual C++ / C++ >> C++ >> C++ & MFC >> Classes and Class Use


Calling .NET from Unmanaged C++
Old MFC code can use ADO.NET
Rating: none

Jim Dill (view profile)
February 1, 2007

Environment:  C++/MFC

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.


(continued)



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.

Layers

  • 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.

Example

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
{
public:
   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();

protected:
   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
{
public:
   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:

NL_OleDbConnection::NL_OleDbConnection()
   : 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)) { }

NL_OleDbConnection::~NL_OleDbConnection()
   { 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.

Project

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
Developer at CambridgeSoft Corp. Hobby: TrainPlayer Software.

Downloads

  • NetClient.zip - Demo app with dialog and wrapper classes

    Tools:
    Add www.codeguru.com to your favorites
    Add www.codeguru.com to your browser search box
    IE 7 | Firefox 2.0 | Firefox 1.5.x
    Receive news via our XML/RSS feed







  • RATE THIS ARTICLE:   Excellent  Very Good  Average  Below Average  Poor  

    (You must be signed in to rank an article. Not a member? Click here to register)

    Latest Comments:
    Make the paths relative - jonp71 (03/20/2007)

    View All Comments
    Add a Comment:
    Title:
    Comment:
    Pre-Formatted: Check this if you want the text to display with the formatting as typed (good for source code)



    (You must be signed in to comment on an article. Not a member? Click here to register)

    internet.comearthweb.comDevx.commediabistro.comGraphics.com

    Jupitermedia Corporation has two divisions: Jupiterimages and JupiterOnlineMedia

    Jupitermedia Corporate Info

    Legal Notices, Licensing, Reprints, Permissions, Privacy Policy.
    Advertise | Newsletters | Tech Jobs | Shopping | E-mail Offers