Using Microsoft Interoperability Libraries






3.13/5 (4 votes)
Apr 18, 2006
3 min read

59285

493
How to use Visual C++ unmanaged code with VB.NET.
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:
// // VC++ Structure named Contact to be used in our VB.NET // Diary Application. // Store the structure in a separate file Contact.h // struct Contact { char FName[18]; // First Name char LName[18]; // Last Name char Address[30]; // Address char email[30]; // E-Mail Address };// eof: Contact
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:
// // VC++ Structure named Contact to be used in our VB.NET Diary Application. // Description: CDiary is a phone Directory class // that can be used by any application to store // and retrieve Personal Contact Details in memory #include "Contact.h" #include <vector> using namespace std; class CDiary { private: // Vector that stores list of Contact Numbers in Memory vector<Contact> m_Contacts; public: // The Default Constructor CDiary(void) { } // The Default Destructor ~CDiary(v oid) { } // Add a new Contact into the Contacts List void AddContact(Contact _contact) { m_Contacts.push_back(_contact); } // Delete an Existing Contact from the Database bool DeleteContact(const char* FullName) { int counter = 0; char temp[60]; // A Temporary instance to Contact // to store retrieved information Contact* _contact; // Iterator are the special pointers use // to iterate any Data Container. Here vector // is the Container and we want to iterate // it to retrieve information. vector<Contact>::iterator itr; // Looping Starts Here 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 FullName matches. // delete Contact from the List and return true if( strcmp(temp, FullName ) == 0 ) { m_Contacts.erase(m_Contacts.begin() + counter ) ; return true; } counter++; } return false; } // Search a Contact into the Contacts // List and returns the Contact structure. bool SearchByName(const char* FullName, Contact *_destcontact) { // Temporarily Used by the Function char temp[60]; for(int i=0; i<this->m_Contacts.size(); i++) { // To Concatenate the FirstName and Last Name strcpy(temp, ((Contact*)&m_Contacts.at(i))->FName); strcat(temp,((Contact*)&m_Contacts.at(i))->LName); // If FullName matches return // the Contact details back to the Calling Program 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).
// // This is the .NET Wrapper Class for // our C++ CDiary Class. Any .NET Compatible // Application can now use our C++ classes. // class CDiaryWrapperClass { private: CDiary *m_diary ; Contact *m_contact; public: // The Default Contructor CDiaryWrapperClass::CDiaryWrapperClass(void) { // Here we instantiate class member variables m_diary =new CDiary(); m_contact = new Contact(); } //Default Destrcutor CDiaryWrapperClass::~CDiaryWrapperClass(void) { //Here we releases the memory //allocated to member variables delete m_diary; delete m_contact; } // Member Function to call Old C++ class function AddContact bool CDiaryWrapperClass::AddContact(System::String *FName, System::String *LName, System::String *Address, System::String *EMail ) { // Here is using Interoperability // Libraries to convert Parameters 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()); // Here we prapare new Contact object // to insert into the database strcpy(m_contact->FName,strFName); strcpy(m_contact->LName,strLName); strcpy(m_contact->Address,strAddress); strcpy(m_contact->email,strEMail); // Here is Calling the old C++ function from wrapper m_diary->AddContact(*m_contact); return true; } // Member Function to call // Old C++ class function DeleteContact bool CDiaryWrapperClass::DeleteContact(System::String *FullName) { // Here is using Interoperability // Libraries to convert Parameters System::IntPtr ptr = System::Runtime::InteropServices:: Marshal::StringToHGlobalAnsi(FullName); char *strFullName = static_cast<char*> (ptr.ToPointer()); // Here is Calling the old C++ function from wrapper m_diary->DeleteContact(strFullName); return true; } // Member Function to call Old // C++ class function SearchByName bool CDiaryWrapperClass::SearchContactByName( System::String *FullName, System::String **FName, System::String **LName, System::String **Address, System::String **EMail) { // Here is using Interoperability // Libraries to convert Parameters System::IntPtr ptr = System::Runtime:: InteropServices::Marshal::StringToHGlobalAnsi(FullName); char *strFullName = static_cast<char*> (ptr.ToPointer()); Contact _contact; // Here is Calling the old C++ function from wrapper m_diary->SearchByName(strFullName,&_contact); // Here we return the values to VB.Net // ( or any other application that calls this function) *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:
'
' Description : This is the VB.NET Application
' class 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.