Click here to Skip to main content
15,860,972 members
Articles / Programming Languages / C#

Windows Development in C++, COM API Clients

Rate me:
Please Sign up or sign in to vote.
4.98/5 (31 votes)
3 Jan 2015CPOL7 min read 62.4K   1.6K   106   13
Using the Facade Pattern to simplify development with COM based APIs

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 its 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 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 need it:

C++
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:

C++
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:

C++
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:

C++
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:

C++
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:

C++
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.

C++
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

Image 1

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:

C++
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:

C++
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:

C++
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.

C++
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.

C++
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.
C++
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:

C++
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.

C++
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:

C++
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:

C++
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:

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

And our wrapper is implemented as follows:

C++
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 occurred. 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

  • 1st November, 2012 - initial posting
  • 15th November, 2012 - Library update - have a look at Windows Development in C++, working with menus[^] for a list of changes made to the library
  • 18th November, 2012 - Library update
  • 21st November, 2012 - Library update
  • 22nd November, 2012 - Library update
  • 30th November, 2012 - Library update
  • 20th August, 2014 - More than a few updates and bug-fixes
  • •3rd January, 2015 - A few new classes, some updates and a number of bug-fixes

License

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


Written By
Architect Sea Surveillance AS
Norway Norway
Chief Architect - Sea Surveillance 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.no

Comments and Discussions

 
GeneralMy vote of 5 Pin
JustWatchLittle 13-Dec-17 5:17
professionalJustWatchLittle 13-Dec-17 5:17 
QuestionReally fine article and code Pin
KarstenK10-Sep-14 9:19
mveKarstenK10-Sep-14 9:19 
AnswerRe: Really fine article and code Pin
Espen Harlinn10-Sep-14 12:19
professionalEspen Harlinn10-Sep-14 12:19 
GeneralRe: Really fine article and code Pin
KarstenK10-Sep-14 22:49
mveKarstenK10-Sep-14 22:49 
GeneralMy vote of 5 Pin
Wendelius4-Nov-12 9:55
mentorWendelius4-Nov-12 9:55 
GeneralRe: My vote of 5 Pin
Espen Harlinn4-Nov-12 10:01
professionalEspen Harlinn4-Nov-12 10:01 
GeneralRe: My vote of 5 Pin
Cristian Amarie13-Apr-15 18:54
Cristian Amarie13-Apr-15 18:54 
QuestionThanks Pin
Nicolas Dorier4-Nov-12 2:40
professionalNicolas Dorier4-Nov-12 2:40 
Coming from the .NET world I always thought that COM was a legacy things...
Now I'm moving into lower layers, I'm amazed about how cool things we can do with it... I never used COM in C++ though, because I was afraid of the syntax... not anymore with your work.

modified 4-Nov-12 8:47am.

AnswerRe: Thanks Pin
Espen Harlinn4-Nov-12 3:13
professionalEspen Harlinn4-Nov-12 3:13 
GeneralRe: Thanks Pin
Nicolas Dorier4-Nov-12 3:52
professionalNicolas Dorier4-Nov-12 3:52 
GeneralRe: Thanks Pin
Espen Harlinn4-Nov-12 3:57
professionalEspen Harlinn4-Nov-12 3:57 
GeneralMy vote of 5 Pin
gndnet1-Nov-12 10:01
gndnet1-Nov-12 10:01 
GeneralRe: My vote of 5 Pin
Espen Harlinn1-Nov-12 11:51
professionalEspen Harlinn1-Nov-12 11:51 

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

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