Hixm AddttM ftrttta

DnpltfiUfff Imrrfocfl ..

J Rtftrtni«... A Cerwri Clm« — V 1st '"i-KiCi...

AddtoGifcrs Q UoaFctbi...

Grai^ibyfliresj v tuif*«J

Figure 3-10: Activating the Implement Interface Wizard The resulting dialog box lists each IDL interface that is currently not supported by the C++ ATL coclass. Once you check off support for ITurbo (Figure 3-11), you see the following source code modifications:

■ The new interface has been added to the class' inheritance chain.

■ The class' COM map has been updated with a new COM J NTERFACE_ENTRY listing.

Implement Inltrtat

AruCAflJil^lHLti I 3 'vlw n


Figure 3-11: Supporting a new COM interface using ATL

Here are the relevant code updates:

// After running the wizard, your ATL coclass is // equipped to return the new interface.

class ATL_NO_VTABLE CComCar :

public CComObjectRootEx< CComSingleThreadModel>, public CComCoClass< CComCar, & CLSID_ComCar>, public IComCar, public ITurbo


At this point, you can make use of the Add Method tool as before:

STDMETHODIMP CComCar::TurboBlast() {

MessageBox(NULL, "Turbo blast!!", "ATL ComCar", MB_OK); return S_OK;

So, that wraps up this rapid-fire tour of developing basic COM servers with ATL. As I am sure you would agree, aTl greatly simplifies the creation of C++-based COM servers. Obviously, there is a great deal more to ATL than what I have covered here. You will see additional aspects of ATL when you examine COM connection points, COM error handling, and the COM enumeration object (IEnumXXXX). Nevertheless, at this point you should be able to move around the ATL framework a bit more fluidly.

CODE The AtlCarServer application is included under the Chapter 3


The Role of Visual Basic 6.0

ATL is a vast improvement to the raw C++/IDL development cycle, yet it poses one minor problem (depending on your point of view): ATL still uses C++. To be frank, C++ will never win an award for the most elegant programming language (or most user friendly, or most intuitive, or . . .). C++ is a powerful language, and when you need to build a very complex COM server that makes use of numerous advanced techniques such as tear-off interfaces, COM categories, custom marshaling, and so forth, C++ is an absolute necessity. However, when it comes to raw productivity, nothing comes close to Visual Basic 6.0.

When developers build COM servers using VB 6.0, they are making a conscious choice to focus on nothing but the business logic of the current problem domain. As alluded to in the previous paragraph, VB COM servers are unable to take advantage of advanced COM programming patterns. Likewise, VB 6.0 does not allow you to directly establish GUID values, edit (or alter) the generated IDL code, or participate in exotic COM threading models. Nevertheless, VB 6.0 is the most popular COM development paradigm in use, given that many applications don't need to use these advanced features in the first place. To see just how simple building a COM server can be, let's recreate the essence of ComCar from the cozy confines of VB 6.0.

Building COM Servers Using Visual Basic 6.0

Visual Basic supports two core project workspace types used to build in-proc or local (and remote) COM servers: ActiveX DLLs and ActiveX EXEs (see Figure 3-12).





tkjf&t M


P'VT'J WV-ÏJ*' CoiimntU CwiiwnEii

P'VT'J WV-ÏJ*' CoiimntU CwiiwnEii in-n EiiKàiui feJHriM

Figure 3-12: Core VB 6.0 COM project types

Having chosen an ActiveX DLL project workspace, you will be presented with a single *.cls file. Unlike most programming languages, VB 6.0 does not support specific language keywords that are used to build class and interface definitions. Rather, each COM type is placed in a *.cls file (as you may be aware, VB .NET does support specific keywords). To begin, change the name of this initial class type to CoCar using the

(Name) property in the Properties window (Figure 3-13).

Properties -



1 CoCar ClassModule


1 Alphabetic

Categorized |


DatáBindlnffÓhá VÍor

0 ■ vbPtone

bata5ourœBeha vior

0 - vbMons


5 ■ MultiUse


0 -NotAnMTSObject


0 ■ MotPersist-üble


| (Name)

Returns the name used in code to identify a iorm,

control; or data access object,

Figure 3-13: Naming your coclass As you learned in the previous chapter, COM development demands the use of interfaces. However, given that VB 6.0 generally attempts to hide interfaces from view, you receive a [default] interface automatically as you add Public properties, functions, and subroutines to a given *.cls file. Put another way, each *.cls file is expressed as the [default] interface of the coclass. Therefore, if you add the following VB code to CoCar.cls, you are actually defining the members of the [default] interface of the CoCar coclass.

Option Explicit

' Define class level variables in the ' [General][Declaration] section.

Private mCurrSpeed As Long Public Property Get Speed() As Long

Speed = mCurrSpeed End Property

Public Property Let Speed(delta As Long)

mCurrSpeed = mCurrSpeed + delta End Property

Defining Auxiliary Interfaces

As mentioned, Visual Basic 6.0 does not supply a keyword to define a COM interface. Rather, interfaces are placed into *.cls files and are represented as empty method (or property) implementations. This is just about as close as VB 6.0 comes to the concept of a pure virtual function. If you insert a new *.cls file (using the Project | Add Class Module menu selection), you are free to define the following IVBTurbo interface (be sure to change the name of the class file accordingly):

' A VB 6.0 interface definition

Option Explicit Public Sub TurboBlast() End Sub

When you are building a VB 6.0 interface definition, it is good practice to set the type's Instancing property to PublicNotCreatable (Figure 3-14).

Properties - IVBTurbo y

IVBTurbo ÇlassModule Alphabetic | Categorised |



DataEiin ding Behavior

0 - vbNone

Data5ourceBeha v ior

0 - vbNone


2 - PublicNotCreatable ▼ |


0 - NotAnMTSObject


0 ■ NotPer sizable

5ets a value thaï specifies whether1 you tan create instances of a public class outside a project.

Figure 3-14: Interfaces should not be directly creatable.

This is considered good practice because it will prevent the COM client from directly "New-ing" the interface. Any attempt to do so will result in a compiler error:

' PublicNotCreatable types cannot be directly created!

Dim itfIVBTurbo as New IVBTurbo ' Nope!

Implementing Interfaces in VB 6.0

When you wish to implement additional interfaces on an existing COM type, make use of the Implements keyword. Note that Implements definitions must appear in the [General][Declarations] section of a given *.cls file. Once you have specified which behaviors your coclass supports, you may make use of the VB 6.0 IDE to generate stub code for each member of a particular interface. Simply select the interface (by name) from the left-hand drop-down list and each interface member from the right-hand dropdown list. The finished product is shown in Figure 3-15.

Figure 3-15: The completed VB 6.0 CoCar

When you make use of the VB 6.0 IDE to generate default stub code, notice that the members are declared as Private rather than Public:

Private Sub IVBTurbo_TurboBlast() mCurrSpeed = mCurrSpeed + 50 End Sub

The reason for this is simple. If you declare this type as Public, you will suddenly have a member named IVBTu rbo_TurboBlast() as a member of the [default] public interface! In such a case, the object user would need to make use of the following oddball syntax:

Dim c3 as CoCar Set c3 = New CoCar c3.IVBTurbo_TurboBlast

By defining the TurboBlast() method of the IVBTurbo interface as Private, you force the user to obtain the IVBTurbo interface explicitly:

Dim c4 as CoCar Set c4 = New CoCar Dim iftIVBTurbo as IVBTurbo Set iftIVBTurbo = c4 IftIVBTurbo. TubroBlast

At this point, you are free to compile your VB 6.0 COM server using the File | Make menu selection. Notice that you don't need to manually create any IDL definitions. Also notice that each DLL export and the required class factory have been supplied on your behalf.

As a final positive note, VB 6.0 will automatically register this COM server on your development machine as part of the compilation cycle (thus you can hunt down the registration entries using regedit.exe).

Setting Binary Compatibility

I have one final point to make regarding VB 6.0 COM development. Because VB is attempting to simplify the creation of COM servers, GUIDs are assigned automatically behind the scenes. In fact, each time you compile your project, VB 6.0 will generate new GUIDs for your COM types! This is obviously a huge annoyance, given that any existing clients using this COM server are effectively broken.

To prevent this GUID generation madness from occurring, get in the habit of enabling binary compatibility as soon as you have performed your first compile. When you do so, VB will stop generating new GUIDs and freeze the current identifiers. If you attempt to alter the definition of any interface, you will be warned through a series of dialog boxes. To specify this type of version compatibility, choose the Binary Compatibility option in the Project Properties dialog box (Figure 3-16).

VbfcCdrSef rar - Project Propertiei X

Ctnpd I M*e I Coon* Component | Cctug^gj start Mujc f amiimt

Ctnpd I M*e I Coon* Component | Cctug^gj start Mujc f amiimt

P(«l£)ti Sirvir I- Psrimte SflVirK«

C [}:. CHnwiUty r priSu'CampitbHv


UK | CaKri j Help

Figure 3-16: Freezing the current GUID values Viewing the Generated IDL Using Oleview.exe

To prove that the VB 6.0 IDE is maintaining the same binary standard as a COM server created using C++ or ATL, you need to be introduced to the oleview.exe utility. This tool, which ships with Visual Studio, allows you to investigate the set of registered COM servers on your development machine. Using oleview.exe, you are able to view the set of interfaces supported on a given object (provided the interface has been registered under HKCR\Interface), the underlying IDL, and the numerous registration entries for the COM binary. Figure 3-17 shows the set of expandable nodes.

■ Ol EKOW Object

ye QblMl lew bit

0*1**1 Al II 1

- ilObwetikwr?

- jJ lita^Mi

Jf-, AlOtKdi


Figure 3-17: The oleview.exe utility The most important node in this instance is the All Objects category. Once you expand this node, you will be able to find the CoCar and IVBTurbo types listed alphabetically by ProgID (Figure 3-18).

- Ul.tJEUUUbfttlVtorcr


fr ÎthT« (J*

Slrl A- in M

î KflnnffClwrAïJrt

Î EiflffwiicrTPKtjTi

a A

ta»SFF5 2Fii&4fi£aAi«Ti?9M2AM£f7i



_______ i fc .-j*_r"____

♦ f ittanomti.coc* v

< >

< >

Figure 3-18: Locating your VB 6.0 COM types As you can see, VB has automatically implemented a number of standard COM interfaces on the CoCar type. You will get to know the role of these interfaces as you progress through the text, but for now Table 3-2 provides a quick rundown of the core behaviors (grouped by related functionality).

Figure 3-18: Locating your VB 6.0 COM types As you can see, VB has automatically implemented a number of standard COM interfaces on the CoCar type. You will get to know the role of these interfaces as you progress through the text, but for now Table 3-2 provides a quick rundown of the core behaviors (grouped by related functionality).

Table 3-2: COM Interfaces Automatically Supported by VB 6.0 COM

VB 6.0 Autoimplemented Interface

Meaning in







Release(), and


for each COM


< ClassName>

Recall that VB



generates a


interface, which

is populated

with each Public

member defined

in the *.cls file.

The name of the


interface is



ass. Thus, if you

have a class

named CoCar,

the default

interface is



These two


interfaces allow

a COM class to

send events to a




Provide late-

Table 3-2: COM Interfaces Automatically Supported by VB 6.0 COM

VB 6.0 Autoimplemented Interface

Meaning in Life


binding capabilities. Required for late- bound scripting languages such as VBScript.


Allows a COM class to send COM "error" objects to report a processing error.

Now on to viewing the IDL itself. Simply right-click the coclass icon and select View Type Information from the context menu. The end result is shown in Figure 3-19.

<■ 1 r Tpc-I il> Virwrp


Efc »M

iOj J J

> doprteftea _CoCar + ? Harf-K* JloCfir + ö twtasfGiCir + if dKmr«* j^fiTvto

* ^ ntrtm* JtiQfijrto

* aa cK^Ki iverurtB

Conors rod [DL iilo (by iJio UX^CCH *


lkbr«ry Vtttufîonmr *

Figure 3-19: Viewing the IDL

Figure 3-19: Viewing the IDL

If you select the CoCar type, you will see an IDL definition that should look quite familiar at this point.

// The VB CoCar type definition.

[ uuid(E93D5FF5-3F76-4BE9-A547-5398B2AA4CF7), version(1.0)] coclass CoCar { [default] interface _CoCar; interface _IVBTurbo;

Here, you can see that the [default] interface is indeed _CoCar. This interface defines a single COM property. As you may remember, COM properties are defined using the [propget] and [propput] IDL keywords.

// The [default] interface.

[ odl, uuid(BFC753BA-4CEB-4682-BD63-8973D3CB2186), version(1.0), hidden, dual, nonextensible, oleautomation ] interface _CoCar : IDispatch { [id(0x68030000), propget] HRESULT Speed([out, retval] long*); [id(0x68030000), propput] HRESULT Speed([in, out] long*);

You can see from the _CoCar IDL definition that VB 6.0 always creates dual interfaces, which by definition support the [dual] interface and are derived directly from IDispatch rather than lUnknown. As mentioned in Chapter 2, this core COM interface provides a way for late-bound clients (such as scripting clients) to determine the functionality of a COM class at runtime.

The auxiliary IVBTurbo interface is not directly listed in the coclass statement, however. Rather, an intermediate interface, _VBTurbo, is listed, and it has the following IDL definition:

[ odl, uuid(0FE9EC86-7959-42CA-97B3-61B14214718D), version(1.0), hidden, dual, nonextensible, oleautomation]

interface _IVBTurbo : IDispatch { [id(0x60030000)] HRESULT TurboBlast();

While examining the remaining IDL definitions, also notice that VB has assembled a COM library statement listing each type, as well as a raw dispinterface definition for each COM interface. In the next chapter, I drill much deeper into the world of COM type information. For the time being, simply understand that VB 6.0 manually generates the correct COM metadata.

CODE The Vb6CarServer project is included under the Chapter 3


Making Use of Your COM Servers

To test the functionality of your ATL and VB 6.0 COM servers, you wrap up by creating a new standard EXE VB application. As always, before you can use the COM types created in a separate binary file, you must set references to the COM type information (Figure 3-20).

Rtfrrrtcn - P"rojrct1 . ybp X

Aiilnit Rftr+imn.

V Vteuil Bmk fnifcm« nto^ads and procMhiTM -

lJ Tvpe Library f VtUCBrSifvn

I J VS r^jir i(JM Cimpgnfiit !.fl Tjpt Utirwy D 1*5 FmjtMd L.«Typ* prlS ltt

AQudAwt TUtor-il ^Miirbh intN

X3«Mn pismur ■ ' Aar.-i 04 ri:- iKM[■ *tlim Si 3S "HamrtMtt fimidtr

AflCiiifw l.OJupf ubroiy toawn; t BoflL? liiKrcpteti LattcmpM' S^rtuStrwA

Lnnr]nAi}tf "^lintlAril

Figure 3-20: Setting references to the COM type libraries If you examine the Object Browser (Figure 3-21), you will see that the COM enumeration you defined in your ATL server project has mapped correctly to the Visual Basic language (observe as well that the various [helpstrings] are displayed in the lower pane of the tool).

■m OlijiCl BrdwîOr

[email protected](xi



msmters of hadi otvip e1

o -grctais*


^ fïurbo

(i3 ïm_RAOlO


En uni Ftfl[)K>rYPÉ mmtercl ailcarserverub

Figure 3-21: Viewing IDL COM types The user interface of your VB client is short and sweet. As shown in Figure 3-22, you are simply providing a way for the client to activate the ATL ComCar and VB 6.0 CoCar.

x ComCrti Command Center


Cnwrt &pw<J

Figure 3-22: Another COM client As for the code, things look much like they did when you accessed your RawComCar types in Chapter 2. Here is the complete code behind the VB form:

Option Explicit

Private vbCar As Vb6CarServer.CoCar Private atlCar As ATLCARSE RVERLib.ComCar Private Sub btnATLCoCar_Click() ' Speed up ATL car and crank some tunes. atlCar.SpeedUp 10 atlCar.TurnOnRadio AM_RADIO

Dim itfTurbo As ATLCARSERVERLib.ITurbo Set itfTurbo = atlCar itfTurbo.TurboBlast Set itfTurbo = Nothing End Sub

Private Sub btnUseVb6Car_Click() ' Use [default] interface of VB 6.0 coclass.

vbCar.Speed = vbCar.Speed + 10

' Get IVBTurbo

MsgBox "Turbo boosting", , "Message from Car Command..." Dim itfVbTurbo As IVBTurbo Set itfVbTurbo = vbCar itfVbTurbo.TurboBlast txtCurrVbSpeed.Text = vbCar.Speed Set itfVbTurbo = Nothing End Sub

Private Sub Form_Load() Set vbCar = New Vb6CarServer.CoCar Set atlCar = New ATLCARSERVERLib.ComCar End Sub

Private Sub Form_Unload(Cancel As Integer) ' Explicitly decrement ref counts.

Set vbCar = Nothing Set atlCar = Nothing End Sub

CODE The Vb6CarsClient application is included under the Chapter 3


Excellent! At this point, you have learned the basic process of building COM servers using raw C++, the Active Template Library, and Visual Basic 6.0. Better yet, you now have a total of three COM servers that you make use of through various .NET-aware languages later in this text.


This chapter and the preceding one have guided you through the process of building three COM servers, beginning with the most complex (but most powerful) technique of using raw C++/IDL. The Active Template Library (ATL) attempts to lessen the burden of C++ COM server development by defining a number of base-class templates (and integrated wizards). Visual Basic 6.0 is far and away the least painful approach to COM server development, given that VB hides the low-level COM grunge from view. Now that you have seen the process of building various COM servers, the next (and final) COM-centric chapter drills into the type system of classic COM.

Was this article helpful?

0 0

Post a comment