Before the release of Visual Basic 5, many people could be heard saying COM?? Now professional developers are laughed at if they don't know what COM stands for. Well, it stands for the Component Object Model. In this article I will take you through using COM Interfaces with class modules to create an object orientated program. As we go along, I will try and explain some confusing terms and how they are useful.
A beginner's guide to COM interfaces.
Visual Basic makes using COM Interfaces much much easier then C++. In C++ you have to worry about sorting through a mound of information on GUIDs (Globally Unique IDs) and other complicated procedures and functions. Visual Basic takes care of most of this low level stuff, leaving us to have some fun.
When I first heard of COM, Interfaces and Objects I thought I had just landed on planet mars. And to tell the truth, not many other developers actually really understood these terms. So, I grabbed a copy of Deborah Kurata's award winning book, Doing Objects in MS Visual Basic. This book uses her no jargon style writing to clearly explain object orientated programming, including COM. I suggest you take a look.
At the time, many people were asking why should we use COM Interfaces/Objects? Well, the simple answer is that it saves time. One of the points of object orientated programming is code reuse, and, as I explain our demo project, you will see that it creates reusable class modules.
So far I have outlined the background of COM Interfaces etc and you are still wondering what is a COM Interface? Well, an Interface is a way of publishing a set of properties and methods so that other programs can interface with your application through COM. This can be seen in the highly popular VBA scripting language found in most new Microsoft products. To see this in action, open VB, add a reference to the Microsoft Word 8 Object Model, and load up the Object Browser. From the Libraries combo box select Word. Take a look at the properties and methods that it exposes.
To use an interface within your own program you need to use class modules. First of all you create a class that contains only methods and member variables, except you place no code within these methods. This class will be implemented into another class. Let me explain. Say this first class is called IDataOject, and it contains a GetInfo and Save method. Well, you will want another class to take on or 'implement' IDataObject's methods and properties. We do this by using the Implements keyword. We now have another class that implements the IDataObject interface.
I know what you are saying now, why on Earth is this useful? Well, instead of spending another ten pages explaining it, here is a little example.
Open VB and create a new Standard EXE Project. Click Project, Add Class Module. Repeat this. Also Click Project, References. Select Microsoft DAO 3.51 Object Library. Now you should have two class modules. Rename the first IDataObject and the second CPerson. To the IDataObject add the following code:
Option Explicit Public IsChanged As Boolean Public Sub Save() End Sub Public Sub GetInfo() End Sub
Open the second class, CPerson and add the following code:
Option Explicit Implements IDataObject Private m_db As Database Private m_rs As Recordset Private m_lngID As Long Private m_strName As String Private m_strAddress As String Private m_blnChanged As Boolean Private Sub Class_Initialize() Set m_db = OpenDatabase(App.Path & "\people.mdb") End Sub Private Sub Class_Terminate() m_rs.Close m_db.Close Set m_rs = Nothing Set m_db = Nothing End Sub Private Sub IDataObject_GetInfo() If ID = 0 Then Err.Raise vbObjectError, "CPerson", "A valid ID must be set before attempting to retrieve data." End If Set m_rs = m_db.OpenRecordset("SELECT * FROM People WHERE ID =" & ID) Name = m_rs.Fields("Name").Value Address = m_rs.Fields("Address").Value End Sub Private Property Let IDataObject_IsChanged(ByVal RHS As Boolean) m_blnChanged = RHS End Property Private Property Get IDataObject_IsChanged() As Boolean IDataObject_IsChanged = m_blnChanged End Property Private Sub IDataObject_Save() If ID <> 0 Then m_rs.Edit Else m_rs.AddNew End If m_rs.Fields("Name").Value = Name m_rs.Fields("Address").Value = Address m_rs.Update End Sub Public Property Get ID() As Long ID = m_lngID End Property Public Property Let ID(ByVal NewID As Long) m_lngID = NewID End Property Public Property Get Name() As String Name = m_strName End Property Public Property Let Name(ByVal NewName As String) m_strName = NewName End Property Public Property Get Address() As String Address = m_strAddress End Property Public Property Let Address(ByVal NewAddr As String) m_strAddress = NewAddr End Property
As you can see we are using a database to store the CPerson information. We have used DAO 3.5 for this although you can modify it to whatever data access you want. Notice that all the methods and properties have been implemented. This is neccessary when implementing an interface otherwise you will run into errors. Take a closer look at the IDataObject_Save method. If the ID is 0, then it is not a current record and therefore a new one needs to be added. Also take a look at the IDataObject_GetInfo method. As with all code in class modules, you should always raise the error and let the module / form / usercontrol handle the error.
The clever bit is now in the form's code. On the form add two command buttons (cmdGet and cmdSave) and a text box (txtName). In the General Declarations procedure declare these two variables:
Private Person As CPerson Private DataObj As IDataObject
The best code is in the Form_Load procedure. What we are going to do is to create a new instance of the CPerson class, and set the DataObj variable to the new CPerson instance. What this does is it makes both variables point to the same object, that is IDataObject. But Person also points to CPerson. So if you want to interface with the database, use the DataObj variable and if you want to interface with the person information use the Person variable. To demonstrate this, copy the following code into the form:
Private Sub cmdUpdate_Click() If DataObj.IsChanged Then Person.ID = 1 Person.Name = txtName DataObj.Save End If End Sub Private Sub cmdGet_Click() Person.ID = 1 DataObj.GetInfo txtName = Person.Name End Sub Private Sub Form_Load() Set Person = New CPerson Set DataObj = Person DataObj.IsChanged = False End Sub Private Sub txtName_Change() DataObj.IsChanged = True End Sub
This reads in the first record (cmdGet) into txtName where you can change it (and if you do then it sets the IsChanged flag to True). Then the cmdSave button saves the new name if the IsChanged flag is True.
You can now create multiple classes that implement the IDataObject interface very quickly and easily. Take a look at the example below.
Now that you have made your first COM Interface you will be wanting to look for some more examples. Well, look to http://www.eastwood60.freeserve.co.uk/ and download either version 1 or version 2 of the VBCodeLibrary project. The source code demonstrates how to use DAO 3.5 data access with COM Interfaces and Objects. Take a look, as it should help you out.
That's it from me this time, and I hope you understand COM Interfaces a bit better now. If you have any comments on this article then please email them to me at email@example.com or drop in on my Visual Basic web site at http://www.vbsquare.com/