Introduction to Delphi COM programming

Source: Internet
Author: User
Tags ole

Software reuse is the goal pursued by the industry. People always hope to "Assemble" applications as they build blocks, and component objects act as building blocks. The so-called component object is actually a predefined service or interface that can complete certain functions. The question is, how do these component objects coexist with applications and other component objects and communicate and interact with each other? So what should I do? Standards, allowing these component objects to work in a unified standard manner.
Com is a binary specification and has nothing to do with source code. In this way, even if the COM object is created by different programming languages and runs in different process spaces and different operating system platforms, these objects can communicate with each other. Com is both a standard and an implementation. It provides standard interfaces for accessing core functions of COM objects and a set of API functions in the form of a COM Library (ole32.dll and oleaut32.dll, these API functions are used to create and manage COM objects. In essence, COM is still the client server mode. A customer (usually an application) requests to create a COM Object and manipulate the COM object through the interface of the COM object. The server creates and manages COM Objects Based on Customer requests. The customer and server roles are not absolute.
Component Objects are similar and different from general objects. In general, an object is an instance of data type that encapsulates data and data manipulation methods together, and component objects use interfaces) instead of describing yourself and providing services. The so-called interface is precisely defined as a set of semantic-related functions based on objects. It is actually a pure virtual class, and the interface object is actually implemented ). A com object can have only one interface, such as wndows 95/98 shell extension, and many interfaces. For example, ActiveX Controls generally have multiple interfaces. You can manipulate ActiveX controls in many ways. The interface is the only way for the customer to communicate with the server. If a component object has multiple interfaces, you cannot directly access other interfaces through one interface. However, COM allows customers to call QueryInterface () in the com library to query other interfaces supported by component objects. In this sense, the component object is a bit like the broker of the interface object.
After QueryInterface () is called, if the component object supports the interface to be queried, QueryInterface () returns a pointer to this interface. If the component object does not support this interface, QueryInterface () returns an error message.
Therefore, QueryInterface () is very useful. It can dynamically understand the interfaces supported by component objects. An interface is a reflection of the idea of group-to-object programming. It hides the details of implementing services for COM objects. The COM object can be completely independent of the customers who access it, as long as the interface itself remains unchanged. If you need to update an interface, you can redefine a new interface. for customers who use the old interface, the code is protected to the greatest extent.

Through the wizard, Delphi can quickly and conveniently directly establish code for implementing COM objects, but the entire com implementation process is completely encapsulated, and the structure is not even as clear as VCL.

A Delphi programmer who does not have C ++ com development experience or even has no access to com development can easily design an interface according to the tutorial,
However, I am afraid that it is unclear what the generated code represents and what can be customized. Some basic concepts of COM have been introduced in the previous sections of "COM programming technology under Delphi". I would like to talk about some personal understandings, hope to help those who have doubts about the COM programming in Delphi.

COM (Component Object Model) is a huge system. In short, COM defines a set of APIs and a binary standard to allow communication between independent objects from different platforms and different development languages. COM objects only have methods and properties and contain one or more interfaces. These interfaces implement the COM object function. by calling the registered COM object interface, data can be transferred between different platforms.

Com standards and details can be used to produce several large books. Here, we will not focus on it. We will just explain how Delphi encapsulates and implements COM. For Delphi developers with insufficient com technical experience,
The code generated by Delphi through the template is like drawing an abstract image, but you do not necessarily know what the image is, or how to draw your own things. This article helps you solve such problems.

Explain some concepts again
The article "COM programming technology under Delphi" has already introduced many com concepts, such as guid, CLSID, IID, reference count, and iunknown interfaces. The following are some additional content:

Relationship between COM and DCOM, COM +, Ole, and ActiveX

DCOM (Distributed COM) provides a means to access other machines on the network. It is a network extension of COM and can be created and called remotely. COM + is a technology launched by Microsoft after an important update of COM, but it is not equivalent to a com upgrade. COM + is backward compatible, however, to some extent, it has different features than COM, such as stateless, transaction control, and security control.

Previously, Ole was used to describe a complete set of technologies based on the com architecture. Now Ole only refers to technologies related to object connection and embedding; activeX is used to describe non-COM Technology Based on COM. Its important content is automation, which allows an application (called automation controller) manipulate another application or library (called

Or expose application elements.

It can be seen that com is related to the above technologies and they are used to enable cross-platform or cross-network development tools for objects.

Interfaces under Delphi

The interface concept in Delphi is similar to the pure virtual class in C ++, and because the Delphi class is a single inheritance mode (C ++ is multi-inheritance ), that is, a class can only have one parent class. To some extent

Multiple inheritance can be implemented. The declaration of an interface class is different from that of a general class declaration. It can be like multi-inheritance. Class Name = Class (Interface Class 1, Interface Class 2... ), And then the declared interface class will overload the virtual method of the inherited class to implement the interface function.

The following are the declarations of iinterface, iunknown, and idispatch. Do you know the connection between these important interfaces? The interfaces of any COM object are ultimately inherited from iunknown, while the Automation Object also includes idispatch. We will see its role in the DCOM section later.

Iinterface = interface
Function QueryInterface (const IID: tguid; out OBJ): hresult; stdcall;
Function _ addref: integer; stdcall;
Function _ release: integer; stdcall;

Iunknown = iinterface;

Idispatch = interface (iunknown)
Function gettypeinfocount (Out count: integer): hresult; stdcall;
Function gettypeinfo (index, localeid: integer; out typeinfo): hresult; stdcall;
Function getidsofnames (const IID: tguid; names: pointer;
Namecount, localeid: integer; DISPIDs: pointer): hresult; stdcall;
Function invoke (dispid: integer; const IID: tguid; localeid: integer;
Flags: word; var Params; varresult, interval info, argerr: pointer): hresult; stdcall;
End: In contrast to the "COM programming technology under Delphi" article, you can understand the definition in iinterface, that is, interface query and reference count, which is also required to access and call an interface.

QueryInterface can get the interface handle, while addref and release are responsible for registering the number of calls.

What is the relationship between COM and interfaces? Com communicates with components, applications, customers, and servers through interfaces. The COM object needs to be registered, while a guid is the unique name of the recognition interface.

If you have created a COM Object, its declaration is similar to txxxx = Class (tcomobject, ixxxx). The preceding is the base class of the COM object, and the declaration of the following interface is: ixxxx = interface (iunknown ). Therefore, iunknown is the ancestor of the COM object interface class in Delphi. At this point, I think you have a preliminary understanding of the origins of the interface class.


Interfaces are the basis for the implementation of COM, and interfaces can be inherited. However, interfaces are not implemented by themselves, and only declarations are supported. So how can we reuse the interface Implementation of COM objects? The answer is aggregation. Aggregation is to create an contained object (an internal object), so that the interface of the internal object is exposed to the external object.

Simply put, after the COM object is registered, you can find and call the interface. But isn't an interface just a definition? It must find the implementation of this definition in some way, that is, the method of "implementation class" of the interface, in this way, the specific operation is finally transferred through the external interface, and the execution result is returned through the interface.

In-process and out-of-Process)

The basis for implementing an in-process interface is a DLL, and an out-of-process interface is built on an application (exe. Generally, the purpose of establishing an external interface is to facilitate debugging (it is very troublesome to track the DLL), and then release the code in the process. Because the execution efficiency in the process is higher than that outside the process. (That is, first create an interface in the process, and then change it to release in the process .)

The COM object is created in the server's process space. If it is an EXE-type server, the server and the client are not in the same process; if it is a DLL-type server, the server and the client are a process. Therefore, memory space can be saved in the process and the instance creation time can be reduced.

Stdcall and safecall

The default method of calling the COM interface generated by Delphi is stdcall rather than the default register. This is to ensure the interface compatibility of compilers in different languages.

The default value for the dual interface is safecall. In addition to the safecall method, it also encapsulates the method to return the hresult value to the caller. The advantage of safecall is that it can capture all exceptions. Even exceptions that are not handled by code in the method can be processed by a coat and returned to the caller through hresult.

Different types such as widestring

The default character parameter or return value in the interface definition will not be a string but a widestring. Widestring is a unicode type that complies with OLE 32-bit in Delphi. It is a character.

, Widestring and string are almost the same. When processing Unicode characters, there will be a big difference. Com is intended for cross-platform use, so it is easy to understand why the widestring type is used for data communication.

In the same way, the integer type will be sysint, int64, smallint, or shortint. these minor changes are to conform to the specification.

Generate basic code through wizard

Create a new project wizard (menu "file-New-Other" or "new items") and select the ActiveX page. Create an ActiveX Library first. After compilation, It is a DLL file (in process ). Create a COM object on the same page.

Then you will see the following wizard, in addition to entering the class name (the interface name will be automatically filled according to the class name), create
Instance mode and threading model options.

Instance mode-> determine how the COM object creates an instance after the client request:

Internal: used internally by the COM object. It does not respond to client requests and can only be created through other methods inside the COM object;

Single Instance: a new program and an independent object instance will be created no matter whether the system has the same COM object;

Mulitple instance: if multiple identical COM objects exist, only one program is created. Instances of multiple COM objects share public code and have their own data space.

Single/mulitple instances have their own advantages. Although mulitple saves memory, it is more time-consuming. That is, the single mode requires more memory resources, while the mulitple mode requires more CPU resources, and the single instance has an average load to respond to requests. This parameter should be considered based on the actual needs of the server.

There are five thread modes:

Single: only single thread, simple processing, minimum throughput;

Apartment: multi-thread com program, single-thread COM Object Processing request;

Free: multiple instances of a COM object can run simultaneously. When throughput is increased, necessary protection is required for COM objects to avoid conflicts between multiple instances;

BOTH: both the aartment and free thread modes are supported.

Neutral: it can only be used under COM +.

Although the efficiency of free and both is improved, it is recommended that you use the default method of Delphi to avoid conflicts (which is not easy to debug.

Type Library Editor)

Suppose we create a class named tsample and an isample interface (), and then create a method named getcominfo using the Type Library Editor (right-click the tree on the right and then pop up

Select New-method or click the button above. Two parameters (valint: integer, valstr: string) are created on the left parameters page. The return value is BSTR.

In addition to common types, parameters and return values also support many pointer, OLE object, and interface types. Create a common COM object, and its returen type can be arbitrary,

This is a difference from DCOM.

Double-click the modifier column. In the dialog box that appears, you can select the const and out definitions for the in and out parameters, and select has default value to set the default values.

Detailed explanation of Delphi Generation Code

After you click the refresh button to refresh, the code automatically generated by Delphi corresponding to the above Type Library editor is as follows:

Unit ucom;

{$ Warn symbol_platform off}
Windows, ActiveX, classes, comobj, pcom_tlb, stdvcl;

Tsample = Class (ttypedcomobject, isample)
Function getcominfo (valint: sysint; const valstr: widestring): widestring; stdcall;


Uses comserv;

Function tsample. getcominfo (valint: sysint; const valstr: widestring): widestring;


Ttypedcomobjectfactory. Create (comserver, tsample, class_sample, cimultiinstance, tmapartment );

End. Reference Unit

Three special units are referenced: comobj, comserv, and pcom_tlb. Comobj defines the parent class ttypedcomobject and

Class factory class ttypedcomobjectfactory (inherited from tcomobject and tcomobjectfactory [added typed], early versions such as the com established by delphi4 directly inherited from tcomobject and used tcomobjectfactory ); the comserv Unit defines the global variable comserver: tcomserver [actually has this], which is inherited from tcomserverobject. The role of this variable will be mentioned later.

These classes are relatively basic classes for implementing COM objects in Delphi. tcomobject (COM object class) and tcomobjectfactory (COM Object Class factory class) are themselves iunknown

Two implementation classes include code for creating, querying, registering, and registering a COM object. Tcomserverobject is used to register the service information of a COM object.

Interface Definition

Let's look at the interface class definition tsample = Class (ttypedcomobject, isample ). Here, we can roughly guess how tsample is created and noted through the role of the parent class involved.

This is a standard COM object. How does the interface isample come from? The pcom_tlb unit is automatically created by the system. Its name is added with _ TLB, which contains isample

= Interface (iunknown) interface definition. As mentioned above, all com interfaces are inherited from iunknown.

In this unit, we can also see the definitions of the three types of IDS (CLSID required for Type Library ID, IID, and COM Registration): libid_pcom, iid_isample, and class_sample. The key is

At this time, the interface itself only defines code without any implementation code. Where is the interface creation executed? _ TLB contains the following code:

Cosample = Class
Class function create: isample;
Class function createremote (const machinename: string): isample;

Class function cosample. Create: isample;
Result: = createcomobject (class_sample) as isample;

Class function cosample. createremote (const machinename: string): isample;
Result: = createremotecomobject (machinename, class_sample) as isample;
End; the interface definition code, which is generated by the wizard and Type Editor of Delphi, is bound to a "CO + class name" class, which implements the code for creating an interface instance.

Createcomobject and createremotecomobject
Functions are defined in comobj units. They are functions that use CLSID to create COM/DCOM objects!

Initialization: register the class factory of the COM Object

The class factory is responsible for unified management of interface classes-in fact, it is managed by objects that support iclassfactory interfaces. The inheritance relationship of the class factory class is as follows:

Iclassfactory = interface (iunknown)

Tcomobjectfactory = Class (tobject, iunknown, iclassfactory, iclassfactory2) ttypedcomobjectfactory = Class (tcomobjectfactory)

We know how the interface isample is created, and how the interface implementation class tsample is defined as the implementation class of the COM object. Now explain how it was registered and when it was created.

All these tricks are in the initialization part of the last Delphi unit. Here there is a statement created by the class factory.

Initialization is a special part of Delphi for initialization. The code in this part will be executed first when the entire program is started. Review the previous content and observe the parameters of ttypedcomobjectfactory:

Comserver is the object used to register/unregister COM services. tsample is the interface implementation class, class_sample is the guid corresponding to the interface, cimultiinstance is the instance mode, and tmapartment is the thread mode. The characteristics and elements that a COM object should possess are included !]

How can we manage COM objects? In the comobj unit, you can see a defined function comclassmanager: tcomclassmanager;

Here tcomclassmanager is the management class of COM objects. When an object whose ancestor class is tcomobjectfactory is created, the following statement is executed in the "Create" statement:

Comclassmanager. addobjectfactory (Self );

The original form of the addobjectfactory method is procedure tcomclassmanager. addobjectfactory (Factory: tcomobjectfactory ).

Removeobjectfactory method. I will not post the specific code. I believe you have guessed its role-Add the current object (Self) to the ffactorylist managed by comclassmanager.

Encapsulation secrets

The reader should have the last question: if the server determines a COM object through the registration of the class factory and guid, how does the Server start a program containing the COM object when the client calls it?

When you create an ActiveX library project, you will find a difference from a common DLL template-It defines four Output Routines:






These four routines are not compiled by us. They are all implemented in the comserv unit examples. The Unit also defines the class tcomserver and creates the class instance in the initialization section, that is, the global variable comserver mentioned above.

Routine dllgetclassobject obtains the object supporting the iclassfactory interface through CLSID; routine dllcanunloadnow determines whether the DLL can be detached from the memory; dllregisterserver

And dllunregisterserver are responsible for DLL registration and de-registration. Their specific functions are implemented by comserver.

Implementation of interface classes

Now the ins and outs of the automatically generated code have been clearly explained. The next step is to add the implementation code of the interface method. In function tsample. getcominfo

Add the following code. The example I wrote is very simple. It just organizes a string based on the passed parameters and returns it. This proves that the interface is called correctly and the code is executed:

Function tsample. getcominfo (valint: sysint; const valstr: widestring): widestring;
Server1 = 1;
Server2 = 2;
Server3 = 3;
S: string;
S: = 'this is COM server :';
Case valint
Server1: S: = S + 'server1 ';
Server2: S: = S + 'server2 ';
Server3: S: = S + 'server3 ';
S: = S + #13 + #10 + 'execute client is '+ valstr;
Result: = s;
End; register and create COM objects and call Interfaces

Create an application to test the preceding COM. Few code is required. Create an interface instance and execute its method. Of course, we must register com first; otherwise, call

If the interface cannot be found based on CLSID, the report "cannot write entries to the Registry" will be reported ". If the interface definition is inconsistent, "interface not supported" is reported ".

Compile the com project above, select "Run-register ActiveX Server" from the menu, or register the compiled DLL file through the regsvr32.exe program in the system/system32directory under windows. You can use regsvr32 /? . The COM Object outside the process (exe type) is registered after an application is executed.

After the prompt that the DLL is successfully registered, you should be able to correctly execute the following client programs:

Uses comobj, pcom_tlb;

Procedure ttest. button1click (Sender: tobject );
Comsvr: isample;
Retstr: string;
Comsvr: = createcomobject (class_sample) as isample;
If comsvr <> nil then
Retstr: = comsvr. getcominfo (2, 'client 2 ');
Showmessage (retstr );
Comsvr: = nil;
End else
Showmessage ('?? §??? § 2 ??????? Limit 2? 3? § | 1 | ');
End; the final value is returned from an "interface" outside the current program. We do not even know the implementation of this interface! The first contact with COM. After successfully executing this program and the dialog box is displayed,

You may feel a wonderful way of technology, because you only call the "interface" to complete your guesses.

Create a distributed DCOM (automated interface)


In versions earlier than DELPHI6, the origins of all interfaces were iunknown. Later, iinterface was introduced to avoid blurring the interface concept in cross-platform operations.

The steps for generating DCOM using the wizard are almost the same as those for com. The generated code only replaces the parent class of the interface class with tautoobject, and the class factory class with tautoobjectfactory. This is actually not

Because tautoobject and so on are a standard com Plus idispatch interface, tautoobjectfactory is directly inherited from ttypedcomobjectfactory:

Tautoobject = Class (ttypedcomobject, idispatch)

Tautoobjectfactory = Class (ttypedcomobjectfactory)

The Automation server supports dual interfaces and must implement idispatch. Due to the limitation of scope, this article can only be simply put forward. idispatch is an important area in the implementation of DCOM and COM technologies.

No. Open the _ TLB. Pas unit and you can find the definitions of ixxx = interface (idispatch) and ixxx = dispinterface. This is not found in the preceding com example.

Differences during creation

When using the Type Library Editor, there are two different places. First, you must select hresult for return type. Otherwise, an error is prompted to meet the needs of the dual interface. When

After selecting hresult for return type, you will find that the method definition will become procedure rather than the expected function ).

How can we make the method return values? You also need to add another parameter at the end of parameters, and rename the parameter to be the same as the method name, set the parameter type to pointer (if not found)

For a certain type of pointer type, you can directly add * after the type, and BSTR * is the pointer type of BSTR ). Finally, set parameter flags to retval In the modifier column.

Out is automatically selected, and in is canceled.

After refreshing, get the following code. The specific implementation of the add method is as follows:

Tsampleauto = Class (tautoobject, isampleauto)


Function getautoserinfo (valint: sysint; const valstr: widestring): widestring; safecall;


Remote interface call

The createremotecomobject function must be used to call remote interfaces. Other interfaces, such as interface declarations, must be the same as the COM interface calls. Createremotecomobject function Ratio

Createcomobject has one more parameter, that is, the computer name of the server, which is more capable of querying remote calls than COM. The code in the previous "interface definition description" section can be

Depending on the differences between createcomobject and createremotecomobject.

Custom COM Object

An important benefit of an interface is that an interface can be updated without upgrading the client. The client call method is one regardless of the application upgrade or business change.


Since we have figured out how Delphi implements an interface, can we define the interface without using the wizard? In this way, you can use an interface to inherit different interface implementation classes,

To complete different functions. It also facilitates group development, client development, in-process/out-of-process synchronous compilation, and debugging.

Interface Unit: xxx_tlb.pas

The preceding section briefly describes the precautions for interface definition. In addition to being instantiated, an interface has the following differences with a common class: fields cannot be defined in the interface, and all attributes must be read and written

Method implementation. The interface has no constructor or destructor, and all the Members are public. Methods in the interface cannot be defined as virtual, dynamic, abstract, and override.

First, we need to create an interface. The interface definition previously exists only in one place, that is, in the xxx_tlb.pas unit. You can use the Type Library editor to generate such a unit. Or

On the ActiveX page of the new project, select the last icon (Type Library) to open the Type Library editor. Press F12 to view the TLB file (saved as. TLB. No task defined

In addition to a large comment, only Libid (guid of the Type Library) is defined in the TLB file ). If the Type Library editor is disabled, you can use the menu view at any time.

-Type Library.

Create a new interface first (this step has been completed automatically by using the wizard), and then create methods, properties, and so on... The generated TLB file content is roughly the same as that generated by the wizard _ TLB Unit

Same, but only defined, lack of "CO + class name" and other interface creation code.

Observe the code and you will find that the interface is inherited from idispatch. You must change the idispatch here to iunknown. Save the file to get the. TLB file, and what we want is a unit.

(. Pas) file, just to declare the interface, copy the code and save it to a new unit.

Custom CLSID

From the registration and call sections, we can see the important role of clsid. CLSID is a guid (globally unique interface identifier) used to identify an object. GUID is a 16-byte, 128-bit binary

Data. The syntax for Delphi to declare a guid constant is:

Class_xxxxx: tguid = ''' {XXXXXXXX-XXXXX-XXXXXXXX }'''';

Press Ctrl + Shift + G on the editing interface of Delphi to automatically generate the data string after the equal sign. The GUID declaration is not necessarily in the _ TLB unit. It can be declared and referenced anywhere.

Interface Class declaration and implementation

Create an ActiveX Library Project, add the defined TLB unit, and create a new unit. The name of my TLB unit is mydef_tlb.pas, and an interface is defined.

Imyinterface = interface (iunknown), and a method function samplemethod (VAL: smallint): sysint; safecall; now let's look at all the interfaces.

Class declaration and implementation code:

Unit umydefcom;


Uses comobj, comserv, ActiveX, mydef_tlb;

Class_mysvr: tguid = '{1c0e5d5a-b316-44a4-af6c-478363581d43 }';

Tmyiclass = Class (tcomobject, imyinterface)
Procedure initialize; override;
Destructor destroy; override;
Finitval: word;
Function samplemethod (VAL: smallint): sysint; safecall;

Tmysvrfactory = Class (tcomobjectfactory)
Procedure updateregistry (register: Boolean); override;


Procedure tmyiclass. initialize;
Finitval: = 100;

Destructor tmyiclass. Destroy;

Function tmyiclass. samplemethod (VAL: smallint): sysint;
Result: = Val + finitval;

Procedure tmysvrfactory. updateregistry (register: Boolean );
If register then
Createregkey ('myapp \ '+ classname, 'guguidtostring (class_mysvr ));
End else
Deleteregkey ('myapp \ '+ classname );

Tmysvrfactory. Create (comserver, tmyiclass, class_mysvr, 'mysvr ', '', cimultiinstance, tmapartment );

End. class_mysvr is the custom CLSID, tmyiclass is the interface implementation class, And tmysvrfactory is the class factory class.

COM object initialization

Procedure initialize is the interface initialization process, rather than the common create method. After the client creates an interface, it will first execute the code in it, which is the same as create

. During the life cycle of a COM object, it is inevitable that you need to initialize class members or set the initial values of variables. Therefore, you often need to reload this process.

Correspondingly, destructor destroy has the same role as the standard destructor of the class.

Class Factory Registration

In the last part of the Code, if tcomobjectfactory is used for registration, it is exactly the same as what we mentioned above. Here I deliberately inherited the class tmysvrfactory once, and

The updateregistry method is loaded to write additional content to the Registry. This is a small trick. I hope you can find out the Delphi implementation structure of the COM/DCOM object based on the ideas in this article.

You can refer to the opposite. After all, you can control COM objects as you like.


Introduction to Delphi COM programming

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.