Products >lcc-win32 > COM-Support

COM Support

Importing a type library

Import type library gives you the means to import third party COM components meant for automation. The components may be written in any language/development environment that can create them and you can embed these in your program.

An example of a COM component that becomes available to you is MSADO.

The dialog looks like this:

To the left you will find all registered typelibraries on your system. If you click one the top right listbox enumerates all version of the dll’s that go with the typelibrary. You can pick the dll and press "select" or double click on the dll to add the COM library to the list of selected libraries.

With the "Browse" button you can manually select a file to import a typelibrary from.

Remove will remove a selected library from the list.

After pressing OK, Wedit will generate a header for every selected library in the dialog. The generated headers will contain all the GUID"s, enums, structures, typedefs, interfaces and unions found in the library. You can then include the header in a file and start using the library following the API docs of that particular library.

Some libraries (like Microsoft Word"s library) need other libraries before the header can work but others (like MSN Messenger) work right out of the box.

Tutorial on importing a type library.

To show how you can start using a third party COM component in lccwin32 we"ll create a project that automates Microsoft xml version 2.0. MSXML should be on most windows systems that have Internet Explorer installed.

Start up Wedit, create a single user console project and call it automation. Add one source file to it and call it main.c. Choose "Project => Import Typelibrary" from the main menu and look for the description "Microsoft XML, version 2.0". A shortcut to get there is to press ‘n" in the left listbox and look a few items up in the list. Select the version 2.0 item and the corresponding dll will be shown in the right listbox. Double-click on the dll or press the select button and it will be marked for importing.

Because we only want this one component, press "Ok" and a header file will be generated from the dll if it"s successful. The header file will be put in the automation project folder.

Now we can start creating the actual program.

Create the following code, later we"ll analyze it.

#include <stdio.h>
#include "msxml.h"

int main(void) {

  HRESULT hr;
  IXMLDOMDocument *doc = NULL;

  CoInitialize(NULL);
  hr = CoCreateInstance(&CLSID_DOMDocument,
                        NULL, CLSCTX_INPROC,
                        &IID_IXMLDOMDocument,
                        (void **)&doc);

  if (SUCCEEDED(hr)) {
    doc->Release();
  }
  else
    printf("Error creating MSXML instance. 0x%x\n", hr);
  CoUninitialize();
  return 0;
}

  

If this is compiled, you will get link errors on CoCreateInstance (among others). To fix that, add ole32.lib to the linker files.

Now, what this program does is creating an instance of an IXMLDOMDocument interface and if it succeeds, it releases it. Nothing very useful so far, but we need the concepts first. The second line includes the generated header and in main I"ve declared a HRESULT and a pointer to the IXMLDOMDocument interface. Almost all functions in COM return a HRESULT indicating success of failure. You can check for success with the SUCCEEDED() and FAILED() macros.

The first thing you should do in any COM application is call CoInitialize(NULL) and at the end you have to call CoUninitialize(). If you forget this, every COM call like CoCreateInstance will fail. I"ve lost count on how many times I forgot to do this, I actually forgot it for this demo application too. CoCreateInstance will create an instance of IXMLDOMDocument. Let"s analyze its parameters.

The first parameter is the class id of the component you want to instantiate. This CLSID is generated by the typelibrary importer and you will find it in msxml.h.

The second parameter goes beyond the scope of this tutorial. You can just pass NULL here.

The third parameter specifies in what context the component will be instantiated. I"ve passed CLSCTX_INPROC_SERVER to indicate that the msxml.dll will be loaded into the same process as automation.exe. This is called an in-process component. If the component resides in another exe you want an out of process component and you have to pass CLSCTX_LOCAL_SERVER. You can also pass CLSCTX_ALL if you want COM to decide where the dll will get loaded.

The fourth parameter is a pointer to IID_IXMLDOMDocument. This interface identifier is also generated by the typelibrary importer and you can find it in msxml.h.

The last parameter is a pointer to a pointer of void *. The cast is not needed in C, but I"ve put it there to show that CoCreateInstance can accept any address of a pointer. If all the parameters are correct and msxml.dll could get loaded the pointer will be filled with the address of an IXMLDOMDocument and you can start calling functions according to its interface.

COM uses a technique called reference counting to decide when a component has to be cleaned up and how many people have pointers to a certain instance. So, for every component you create you should call Release once and assign the pointer a NULL value to indicate that it is no longer a reference to the interface. If you were the last one releasing the pointer the component will get removed from memory in total.

Now we"ll load an XML string from memory and let MSXML retrieve the text from one  the node we have put in. Here"s the complete program.

#include <stdio.h>
#include <wchar.h>>
#include "msxml.h"

int main(void) {
  HRESULT hr;
  IXMLDOMDocument *doc = NULL;

  CoInitialize(NULL);
  hr = CoCreateInstance(&CLSID_DOMDocument,
                        NULL, CLSCTX_INPROC_SERVER,  
                        &IID_IXMLDOMDocument, &doc);

  if (SUCCEEDED(hr)) {
    VARIANT_BOOL ok;
    BSTR bstrXML = SysAllocString(L"<xml><node>COM tutorial</node></xml>");
    if (SUCCEEDED(doc->loadXML(bstrXML, &ok)) && ok == VARIANT_TRUE) {
      IXMLDOMNode *node = NULL;
      BSTR queryString  = SysAllocString(L"/xml/node");
      if (SUCCEEDED(doc->selectSingleNode(queryString, &node))) {
        BSTR text;
        node->get_text(&text);
        fwprintf(stdout, L"%s\n", text);
        SysFreeString(text);
        node->Release();
      }
      SysFreeString(queryString);
    }
    SysFreeString(bstrXML);
    doc->Release();
  }
  CoUninitialize(); 
  return 0;
}

To avoid linker errors on SysAllocString, add oleaut32.lib to the automation project.

After the IXMLDOMDocument is created we create the XML string. COM uses BSTR"s for components that need to be accessed for different languages, so we"ll have to use SysAllocString/SysFreeString and wchar_t in order to make MSXML work.

The line

BSTR bstrXML = SysAllocString(L"<xml><node>COM tutorial</node></xml>");

creates the XML string, watch the L prefix in the string, this makes the string literal use wide characters. I"ve hardcode the string here, but it doesn"t matter where it would"ve come from. Next we call doc->loadXML(bstrXML, &ok) and check if it succeeds. MSXML will only succeed if you pass in a legal XML string. That"s what the "ok" parameter is for.

If it succeeds we create the querystring to select the correct node:

BSTR queryString  = SysAllocString(L"/xml/node");

And then we call doc->selectSingleNode(queryString, &node).

If selectSingleNode succeeds it will fill a pointer to an IXMLDOMNode interface and we can start calling functions on it.

Next, we call node->get_text(&text); and print the text on the console with fwprintf.

You can treat BSTR"s always as if it were const wchar_t * as shown here. Don"t use the standard C string manipulation functions on it, because the COM functions will get confused then. A BSTR has its length prefixed, so if you call wcscpy the length will be incorrect from that point. That"s why you handle a BSTR as a CONST wchar_t *.

If everything goes well, the console will print "COM tutorial" and the program calls SysFreeString and Release for every allocated BSTR and COM pointer.

That's it for the COM component type library importer with MSXML. Now the functionality of all COM components is available to lccwin32.