Using COM

Top  Previous  Next

The Component Object Model (COM) is a platform-independent, distributed, object-oriented, system for creating binary software components that can interact. COM is the foundation technology for OLE, ActiveX, as well as others.

In laymen's terms a COM object represents a collection of subroutines that can be created at will. Kind of like a DLL. The heart of COM is the interface which describes to the compiler what subroutines are available in a particular object.

Emergence BASIC supports COM at a basic level with functions for describing interfaces, calling members of objects, and dealing with GUID's (Globally Unique IDentifiers ). This is a topic for advanced programmers and there are a great many books and online texts dedicated to interfacing with objects which will cover it in greater detail.

 

The interface

At the heart of COM is the interface. The interface defines what functions, known as methods, are callable by your program. Similar to declaring  subroutines, a method definition gives the name of the function and its parameters. To begin definition of the interface use the INTERFACE keyword and end the definition with the ENDINTERFACE keyword. The only statements allowed between the INTERFACE/ENDINTERFACE block are STDMETHOD statements and source comments.

The INTERFACE command expects one parameter, the identifier of the interface. For example suppose we want to access the Windows interface for creating shortcuts, also known as links. The interface is known as IShellLink and there are both ASCII and UNICODE versions for your use. We will define the ASCII version for brevity.
 

INTERFACE IShellLinkA

After the INTERFACE statements comes the method declaration. This is the hard part as most documentation for methods on-line or in books will be for C or C++. And almost all of them omit the first three methods because they are are actually part of a parent interface known as IUnknown.. Namely QueryInterface, AddRef and Release. You can assume they are there but to be sure check the documentation for the COM object you are trying to use.
 

/*** IUnknown methods ***/
STDMETHOD QueryInterface(riid as POINTER, ppvObj as POINTER)
STDMETHOD AddRef( )
STDMETHOD Release( )

With STDMETHOD definitions there is only one return type, the documentation for COM calls it an HRESULT, its really just an INT. It is automatically the standard return type for methods and does not need to be specified. Continuing with the interface definition for IShellLink:
 

/*** IShellLinkA methods ***/
STDMETHOD GetPath(pszFile as STRING,cchMaxPath as INT, pdf as WIN32_FIND_DATAA,fFlags as UINT)
STDMETHOD GetIDList(ppidl as POINTER)
STDMETHOD SetIDList(ppidl as LPCITEMIDLIST)
STDMETHOD SetDescription(pszName as STRING,cchMaxName as INT)
STDMETHOD SetDescription(pszName as STRING)
STDMETHOD GetWorkingDirectory(pszDir as STRING,cchMaxPath as INT)
STDMETHOD SetWorkingDirectory(pszDir as STRING)
STDMETHOD GetArguments(pszArgs as STRING,cchMaxPath as INT)
STDMETHOD SetArguments(pszArgs as STRING)
STDMETHOD GetHotkey(pwHotkey as WORD BYREF)
STDMETHOD SetHotkey(wHotkey as WORD)
STDMETHOD GetShowCmd(piShowCmd as INT BYREF)
STDMETHOD SetShowCmd(iShowCmd as INT)
STDMETHOD GetIconLocation(pszIconPath as STRING,cchIconPath as INT,piIcon as INT BYREF)
STDMETHOD SetIconLocation(pszIconPath as STRING,iIcon as INT)
STDMETHOD SetRelativePath(pszPathRel as STRING,dwReserved as UINT)
STDMETHOD Resolve(hwnd as UINT,fFlags as UINT)
STDMETHOD SetPath(pszFile as STRING)
ENDINTERFACE

An interface may need other data besides the methods. In our example two of the methods require UDT's of WIN32_FIND_DATAA and ITEMIDLIST. The complete interface definition is included with Emergence BASIC in the include directory and can be used in your code by using $INCLUDE "ishelllink.inc" in the source file you wish to use the IShellLink object from.

 

Creating the object

To create and use COM objects your program must first tell Windows that it wants to use COM, and then create the object. The standard command sets includes DECLARE's for the necessary API functions CoInitialize, CoUninitialize and CoCreateInstance. Some interfaces have dedicated API's for creating the object, again consult any necessary documentation. The pointer to the interface, once created, is stored in a special variable type called COMREF. You must define a variable of type COMREF for every interface you wish to access.

For our example these are the steps necessary to initialize COM and create the object:
 

DEF iShell as COMREF
DEF result as INT
CoInitialize(NULL) /* make sure COM is initialized */
'Create the object
result = CoCreateInstance(_CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, _IID_IShellLinkA, iShell)
'Always check the result. If equal to zero then its OK to proceed
IF result = 0
    'use the interface
ENDIF
CoUninitialize( ) /* Tell Windows we are done with COM */

 

Calling methods in the object

Once a valid object is created you can call any method in the object using the COM method operator '->'. First you need to tell the compiler what interface to use with created object. This is how we link objects and interfaces together. Use the SET_INTERFACE command to instruct the compiler what set of methods can be called from a COMREF variable.
 

SET_INTERFACE iShell, IShellLinkA
iShell->SetPath("C:\\Projects\\MyProg.exe")
iShell->SetDescription("Its my program")

Think of the COM method operator as "From this interface -> call this function".

When you are done using an object you must always call its Release method. This deletes the object and frees any memory used by it.
 

iShell->Release( )

Once an object is released you must not attempt to call any of the objects methods without recreating the object first. Doing so will end your program quickly with an Access Violation.

NOTE: As of version 1.06 of the core library you can directly use an interface name when defining a COM object.  This eliminates the need for SET_INTERFACE.  However the command is maintained for backwards compatibility. Example:
 

DEF iShell as IShellLinkA
DEF result as INT
result = CoCreateInstance(_CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, _IID_IShellLinkA, iShell)
'Always check the result. If equal to zero then its OK to proceed
IF result = 0
    iShell->SetPath("C:\\Projects\\MyProg.exe")
    iShell->SetDescription("Its my program")
ENDIF

Interfaces within interfaces

Interfaces can and will contain other interfaces internally. We mentioned the IUnknown methods earlier and the first method in our IShellLinkA interface was not surprisingly QueryInterface. QueryInterfaces gives access to other interfaces that an object might contain. Our IShellLink interface contains once called IPersistFile that allows the object to save itself to disk. In our example the shell link itself is saved to a file to create the shortcut. Similar to CoCreateInstance, QueryInterface takes a GUID and returns a COMREF.
 

DEF ppf as COMREF
result = iShell->QueryInterface(_IID_IPersistFile, ppf)
IF result = 0
    SET_INTERFACE ppf, IPersistFile
    ppf->Save(wsz, TRUE)
    ppf->Release( )
ENDIF

 

GUID usage

A GUID or Globally Unique IDentifier is a special UDT that contains the numeric identifiers needed for accessing COM objects among others. We have been using them all along in our examples without specifically defining them. Emergence BASIC links with the common system GUID's through the UUID.LIB library. All GUID's in this library can be used by defining them as EXTERN and they all start with a leading underscore, which may be different the the official Microsoft documentation. For example:
 

EXTERN _IID_IPersistFile as GUID

You can create your own GUID's by defining a UDT and using the DEFINE_GUID function
 

DEF myGuid as GUID
DEFINE_GUID( myGuid, 0xD7B70EE0,0x4340,0x11CF,0xB0,0x63,0x00,0x20,0xAF,0xC2,0xCD,0x35 )

Many third party COM objects include the GUID definitions in the documentation or include files. The DEFINE_GUID function closely resembles the C macro with the same name to aid in conversions.

See Also: The complete shell link example can be found in the sample file shell_com_example.eba which uses the include file ishelllink.inc mentioned earlier.