Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / VB

Using Microsoft Interoperability Libraries

3.13/5 (4 votes)
18 Apr 20063 min read 1   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

  1. Locate your C++ classes to be used.
  2. Create a new Project in VS.NET of type VC++ Class Library (.NET). Include all your C++ classes that you want to reuse.
  3. Create a new wrapper class having member functions that only calls your C++ routines. Here, interop libraries are useful.
  4. Build the application.
  5. 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.
  6. Call functions defined in the referenced library.
  7. 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

Image 1

Here is the VB.NET application code that uses our C++ class:

VB
'
' 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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here