Navigation:  Programming Cookbook > Creating COM Components > Building a COM component >

Write or Acquire IDL

Previous pageReturn to chapter overviewNext page

When writing a new component from scratch, one should start by defining components COM interfaces in IDL. This is a “language independent” description of the interfaces to the component that is essential for binary interoperability.

When implementing a pre-defined COM interface one does not have to design the interface, but the starting point is the same, but:

1.If a type-library is available, move straight on to Interface Generation, you can reverse engineer the IDL from the type library should you need it.
2.If IDL is available then go to the next stage and build a type library from it.
3.If IDL is not available, then the next best thing is a C header file that has clearly been generated from IDL (this will be obvious from the initial comment which will mention MIDL and the name of the source IDL file). Microsoft are sometimes guilty of supplying a header file generated from IDL without the source IDL. From this it should be relatively easy to construct the equivalent IDL, since all of the necessary information will be available in the header file, even if some of it is in comments. Alternatively you could go back to the supplier of the header file and ask for a copy of the source IDL.
4.If a hand coded C header file is available, then you may be able to edit it into acceptable IDL by using a combination of search/replace, edit macros, and some hand editing. IDL is relatively close to C, but requires additional attributes that you will have to add.

Writing IDL (or engineering it from another source) is a tedious and rather detailed task. The detail is needed because binary interoperability requires that one provide even more detail up front than a statically typed language such as C++ might require in its class definitions. The IDL has to include sufficient detail for efficient remote marshalling, such as the direction in which parameters are passed. For certain parameter types, such as variable sized arrays, it is necessary to know how to determine the size of the array at run-time. A good book to help with the more complex issues of IDL, perhaps containing more detail than you will ever need, is Major.

You may have a tool such as VC++ that includes wizards that help with the generation of IDL, even so we would recommend starting with a minimal definition, perhaps just a single method or property. Once a COM component has been released its interface becomes frozen (at least without generating a new GUID and giving some thought to supporting clients programmed against the old interface), but during development you can revise the IDL, rebuild the type-library, and regenerate the interfaces as often as you like. By starting small and revising incrementally you can avoid getting into “analysis paralysis” attempting to design the perfect interface.

We recommend that you stick to the use of Automation compatible types in your IDL to take advantage of type-library marshalling and to support the maximum range of clients. This does restrict the types of parameters quite a lot, for example it excludes C-style array parameters and structures, but much of the time is it is preferable to define an additional object rather than to use complex types, so this can be good discipline.

In order to be complete your IDL should include, as a minimum, a definition of the component interface, a library statement, within the library statement a definition of the coclass listing the interface as its default interface. See Microsoft’s documentation (or a book) for details of the syntax of IDL.

Here is the IDL for our Random Stream sample, which we’ll save down as Random.idl. We borrowed this IDL from elsewhere and modified it to make IRandomStream a dual interface, so it is completely defined already, but we could have started with just the Next method. The rest of the IDL we’d need anyway:

import "oaidl.idl";

import "ocidl.idl";

       

       [

               object,

               uuid(0E2CEA3B-E6C4-11D2-833B-0020AFAB8EFE),

               dual,

               helpstring("IRandomStream Interface"),

               pointer_default(unique),

               nonextensible

       ]

       interface IRandomStream : IDispatch

       {

               [id(1), helpstring("Answer the next random number in the stream")]

                       HRESULT Next([out,retval]long* plNext);

               [propget, id(2), helpstring("Current random seed")]

                       HRESULT Seed([out, retval] long *pVal);

               [propput, id(2), helpstring("Current random seed")]

                       HRESULT Seed([in] long newVal);

               [propget, id(3),

                helpstring("Lower bound of range of random numbers generated")]

                       HRESULT LowerBound([out, retval] long *pVal);

               [propput, id(3),

                helpstring("Lower bound of range of random numbers generated")]

                       HRESULT LowerBound([in] long newVal);

               [propget, id(4),

                helpstring("Upper bound of range of random numbers generated")]

                       HRESULT UpperBound([out, retval] long *pVal);

               [propput, id(4),

                helpstring("Upper bound of range of random numbers generated")]

                       HRESULT UpperBound([in] long newVal);

       };

 

[

       uuid(0E2CEA2F-E6C4-11D2-833B-0020AFAB8EFE),

       version(1.0),

       helpstring("Random 1.0 Type Library")

]

library RANDOMLib

{

       importlib("stdole32.tlb");

       importlib("stdole2.tlb");

 

       interface IRandomStream;

 

       [

               uuid(A1D42F35-E6C0-11D2-833B-0020AFAB8EFE)

       ]

       coclass RandomStream

       {

               [default] interface IRandomStream;

       };

};

The GUIDs allocated for the interface, coclass, and library must all be unique. They can be allocated in Dolphin by evaluating:

GUID newUnique idlString

 

If you modify an interface after publication, you must allocate a new IID for the new interface definition. Similarly for the coclass – any change in its semantics may break old clients, so it must have a new CLSID. The library is unusual in that one does not normally allocate a new LIBID, but instead one increments the version number.