Introduction
This article is for programmers who have been programming using different languages for many years and now want to develop their applications using Microsoft .NET without rewriting their existing code logic. In this article, I have explained how a C++ (unmanaged) class can be reused in Microsoft .NET without any modifications.
I have evaluated different options like using VC++ 6.0 classes in Visual C# .NET applications, or VC++ 6.0 classes in VB.NET, and then wrote a sample application in VB.NET that uses a C++ class. Finally, I save re-writing the existing classes again for new .NET applications. Thanks to Microsoft for giving interoperable libraries for this purpose.
The VB.NET solution that I have built is not a complete application. It only implements the basic functionality needed for this tutorial.
Background
I have developed a lot of applications in VC++ 6.0, and now want to utilize the new language features of VS.NET. My company, Quntech Pvt. Limited, assigned me as a .NET developer, and gave me some modules of their new software, General Ledger Application, to work with. It’s a huge software having thirty plus modules, and currently I have to work with its persistence module. In VS.NET, different team members can use different languages for their modules. I selected VB.NET for my new module because VB.NET is one of the rapid application development languages that most software developers use today.
Getting Started
Before delving into the details, let’s decide on the classes/code that I will to put in this tutorial. In this tutorial, I haven’t included the whole story, to minimize complications. Instead, I just included the part that is relevant and within the scope of this tutorial. In this tutorial, I am using a simple VC++ 6.0 class having only three member functions, and a VB.NET simple Windows Forms Application Wizard to build my sample application that uses the C++ class.
After Reading This Tutorial
After reading this tutorial you will learn how Microsoft Interoperability Libraries work. You will also learn basic operations that can be applied on vector (C++ std Library) Data Structure.
Using Microsoft Interoperability Libraries
- Locate your C++ classes to be used.
- Create a new Project in VS.NET of type VC++ Class Library (.NET). Include all your C++ classes that you want to reuse.
- Create a new wrapper class having member functions that only calls your C++ routines. Here, interop libraries are useful.
- Build the application.
- Now, create a new VB.NET Windows Application Project, and then add a reference to the previously created wrapper, the VC++.NET wrapper class library.
- Call functions defined in the referenced library.
- The C++ classes will now be in working condition with your newly built application.
My VC++ classes
We use a C++ structure, Contact
, having the following member variables:
struct Contact
{
char FName[18];
char LName[18];
char Address[30];
char email[30];
};
The Contact
structure is the basic structure that is used in other classes.
The C++ CDiary
class stores the instances of Contact
objects in memory, as follows:
#include "Contact.h"
#include <vector>
using namespace std;
class CDiary
{
private:
vector<Contact> m_Contacts;
public:
CDiary(void)
{
}
~CDiary(v oid)
{
}
void AddContact(Contact _contact)
{
m_Contacts.push_back(_contact);
}
bool DeleteContact(const char* FullName)
{
int counter = 0;
char temp[60];
Contact* _contact;
vector<Contact>::iterator itr;
for(itr = m_Contacts.begin() ; itr!= m_Contacts.end(); itr++)
{
_contact = (Contact*)&this->m_Contacts.at(counter);
strcpy(temp , strcat(_contact->FName,_contact->LName));
if( strcmp(temp, FullName ) == 0 )
{
m_Contacts.erase(m_Contacts.begin() + counter ) ;
return true;
}
counter++;
}
return false;
}
bool SearchByName(const char* FullName, Contact *_destcontact)
{
char temp[60];
for(int i=0; i<this->m_Contacts.size(); i++)
{
strcpy(temp, ((Contact*)&m_Contacts.at(i))->FName);
strcat(temp,((Contact*)&m_Contacts.at(i))->LName);
if( strcmp(temp, FullName ) == 0 )
{
memcpy(_destcontact,
&m_Contacts.at(i),sizeof(Contact));
return true;
}
}
return false;
}
};
VC++ Wrapper Class
Here is the code for the wrapper class that wraps our unmanaged code into the managed code. Create a new VS.NET project of type VC++ Class Library (.NET).
class CDiaryWrapperClass
{
private:
CDiary *m_diary ;
Contact *m_contact;
public:
CDiaryWrapperClass::CDiaryWrapperClass(void)
{
m_diary =new CDiary();
m_contact = new Contact();
}
CDiaryWrapperClass::~CDiaryWrapperClass(void)
{
delete m_diary;
delete m_contact;
}
bool CDiaryWrapperClass::AddContact(System::String *FName,
System::String *LName, System::String *Address,
System::String *EMail )
{
System::IntPtr ptr = System::Runtime::InteropServices::
Marshal::StringToHGlobalAnsi(FName);
const char *strFName =
static_cast<const char*> (ptr.ToPointer());
ptr = System::Runtime::InteropServices::
Marshal::StringToHGlobalAnsi(LName);
const char *strLName =
static_cast<const char*> (ptr.ToPointer());
ptr = System::Runtime::InteropServices::
Marshal::StringToHGlobalAnsi(Address);
const char *strAddress =
static_cast<const char*> (ptr.ToPointer());
ptr = System::Runtime::InteropServices::
Marshal::StringToHGlobalAnsi(EMail);
const char *strEMail =
static_cast<const char*> (ptr.ToPointer());
strcpy(m_contact->FName,strFName);
strcpy(m_contact->LName,strLName);
strcpy(m_contact->Address,strAddress);
strcpy(m_contact->email,strEMail);
m_diary->AddContact(*m_contact);
return true;
}
bool CDiaryWrapperClass::DeleteContact(System::String *FullName)
{
System::IntPtr ptr = System::Runtime::InteropServices::
Marshal::StringToHGlobalAnsi(FullName);
char *strFullName =
static_cast<char*> (ptr.ToPointer());
m_diary->DeleteContact(strFullName);
return true;
}
bool CDiaryWrapperClass::SearchContactByName(
System::String *FullName,
System::String **FName, System::String **LName,
System::String **Address, System::String **EMail)
{
System::IntPtr ptr = System::Runtime::
InteropServices::Marshal::StringToHGlobalAnsi(FullName);
char *strFullName = static_cast<char*> (ptr.ToPointer());
Contact _contact;
m_diary->SearchByName(strFullName,&_contact);
*FName = System::String::Copy(_contact.FName);
*LName = System::String::Copy(_contact.LName);
*Address = System::String::Copy(_contact.Address);
*EMail = System::String::Copy(_contact.email);
return true;
}
}
Visual Basic .NET Application
Here is the VB.NET application code that uses our C++ class:
Public Class Form1
Inherits System.Windows.Forms.Form
// Member Variable of our Wrapper Class
Dim diary As New CDiaryWrapperClass
// Member Functions
// When User Clicks on Add Contact Button
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles btnAddContact.Click
Me.lblFName.Enabled = True
Me.lblLName.Enabled = True
Me.lblAddress.Enabled = True
Me.lblEMail.Enabled = True
Me.lblFName.Text = ""
Me.lblLName.Text = ""
Me.lblAddress.Text = ""
Me.lblEMail.Text = ""
Me.btnOK.Enabled = True
Me.btnOK.Text = "Add"
End Sub
// When User Clicks on Delete Contact Button
Private Sub btnDeleteContact_Click(ByVal sender _
As System.Object, ByVal e As System.EventArgs) _
Handles btnDeleteContact.Click
Me.lblFName.Enabled = True
Me.lblLName.Enabled = True
Me.lblAddress.Enab led = False
Me.lblEMail.Enabled = False
Me.btnOK.Enabled = True
Me.btnOK.Text = "Delete"
End Sub
// When User Clicks on Search Contact Button
Private Sub btnSearchContact_Click(ByVal sender _
As System.Object, ByVal e As System.EventArgs) _
Handles btnSearchContact.Click
Me.lblFName.Enabled = True
Me.lblLName.Enabled = True
Me.lblAddress.Enabled = False
Me.lblEMail.Enabled = False
Me.btnOK.Enabled = True
Me.btnOK.Text = "Search"
End Sub
// When User Clicks on OK Contact Button
Private Sub btnOK_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnOK.Click
If (Me.btnOK.Text = "Add") Then
diary.AddContact(Me.lblFName.Text, lblLName.Text, _
lblAddress.Text, lblEMail.Text)
Me.lblFName.Enabled = False
Me.lblLName.Enabled = False
Me.lblAddress.Enabled = False
Me.lblEMail.Enabled = False
Me.btnOK.Enabled = False
ElseIf (Me.btnOK.Text = "Delete") Then
diary.DeleteContact(Me.lblFName.Text + Me.lblLName.Text)
ElseIf (Me.btnOK.Text = "Search") Then
diary.SearchContactByName(Me.lblFName.Text + _
Me.lblLName.Text, Me.lblFName.Text, _
lblLName.Text, lblAddress.Text, lblEMail.Text)
Me.lblFName.Enabled = False
Me.lblLName.Enabled = False
Me.lblAddress.Enabled = False
Me.lblEMail.Enabled = False
End If
Me.btnOK.Enabled = False
End Sub
// When User Clicks on Exit Contact Button
Private Sub btnExit_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnExit.Click
Me.Close()
End Sub
Conclusion
Remember that I only instituted the relevant code here. But using the Microsoft interoperability libraries, any language can pass parameters back and forth to any other language that supports the interoperability library, easily. Developers normally don't know this fact, but it is a very important feature of Visual Studio .NET. Using this feature saves developers' time and efforts. For example, I have also persisted the vector instance to use later, but not mentioning it here because it is irrelevant to the tutorial. Using interoperability libraries ease calling routines in inter-language applications.