Click here to Skip to main content
Click here to Skip to main content

Windows Development in C++, COM API clients

By , 29 Dec 2012
Rate this:
Please Sign up or sign in to vote.

Harlinn Windows on CodePlex[^]

Introduction

Microsoft Component Object Model, COM, is a mechanism providing binary interoperability between software components, and as far as I know, it’s the most successful standard for interoperability ever conceived.

COM is a fundamental part of Windows and .Net. .Net developers write software that uses COM all the time, even if they’re not consciously aware of this, and most software written for the .Net platform can easily expose it’s functionality to unmanaged code using COM by just selecting the ‘Make assembly COM-Visible’ check box found by clicking the ‘Assembly Information…’ button on the project properties ‘Application’ page.

Over the years Microsoft has added a significant number of APIs’ to Windows based on COM, and for C++ developers using them is sometimes a bit cumbersome. As a developer you’re obviously expected to check for program errors, and most COM based APIs’ return a HRESULT conveying information about whether a call succeeded or not. Normally a negative value indicates that an error occurred, but not every API follows this convention – .Net on the other hand transparently converts errors to into exceptions, improving the whole development experience.

One of the fundamental features of COM is object lifetime management, which is implemented using reference counting, so COM requires C++ developers to explicitly decrement the reference count of an object when they no longer needs it:

void foo()
{
    IWICImagingFactory* pIWICFactory;

    HRESULT hr = CoCreateInstance(CLSID_WICImagingFactory1,
                               NULL,CLSCTX_INPROC_SERVER,
                               IID_IWICImagingFactory,(void**)&pIWICFactory);
    if(SUCCEEDED(hr))
    {
        __try
        {
          // You can now use the object until you
          // decrement the reference count by calling
          // Release() on the requested interface 
        }
        __finally
        {
            pIWICFactory->Release();
        }
    }
}

using Release() which is one of three functions that’s part of all interfaces:

class IUnknown
{
public:
 virtual HRESULT __stdcall QueryInterface(REFIID riid, void **ppvObject) = 0;
 virtual ULONG __stdcall AddRef( void ) = 0;
 virtual ULONG __stdcall Release( void ) = 0;
};

While IUnknown is no longer declared exactly like this, it’s technically a correct definition of the interface, and so is this:

typedef struct IUnknownVtbl
{
  HRESULT ( __stdcall *QueryInterface )( IUnknown * This, REFIID riid, void **ppvObject);
  ULONG ( __stdcall *AddRef )( IUnknown * This );
  ULONG ( __stdcall *Release )( IUnknown * This );
} IUnknownVtbl;

typedef struct tagIUnknown
{
    struct IUnknownVtbl *lpVtbl;
}IUnknown;

Which tells us one fundamental thing about COM interfaces: An interface is just a C++ class consisting of pure virtual functions – which really is just a structure containing a single pointer to a structure containing nothing but function pointers.

When we create a new interface:

class IClassFactory : public IUnknown
{
public:
 virtual HRESULT __stdcall CreateInstance( IUnknown *pUnkOuter, REFIID riid, void **ppvObject) = 0;
 virtual HRESULT __stdcall LockServer( BOOL fLock) = 0;
};

We declare that interface as a class derived from IUnknown, containing only pure virtual functions, which is the same as:

typedef struct IClassFactoryVtbl
{
    HRESULT ( __stdcall *QueryInterface )( IClassFactory * This, REFIID riid, void **ppvObject);
    ULONG ( __stdcall *AddRef )( IClassFactory * This );
    ULONG ( __stdcall *Release )( IClassFactory * This);
    HRESULT ( __stdcall *CreateInstance )( IClassFactory * This,IUnknown *pUnkOuter, 
                                                  REFIID riid, void **ppvObject);
    HRESULT ( __stdcall *LockServer )( IClassFactory * This, BOOL fLock);
} IClassFactoryVtbl;

typedef struct tagIClassFactory
{
    struct IClassFactoryVtbl *lpVtbl;
} IClassFactory;

Note that the order of the function pointers is the same as the order of the pure virtual function declarations for the C++ representation of the interface. Those from IUnknown come first, and then come the function pointers representing the two functions added to the interface by the IClassFactory class.

IUnknown is deceptively simple, but if you stop and think about it, you’ll realize that it’s quite elegant. Addref() and Release() provides the functionality required for lifetime management, while QueryInterface allows us to access all the interfaces implemented for the object:

void foo(IUnknown* pUnknownForWICImagingFactory1)
{
    IWICImagingFactory* pIWICFactory = nullptr;

    HRESULT hr = pUnknownForWICImagingFactory1->QueryInterface(IID_IWICImagingFactory,
                                 reinterpret_cast<void**>(&pIWICFactory));
    if(SUCCEEDED(hr))
    {
        __try
        {
            // call functions that are part of the IWICImagingFactory interface
        }
        __finally
        {
            pIWICFactory->Release();
        }
    }
}

The one really important thing to remember is that you must always call Release() for the interfaces you acquire through QueryInterface, or any other COM API function.

Included with this article is the source code for a library that I’m developing, that makes COM client development in C++ somewhat similar to how .Net developers experience COM development.

The following function opens the Common Item Dialog, letting the user select an image which is then loaded and converted into a DIB section using the Windows Imaging Component.

void DeviceContextExampleForm::OpenImageFile()
{
    auto fileOpenDialog = harlinn::windows::FileOpenDialog::Create();
    if(fileOpenDialog->Show(GetSafeHandle()))
    {
        auto item = fileOpenDialog->GetResult();
        String fileName = item.GetDisplayName(SIGDN_FILESYSPATH);
        bitMap = BitmapHandle::LoadFromFile(fileName);
    }
}

The library contains wrappers for a significant number of Windows API COM interfaces, covering:

  • Direct2D
  • DirectWrite
  • Windows Imaging Component
  • Windows Property System
  • Core COM interfaces

When we use .Net to access a COM based API things are a little easier, as .Net performs this error checking for us, and in case of a failure .Net converts it into an exception that is thrown before control is returned to our code. That’s actually a pretty nifty service provided by .Net, because when we go native, as in this function:

HRESULT DemoApp::ConvertBitmapSource(HWND hWnd, IWICBitmapSource **ppToRenderBitmapSource)
{
    *ppToRenderBitmapSource = NULL;
    HRESULT hr = S_OK;
    RECT rcClient;
    hr = GetClientRect(hWnd, &rcClient) ? S_OK: E_FAIL;
    if (SUCCEEDED(hr))
    {
        IWICBitmapScaler *pScaler = NULL;
        hr = m_pIWICFactory->CreateBitmapScaler(&pScaler);
        if (SUCCEEDED(hr))
        {
            hr = pScaler->Initialize(m_pOriginalBitmapSource, rcClient.right - rcClient.left, 
                rcClient.bottom - rcClient.top, WICBitmapInterpolationModeFant);
        }
        if (SUCCEEDED(hr))
        {
            IWICFormatConverter *pConverter = NULL;
            hr = m_pIWICFactory->CreateFormatConverter(&pConverter);
            if (SUCCEEDED(hr))
            {
                hr = pConverter->Initialize(pScaler,GUID_WICPixelFormat32bppBGR, 
                    WICBitmapDitherTypeNone,NULL, 0.f,WICBitmapPaletteTypeCustom);

                if (SUCCEEDED(hr))
                {
                    hr = pConverter->QueryInterface(IID_PPV_ARGS(ppToRenderBitmapSource));
                }
            }
            SafeRelease(pConverter);
        }
        SafeRelease(pScaler);
    }
    return hr;
}

There are suddenly a lot of things going on that has to do with object lifetime and error handling. Not only does .Net convert errors into exceptions, it also manages the lifetime of our references.

To solve the lifetime part of the problem, it’s common to use smart interface pointer classes, like CComPtr<>, which works as far as lifetime is concerned, but we still have to check the HRESULT, because the smart interface pointer does its magic by implementing T* operator -> () const, providing access to the interface.

I would like to be able to implement it like this:

BitmapSource DemoApp::ConvertBitmapSource(std::shared_ptr<Control> theControl)
{
    RECT rect = theControl->GetClientRect();
    auto scaler = imagingFactory.CreateBitmapScaler();
    scaler.Initialize(originalBitmapSource, rect.right - rect.left, 
                rect.bottom - rect.top, BitmapInterpolationMode::Fant);
    auto converter = imagingFactory.CreateFormatConverter();
    converter.Initialize(scaler,GUID_WICPixelFormat32bppBGR, 0.f,BitmapPaletteType::Custom);
    return converter;
}

Or event better, like this:

BitmapSource DemoApp::ConvertBitmapSource(std::shared_ptr<Control> theControl)
{
    RECT rect = theControl->GetClientRect();
    return originalBitmapSource.
        Scale(rect.right - rect.left, rect.bottom - rect.top).
        Convert(GUID_WICPixelFormat32bppBGR);
}

The above illustrates how I would expect a well-designed C++ API to work, letting me focus on what I want to do with a particular piece of code, not error handling and lifetime management.

Error handling and lifetime management still has to be performed, but behind the scenes, somewhat alike to what .Net developers can take for granted when they work with COM.

The Façade Pattern

The facade pattern is a software design pattern often used with object-oriented programming. The name refers to an architectural façade – it’s what you’re able to see from the outside. A facade is an object that provides a simplified interface to the developer, hiding the complexity of interacting with a larger piece of code, such as a class library. A facade object can:

  • Make a software library easier to use, understand and test, since the facade provides convenient methods for common tasks.
  • Make the library more readable, for the similar reasons.
  • Reduce dependencies of outside code on the inner workings of a library. Since the client code uses the facade, this also adds more flexibility to the development process.
  • Wrap a poorly designed collection of APIs with a single well-designed API.

As the last implementation of ConvertBitmapSource demonstrates, this can radically simplify the development process while at the same time improve the robustness of our application.

Requirements

Since COM interfaces inherit the IUnknown interface allowing us to access all the other interfaces implemented by the underlying object, that’s a significant piece of functionality that we want for our façade objects too.

Unknown unknown = renderTarget;
graphics::RenderTarget rt;
if(unknown.Is<graphics::RenderTarget>())
{
    rt = unknown.As<graphics::RenderTarget>();
}

In the above code a graphics::RenderTarget, renderTarget, is assigned to an Unknown object, unknown. Afterwards the Is<T>() function is used to determine whether unknown is something that can be successfully converted to an object of type graphics::RenderTarget – which is done using the As<T>() function.

Meaning we’re able to perform significant COM related operations in a straight forward manner, without really being concerned with any details related to what actually makes this work.

Implementation

Most of the Unknown class was introduced in Rendering text with Direct2D & DirectWrite[^], but then with a focus on how to properly implement the move constructor and move assignment operator.

class Unknown
{
protected:
    IUnknown* unknown;
public:
    // The mandatory InterfaceType, used by many temple functions.
    // 
    // Every derived class must provide a typedef named InterfaceType  
    // for the interface it wraps. 
    typedef IUnknown InterfaceType; 

    Unknown();
    explicit Unknown(IUnknown* theUnknown, bool addref = false);

    // Constructor used during conversion
    Unknown(REFIID iid, const Unknown& theUnknown, bool throwIfNoInterface = true ); 
    Unknown(const Unknown& other);
    Unknown(Unknown&& other);
    ~Unknown();

    operator bool() const;

    Unknown& operator = (const Unknown& other);
    Unknown& operator = (Unknown&& other);

    Unknown& Reset(IUnknown* other = nullptr, bool addRef = false);
    // The As<T> function
    template<typename T> 
    T As() const;

    // The Is<T> function
    template<typename T> 
    bool Is() const;

    template<typename T> 
    static T CoCreateInstanceFromClassId(const CLSID& clsid, DWORD classContext = CLSCTX_INPROC_SERVER);

    template<typename T>
    static T CoCreateInstanceFromClassId(const String& clsid, DWORD classContext = CLSCTX_INPROC_SERVER);

    template<typename T>
    static T CoCreateInstanceFromProgId(const String& progId, DWORD classContext = CLSCTX_INPROC_SERVER);
};

The constructor used during conversion takes three arguments:

  • REFIID iid – identifying the requested interface
  • const Unknown& theUnknown – the object holding a reference to the object we want to retrieve another interface for.
  • bool throwIfNoInterface – indicates whether the constructor will throw an exception if the requested interface is not available on the object.
Unknown(REFIID iid, const Unknown& theUnknown, bool throwIfNoInterface = true )
    : unknown(nullptr)
{
    if( theUnknown )
    {
        IUnknown* pInterface = nullptr;
        auto hr = theUnknown.unknown->QueryInterface(iid,(void**)&pInterface);
        if(FAILED(hr))
        {
            if((throwIfNoInterface == false)&&(hr == E_NOINTERFACE))
            {
                return;
            }
            CheckHRESULT(hr);
        }
        unknown = pInterface;
    }
}

A derived class will typically provide a similar constructor for use by descendants:

protected:
 BitmapSource (REFIID iid, const Unknown& theUnknown, bool throwIfNoInterface = true );

And one that uses __uuidof(InterfaceType) to get the REFIID identifying the interface for the object under construction.

public:
    BitmapSource (const Unknown& theUnknown, bool throwIfNoInterface = true ) 
        : Base ( __uuidof(InterfaceType), theUnknown, throwIfNoInterface ) 
        { }

Now we have the building blocks required to implement the As<T>() const function:

template<typename T>
T As() const
{
    const Unknown& self = *this;
    T result(self,false);
    return result;
}

which enables conversion from one interface wrapper type to another, as long as they satisfy the requirements we've been through.

The Is<T>() const function uses the same mechanism to query whether an object can be converted to another object type:

template<typename T>
bool Is() const
{
    if(unknown)
    {
        T::InterfaceType* pInterface = nullptr;
        auto hr = unknown->QueryInterface(__uuidof(T::InterfaceType),(void**)&pInterface);
        if(hr == S_OK )
        {
            pInterface->Release();
            return true;
        }
    }
    return false;
}

So far we’ve implemented a simple mechanism that provides important functionality for our COM wrapper facades – making them pretty flexible.

Implementing a wrapper function

The signature of the IEnumUnknown::Clone is typical for a COM API function:

virtual HRESULT STDMETHODCALLTYPE Clone( 
            /* [out] */ __RPC__deref_out_opt IEnumUnknown **ppenum) = 0;

And our wrapper is implemented as follows:

EnumUnknown Clone()
{
    auto pInterface = GetInterface();
    IEnumUnknown* pClone = nullptr;
    auto hr = pInterface->Clone(&pClone);
    if(FAILED(hr))
    {
        CheckHRESULT(hr);
    }
    return EnumUnknown(pClone);
}

The GetInterface() function will return a pointer to the IEnumUnknown interface, or throw an exception if no interface pointer is assigned to the object. We then proceed to call the COM interface and check whether an error occured. CheckHRESULT will throw an exception if hr is < 0.

If everything went well, we construct and return an EnumUnknown object.

Concluding remarks

By implementing facade classes for COM APIs’ we’re able to improve the COM client development experience in a way that makes our applications more maintainable and robust, and the good thing is that it isn’t all that difficult.

If you’ve never used C++ templates, this also demonstrates that you can get a lot of mileage out of very little code – and that it doesn’t have to be complicated to be useful.

History

  • 1. of November 2012 - initial posting
  • 15. of November 2012 - Library update - have a look at Windows Development in C++, working with menus[^] for a list of changes made to the library
  • 18. of November 2012 - Library update
  • 21. of November 2012 - Library update
  • 22. of November 2012 - Library update
  • 30. of November 2012 - Library update

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Espen Harlinn
Architect Goodtech Projects & Services AS
Norway Norway
Principal Architect - Goodtech Projects & Services AS.
 
Specializing in integrated operations and high performance computing solutions.
 
I’ve been fooling around with computers since the early eighties, I’ve even done work on CP/M and MP/M.
 
Wrote my first “real” program on a BBC micro model B based on a series in a magazine at that time. It was fun and I got hooked on this thing called programming ...
 
A few Highlights:
  • High performance application server development
  • Model Driven Architecture and Code generators
  • Real-Time Distributed Solutions
  • C, C++, C#, Java, TSQL, PL/SQL, Delphi, ActionScript, Perl, Rexx
  • Microsoft SQL Server, Oracle RDBMS, IBM DB2, PostGreSQL
  • AMQP, Apache qpid, RabbitMQ, Microsoft Message Queuing, IBM WebSphereMQ, Oracle TuxidoMQ
  • Oracle WebLogic, IBM WebSphere
  • Corba, COM, DCE, WCF
  • AspenTech InfoPlus.21(IP21), OsiSoft PI
 
More information about what I do for a living can be found at: harlinn.com or LinkedIn
 
You can contact me at espen.harlinn@goodtech.no

Comments and Discussions

 
GeneralMy vote of 5 PinmvpMika Wendelius4-Nov-12 9:55 
GeneralRe: My vote of 5 PinmvpEspen Harlinn4-Nov-12 10:01 
QuestionThanks [modified] PinmemberNicolas Dorier4-Nov-12 2:40 
AnswerRe: Thanks PinmvpEspen Harlinn4-Nov-12 3:13 
GeneralRe: Thanks PinmemberNicolas Dorier4-Nov-12 3:52 
GeneralRe: Thanks PinmvpEspen Harlinn4-Nov-12 3:57 
GeneralMy vote of 5 Pinmembergndnet1-Nov-12 10:01 
GeneralRe: My vote of 5 PinmvpEspen Harlinn1-Nov-12 11:51 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140415.2 | Last Updated 29 Dec 2012
Article Copyright 2012 by Espen Harlinn
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid