Delphi Examples

ChilkatHOMEAndroid™Classic ASPCC++C#C# WinRTDelphi ActiveXDelphi DLLVisual FoxProJavaMFCObjective-CPerlPHP ActiveXPHP ExtensionPowerShellPythonRubySQL ServerUnicode CUnicode C++Visual Basic 6.0VB.NETVB.NET WinRTVBScript

Delphi Examples

Bounced Mail
Bz2
Character Encoding
CSV
DKIM / DomainKey
Digital Certificates
Digital Signatures
DH Key Exchange
DSA
Email
Email Object
FTP
HTML Conversion
HTTP
IMAP
Encryption
MHT / HTML Email
NTLM
POP3
RSA
S/MIME
SMTP
Socket
Spider
SFTP
SSH
SSH Key
SSH Tunnel
String
Tar
Upload
XML
XMP
Zip Compression

More Examples...
Amazon S3
Byte Array
FileAccess
RSS
Atom
Self-Extractor
Service
PPMD
Deflate
Bzip2
LZW

Type Conversion

 

Article: Understanding COM References in Delphi

Article: Understanding Delphi COM (OLE) Interface References, AddRef and All That

Also see this Chilkat Blog post about calling Free for Delphi objects.

This article is reproduced from Graham Wideman's article at http://www.wideman-one.com/gw/tech/Delphi/dragdrop/comrefs/

IN A NUTSHELL

...Now that we've introduced Delphi's ability to create COM objects, and Delphi's provision of Ixxx COM interface types with automatic reference counting, we can identify some simple rules for using all this.

Never call AddRef or Release! If you declare variables to be of type IUnknown, or ISomeOtherInterface, then Delphi will handle all the reference counting for you.  In other words, Delphi generates hidden calls to AddRef and Release in all the right places. 

Contents

  • This Page
    • Orientation
    • Basics: Interfaces, IUnknown and Reference Counting
    • Delphi COM Reference Types
    • Delphi Rules for COM References
    • Wrinkles
    • Resources and References
  • Other Pages
    • Demo application shows various COM reference scenarios.
      • User guide. ar_bl_03.gif (891 bytes)
      • Release Notes [ none currently for version 1.1]
    • Some comments on related discussions in recent books. ar_bl_03.gif (891 bytes)

Orientation

If you are going to work with COM (also known as OLE) then you need to understand how COM interface references work, and how they are supported in Delphi. This includes the subject of reference counting, the RefCount variable, and the _AddRef and _Release procedures.

There are numerous sections in popular Delphi books on the overall topic, but some of the published explanations of reference counting seem to be ambiguous or wrong. This article and accompanying demo is an attempt to clarify the situation.

Version applicability: This article applies to (and the software has been tested with) Windows NT 4 (SP3) and Windows 95. It uses techniques that have applied certainly since 1995, hence should work with NT 3.5x, and who knows, might even work with NT 5 and Windows 98!. The code is for Delphi 3, I expect it to work in Delphi 4, and it may also work with Delphi 2.

Basics: Interfaces, IUnknown and Reference Counting

In the COM world (as in Java), an "interface" is essentially a set of related functions. A COM object is an object that implements one or more COM interfaces, or in other words offers certain sets of functions. There are many COM interfaces predefined by Windows, and applications can define more. 

In Delphi, you create these objects with a class declaration (like any other Delphi objects) with a little extra syntax to say that you intend this class to be COM-capable, and that it will implement one or more of these interfaces (sets of functions).  You must then of course provide these functions as part of your class, along with any other functions and data you desire. Your object looks and works just like any other Delphi object, but it just "happens" to have functions needed for one (or more) COM interfaces, and you can pass it to Windows API functions that need COM objects.

All COM interfaces are said to "inherit from" the mother of all interfaces, "IUnknown".  This is just another way of saying that all interfaces must include the functions that are in IUnknown. IUnknown contains two main behaviors:

  • Reference counting -- this tracks how many pieces of code are using the object, and takes care of destroying the object when no code is using it any more. Whenever some code creates a reference to a COM object it is supposed to call AddRef on that object, and when it is done it should call Release. AddRef increments the object's internal RefCount counter, and Release decrements it. When Release decrements RefCount from one to zero then Release should destroy the object.
     
  • QueryInterface navigation -- once you have a reference to a COM object you can use IUnknown functions to find out what other interfaces the object supports, and get references to them.  I'll not cover this since this article is about the reference counting topic.

So, if you are going to play with COM objects, you really need to know when to call AddRef and Release, right? Not exactly.

Delphi COM Reference Types

Delphi provides types of variables that are references to COM objects.   These types have names like IUnknown, IDataObject and so forth.  So if you create an object of type TMyCOMObject that implements, say, IDataObject, you can refer to it with variables of type TMyCOMObject, or with variables of type IDataObject. What's the difference in using one versus the other?

  • Variables of type IDataObject:  Reference counting is taken care of for you automatically, but you can only access the functions that the interface provides. Hence variables of this type are really most useful for dealing with objects that are supplied to/from the operating system or other applications, or components implemented in other languages. 
     
  • Variables of type TMyCOMObject: This is just a "regular" Delphi reference, so you can access any member functions or variables that are visible.  Not surprisingly, Delphi does not perform automatic reference counting for you.

Delphi Rules for COM References

Now that we've introduced Delphi's ability to create COM objects, and Delphi's provision of Ixxx COM interface types with automatic reference counting, we can identify some simple rules for using all this.

Never call AddRef or Release! If you declare variables to be of type IUnknown, or ISomeOtherInterface, then Delphi will handle all the reference counting for you.  In other words, Delphi generates hidden calls to AddRef and Release in all the right places.   Examples include:

Situation Automatic action
  • MyIxxxVariable := SomeCOMObject;
  • Release (on object MyIxxxVariable was pointing to, if any).
  • AddRef (on SomeCOMObject)
  • MyIxxxVariable := nil;
  • Release (on object MyIxxxVariable was pointing to)
  • SomeDelphiProc(MyIxxxVariable)
  • For input parameters:
    • on entry: AddRef
    • on exit:  Release
  • MyIxxxVariable goes out of scope (eg, at end of procedure)
  • Release (on object MyIxxxVariable was pointing to)

This is contrary to what you may read elsewhere (for example the Delphi books listed below), but if you are unsure, try the demo application. There are some special wrinkles, see below.

Don't Call Destroy!  The whole idea of COM reference counting is that COM will delete the object when there are no references left.  Now of course, if you created the object, and you know there are no references left (or don't care), then go ahead and Destroy it if you want to. It may be a COM-style object, but unless you actually passed it outside your app, nobody knows about it but your code, so you are free to do with it whatever you like. (And of course, if for some reason you are implementing the Release function, then you will have to call Destroy.  You would rarely be in that position since you can inherit the IUnknown functions from TInterfacedObject or TCOMObject.)

So in the normal case, the object should be left to destroy itself when RefCount decrements to zero.  To put it another way, if you see a need to explicitly call Destroy, that probably indicates that there is some other design problem.

For Auto Destroy, there must have been at least one reference!   If you create an object and only assign it to a Delphi object variable, but never assign it to a COM interface Ixxx type variable, then RefCount will never increment, and never decrement, so Release is never called and in turn never calls Destroy.  Hence to have the object deleted automatically, you must at some point create a COM reference to it -- not a big constraint considering that there's little point creating a COM object if you aren't going to refer to it with a COM reference!

Wrinkles

"Out" Parameters Can't Be "In" Parameters

The following shows the declaration for a function that accepts a COM object input parameter, and returns a COM object through an "out" parameter:

Procedure SomeProc(COMObjectIn: ISomeCOMInterface; out COMObjectOut: ISomeCOMInterface)

Normally you pass an object in via an "in" parameter (no special keyword) and return an object via an "out" parameter... no problem.  However, since the "out" keyword looks like the Delphi "Var" parameter, you might be tempted to try feeding an object into the procedure via an out parameter.

This doesn't work. Immediately on entry, Delphi nils the reference (which would also delete the object if RefCount was 1 on entry).

Note that this is contrary to Delphi Developer's Handbook (1998) page 465.

Calling an API function, passing a COM object out of your application.

When calling a procedure or function and passing a COM object outside your app (eg: to a Windows API function), it's the caller's responsibility to "allocate the resource before handing it over".  This amounts to AddRef-ing before calling, and Release-ing on return. Does Delphi do this for you automatically?  Basically yes, if you do things sensibly. Examples:

Example 1:  This works fine
---------------------------
Var MyIxxxVariable: ISomeCOMInterface;
....
  MyIxxxVariable := TMyCOMObject.Create;  // implicit AddRef 
  SomeAPIFunc(MyIxxxVariable);            // works fine
Example 2:  This doesn't work properly....
--------------------------------------
Var MyDelphiVariable:  TMyCOMObject;
....
  MyDelphiVariable := TMyCOMObject.Create;  // no AddRef 
  SomeAPIFunc(MyDelphiVariable);    // type conversion, but no AddRef!
Example 3:  But this does work properly
---------------------------------------
Var MyDelphiVariable:  TMyCOMObject;
....
  MyDelphiVariable := TMyCOMObject.Create;  // no AddRef 
  SomeAPIFunc(MyDelphiVariable as ISomeCOMInterface);
        // type conversion, and AddRef

Your Code Fetches a COM Object

Since it's the caller's responsibility to allocate the resources it's handing to you, you do not need to call AddRef nor Release when you get an object from outside your application.  The fact that you have been given a reference means that RefCount is already taken care of.  If you "pass that object around" inside your code (assigning the object to other Ixxx-type variables, or passing it to other procedures), then Delphi will, as usual, handle the AddRefs and Releases automatically.

The Final Wrinkle

The final wrinkle is, of course, that I haven't necessarily stumbled across all the wrinkles... I would be very interested to hear of instances where there is a need to call AddRef or Release.

Copyright Status

Please feel free to distribute or publish this article and associated code, provided some note of credit for me remains attached. Permission is also granted to include this article on CDROM collections, again provided credit remains attached. Thanks!


 

© 2000-2013 Chilkat Software, Inc. All Rights Reserved.