1. Home
  2. Computing & Technology
  3. Delphi Programming
An Introduction to COM Programming with Delphi (4 / 6)
Page 2: Our first true COM Object program
 More of this Feature
• Page 1: A Com Object walk-a-bout. A Class Factory tour.
• Page 3: Homework Assignment
 More Delphi COM Lessons
• TOC
• Lesson 1
• Lesson 2
• Lesson 3
• Lesson 5
• Lesson 6
 Join the Discussion
"Post your views, comments, questions and doubts to this article."
Discuss!
 Related Resources
• COM / OLE / ActiveX programming with Delphi
 Download Source Code
• COMObject Source

All of this hard earned COM knowledge will finally be put to use...

Our first true COM Object program

Let us write our first In-Process Com Object Server. Mind you, this is really just a hack to show a step-by-step process. I really don’t think this code could possibly be any smaller.

STEP 1
The first thing we need to do is open Delphi. Close all projects that may be open and select New | ActiveX | ActiveX Library from the menu bar. Next, you need to select New | ActiveX | Com Object. A window like the one shown in example 2.0 will appear. Type in the same information as I have. Click OK when finished.

Com Object Wizard in Delphi

STEP 2
Delphi in all of it's glory will generate the COM Object code for you, thus sparing you the annoying task of typing it all in on your own. We are now going to do a little surgery, but first, I want to discuss the GUIDS. One of the key things to making a DLL build a Com Object for you is to pass to the DLL the IID and CLSID. If you don’t recall, these are the definitions.

IID = Interface Identifier (A specific type of GUID)

CLSID = Class Identifier (Another type of GUID) It uniquely identifies your object.

Keep in mind that a GUID is just a GUID. It is the structure that the GUID is attached to that makes it a specific type of GUID. Okay, back to the surgery. Since I just established the fact that the DLL needs both of these GUIDs, you should ask yourself how your client program is supposed to know what these GUIDs are. Well, the answer is simple. You simple include them in your uses clause. This is what is happening when you include the ActiveX.Pas file in your uses clause. You should open it up to explore it. In this unit, you will find a myriad of IID’s and CLSID’s. This makes it so you have access to both the class and the interfaces that can be used to reference it.

That said, create a new unit called Globals.Pas and move the following line of code from your unit that contains the Com object into your Globals.Pas unit. Then add Globals to the uses clause in the Com Object Unit.

const
  //move this to Globals.
  Class_DisplaySomething: TGUID = 
       '{8576CE02-E24A-11D4-BDE0-00A024BAF736}';  

STEP 3
Next, we need to build the IDisplaySomething interface. This will be done in the Globals.Pas unit as well since this interface will need to be shared with the client application. Below is the full listing of how your DSGlobals should look.

unit DSGlobals;

interface

const
  Class_DisplaySomething: TGUID = 
      '{8576CE02-E24A-11D4-BDE0-00A024BAF736}';
  IID_IDisplaySomething : TGUID = 
      '{8576CE04-E24A-11D4-BDE0-00A024BAF736}';

type
  IDisplaySomething = interface
  ['{8576CE04-E24A-11D4-BDE0-00A024BAF736}']
    procedure DisplayMessage(S : string);
  end;

implementation

end.

An easy trick I did was build the interface first, give it a GUID, and then make the IID_IDisplaySomething constant afterwards because then I can just grab the GUID from the interface without the [ ] as opposed to adding them.

STEP 4
Now you need to provide implementation for our procedure DisplaySomething(S : string);. This will be implemented in the Com Object unit where we have TDisplaySomething defined. Below is the full code for your Com Object unit. After you have implemented it, build your project. It will create the DLL in the projects current directory. You have just created your first in-process COM server!

unit DLL_DisplaySomethingUnit;

interface

uses
  Windows, ActiveX, ComObj,
  DSGlobals, Dialogs;

type
  TDisplaySomething = class(TComObject, IDisplaySomething)
  protected
    procedure DisplayMessage(S : string);
  end;

implementation
uses ComServ;

{ TDisplaySomething }

procedure TDisplaySomething.DisplayMessage(S: string);
begin
  ShowMessage(S);
end;

initialization
  TComObjectFactory.Create(
             ComServer, 
             TDisplaySomething, 
             Class_DisplaySomething,
             'DisplaySomething', 
             'A COM Object!', 
             ciMultiInstance, 
             tmApartment);
end.

STEP 5
We will now need to register our DLL with the Windows OS. In Delphi, select Project | Register ActiveX Server. Delphi will proceed to build the DLL for you and register it in windows. If the process is successful, you will see the message displayed: "Successfully Registered ActiveX Server ...". For fun, you should open Regedit and look at the really cool in-process com server we just created. Since we know the GUID, we can cut and paste it into the Windows Registry Find entry to locate it.

STEP 6
Let us now build the client and have it grab our newly created Com Object. Create a new application and add DSGlobals, ComObj, and ActiveX to your uses clause. Put a single button in called AButton on your form and double click it. Add the following code into the OnClick event:

var
  FMyComObject : IDisplaySomething;
begin
 OleCheck(CoCreateInstance(
      Class_DisplaySomething,
      nil,
      CLSCTX_ALL,
      IDisplaySomething,
      FMyComObject));
                          
 FMyComObject.DisplayMessage
              ('I have succeeded!');
end;

STEP 7
Build your program and click the button on the form. If you see the message, “I have succeeded”, then you have just successfully programmed your first Com Object Server and Client.

Download this example COM object.

Important Details
Now that we have created a COM server, we will now need to study some important characteristics within the code.

TCOMObjectFactory The only thing you need to know about this object is that it will fire up your DLL and pass an interface back to a client program. Delphi automatically creates the call to TComObjectFactory.Create for you. If you desperately want to know a little bit about the Create method, here is some information.

TComObjectFactory.Create(
  //Manages your object. 
  ComServer,              
  //The implementation of your interface.
  TDisplaySomething,      
  //The CLSID of your com object.
  Class_DisplaySomething, 
  //The name of your class.
  'DisplaySomething',     
  //A description of your class.
  'A COM Object!',        
  //Determines if multiple clients can access this.
  ciMultiInstance,        
  //The chosen threading model.
  tmApartment);            

ActiveX and ComObj The ActiveX.pas unit contains the majority of COM declarations. They key classes that make Com programming possible in the VCL are defined in ComObj.pas. This unit contains classes like TComObject, TTypedComObject, and TAutoObject.

CoCreateInstance The CoCreateInstance function tells COM to retrieve your Com Object from your server

OleCheck(CoCreateInstance(
 //CLSID of your Com Object
 Class_DisplaySomething,
 //Used in aggregation. I wont' discuss 
 //aggregation so set to nil.
 nil,                    
 //This is the type of server it is accessing 
 //the com object from. _All will find it
 CLSCTX_ALL,             
 //The interface for your Com Object.
 IDisplaySomething,                    
 //Gets a pointer to your Com Object and 
 //puts it in this interface variable
 FMyComObject));                     .

OleCheck You may be wondering why I called the CoCreateInstance from within the OleCheck function. The reason is quite simple. Most OLE functions return an HResult variable. You will always want to check this result since failure to do so can have exceedingly tragic consequences. It is easier to use the OleCheck function than it is to hardcode source that tests each result value that the function could return.

One of the great things about OleCheck is that it will take an error and then convert it to a string error that is written in almost layman terms. (Well, what we coders call layman terms!) A simple rule to remember is: Make sure you always use OleCheck when making OLE function calls!

Use these types when passing information to and from COM Objects
The following types are valid in a type library. The Automation column lists types that can be used by an interface that has its Automation or DispInterface check marked

Delphi typeIDL typeVariant typeAutomationDescription
SmallintshortVT_I2Yes2-byte signed int
IntegerlongVT_I4Yes4-byte signed integer
SinglesingleVT_R4Yes4-byte real
DoubledoubleVT_R8Yes8-byte real
CurrencyCURRENCYVT_CYYescurrency
TDateTimeDATEVT_DATEYesdate
WideStringBSTRVT_BSTRYesbinary string
IDispatchIDispatchVT_DISPATCHYespointer to IDispatch
SCODESCODEVT_ERRORYesOle Error Code
WordBoolVARIANT BOOLVT_BOOLYestrue=-1 false=0
OleVariantVARIANTVT_VARIANTYesOle Variant
IUnknownIUnknownVT_UNKNOWNYespointer to IUnknown
ShortIntbyteVT_I1No1 byte signed integer
Byteunsigned charVT_UI1Yes1 byte unsigned integer
Wordunsigned shortVT_UI2No*2 byte unsigned integer
UNITunsigned longVT_UI4No*4 byte unsigned integer
Int64__int64VT_I8No8 byte signed real
LargeUIntuint64VT_UI8No8 byte unsigned real
SysIntintVT_INTNo*system dependant integer
SysUIntunsigned intVT_UINTNo*system dependant unsigned integer
HResultHResultVT_HRESULTNo32 bit error code
Pointerunsigned intVT_VOIDNountyped pointer
SafeArraySAFEARRAYVT_SAFEARRAYNoOLE Safe Array
PCharLPSTRVT_LPSTRNoa pointer to char
PWideCharLPWSTRVT_LPSTRNoa pointer to widechar
*) The types Word, UINT, SYSINT, and SYSUINT may allow automation with certain applications.

Note: Byte (VT_UI1) is Automation-compatible. However, it is not allowed in a Variant or OleVariant since some Automation servers cannot handle the value properly.

Next page > Homework Assignment > Page 1, 2, 3

An Introduction to COM Programming with Delphi: Table of Content
<< Previous Lesson (3): What is the implements directive? What is the Method Resolution Clauses? Pseudo-Multiple Interface Inheritance. Interface properties and other fine tales of horror.
>> Next Lesson (5): Marshaling Data. Behold the power of Variant Arrays. Using Variants and Variant Arrays.

 

Explore Delphi Programming
About.com Special Features

Holiday Central

What to eat, where to go, fun things to do and how to save money on the perfect gifts. More >

Family Tech Center

Stay connected and entertained with reviews on tips on the latest HDTVs, cellphones and more. More >

  1. Home
  2. Computing & Technology
  3. Delphi Programming

©2009 About.com, a part of The New York Times Company.

All rights reserved.