Next: The Dynamic Method, Up: Using COM objects with GNAT [Contents][Index]

### 3.1 The Static Method

Step 1 - Create Binding

The first step in using COM objects from GNAT is to create an Ada binding to the interfaces supported by the COM Object and to the needed GUIDs for the object and the interfaces. The author of the object provides this information in source format, or as a Type Library. There are a number of tools that can read Type Libraries and provide the needed information including COMScope (see http://www.gnavi.org) and Microsoft’s OLE/COM object viewer distributed with the Win32 platform development kit.

An aliased CLSID should be created for the GUID of the object to be created and IIDs for each interface.

For example:

    --  CLSID of COM object to create
    --  {45F9F481-787C-11D3-821C-52544C1913DE}

    CLSID_GNATCOMClass : aliased CLSID :=
      (16#45F9F481#, 16#787C#, 16#11D3#,
        (char'Val (16#82#), char'Val (16#1C#),
         char'Val (16#52#), char'Val (16#54#),
         char'Val (16#4C#), char'Val (16#19#),
         char'Val (16#13#), char'Val (16#DE#)));

    --  IID of the IGNATMessage interface
    --  {45F9F482-787C-11D3-821C-52544C1913DE}

    IID_IGNATMessage : aliased IID :=
      (16#45F9F482#, 16#787C#, 16#11D3#,
        (char'Val (16#82#), char'Val (16#1C#),
         char'Val (16#52#), char'Val (16#54#),
         char'Val (16#4C#), char'Val (16#19#),
         char'Val (16#13#), char'Val (16#DE#)));

Then create access methods for the members of the interface you are planning to create and any inherited methods. Note that the first parameter of every function should be a pointer back to the interface itself. This provides the C++ style this pointer to the COM methods being called.

For example:

    --  Inherited methods from IUnknown
    type af_IGNATStat_QueryInterface is access
      function (This   : access IGNATStat;
                riid   : in     REFIID;
                ppvObj : access Win32.PVOID)
      return Win32.Winerror.HRESULT;
    pragma Convention (StdCall, af_IGNATStat_QueryInterface);

    type af_IGNATStat_AddRef is access
      function (This : access IGNATStat)
      return Win32.ULONG;
    pragma Convention (StdCall, af_IGNATStat_AddRef);

    type af_IGNATStat_Release is access
      function (This : access IGNATStat)
      return Win32.ULONG;
    pragma Convention (StdCall, af_IGNATStat_Release);

    --  Methods of IGNATMessage

    type af_IGNATStat_Calls is access
      function (This          : access IGNATStat;
                NumberOfTimes : access Interfaces.C.int)
      return Win32.Winerror.HRESULT;
    pragma Convention (StdCall, af_IGNATStat_Calls);
    --  Return number of times methods of the IGNATMessage interface
    --  were called

Then construct the interface and the table of functions used to access the methods.

For example:

    type IGNATStat is record
          lpVtbl : Pointer_To_IGNATStatVtbl;
    end record;
    pragma Convention (C_Pass_By_Copy, IGNATStat);

    type IGNATStatVtbl is record
          QueryInterface : af_IGNATStat_QueryInterface;
          AddRef         : af_IGNATStat_AddRef;
          Release        : af_IGNATStat_Release;
          Calls          : af_IGNATStat_Calls;
    end record;
    pragma Convention (C_Pass_By_Copy, IGNATStatVtbl);

Step 2 - Initialize COM

Initialize the COM libraries with a call to `CoInitialize`:

    hr : Win32.Winerror.HRESULT;
    hr := CoInitialize (System.Null_Address);

Step 3 - Create Object

Create the object using `CoCreateInstance` that will return and access to the requested interface in the COM object.

For example:

    --  The requested interfaces is IID_IGNATMessage

    hr := CoCreateInstance (CLSID_GNATCOMClass'Unchecked_Access,
                            null,
                            Win32.DWORD (CLSCTX_ALL),
                            IID_IGNATMessage'Unchecked_Access,
                            RetPointer'Unchecked_Access);

    GnatMessage_Ref := To_Pointer_To_IGNATMessage (RetPointer);

Step 4 - QueryInterface

Use methods in the returned interface or request alternate interfaces using `QueryInterface`.

For example:

    --  To call a method
    hr := GnatMessage_Ref.lpvtbl.Beep (GnatMessage_Ref);

    --  To access a different interfaces of the object
    --  in this case IID_IGNATStat

    hr := GnatMessage_Ref.lpvtbl.QueryInterface
           (GnatMessage_Ref,
            IID_IGNATStat'Unchecked_Access,
            RetPointer'Unchecked_Access);

    GnatStat_Ref := To_Pointer_To_IGNATStat (RetPointer);

Step 5 - Release

When any interface is no longer needed a call to `Release` should be made.

For example:

    refcount : Win32.ULONG;
    refcount := GnatStat_Ref.lpvtbl.Release (GnatStat_Ref);

The reference count that is returned can be bogus and should not be relied upon for any purpose.

Step 6 - Unitialize COM

Shutdown the COM libraries with a call to `CoUninitialize`.

    CoUninitialize;

The client code should be compiled with the linker option `-lole32` and `-loleaut32` to include the needed linker libraries. See the GNAT example `com_static.adb` for a complete example of using COM objects.

Next: The Dynamic Method, Up: Using COM objects with GNAT [Contents][Index]