The Unknown class wraps the IUnknown interface, and it’s the base class for all the COM interface wrapper classes implemented by the Harlinn.Common.Core and Harlinn.Windows libraries. The Unknown class is also the base class for the generic ComPtr smart pointer template class.

The COM wrapper classes was created to make it simpler to write apps consuming COM interfaces.

The Unknown implementation details, below, provides a detailed description of the Unknown class, but first two example apps. The first implemented using an interface wrapper, the second working directly with the interfaces. and both apps implementing the same functionality.

The following app uses the Dom::Document class, which wraps the IXMLDOMDocument interface from MSXML, to load an xml document into a DOM document:

#include <HCCXML.h>
using namespace Harlinn::Common::Core;
using namespace Harlinn::Common::Core::Xml;

void loadDOM( )
{
    auto document = Dom::Document::Create( );
    
    document.SetAsync( false );
    document.SetValidateOnParse( false );
    document.SetResolveExternals( false );

    auto success = document.Load( L"stocks.xml" );
    if ( success )
    {
        auto xml = document.Xml( );
        wprintf( L"XML DOM loaded from stocks.xml:\n%s\n", xml.c_str() );
    }
    else
    {
        auto parserError = document.ParseError( );
        auto reason = parserError.Reason( );
        wprintf( L"Failed to load DOM from stocks.xml. %s\n", reason.c_str() );
    }
}

int main()
{
    try
    {
        ComInitialize init;
        loadDOM( );
    }
    catch ( const std::exception& exc )
    {
        printf( "Exception: %s", exc.what( ) );
    }
}

That was pretty simple, and the only thing that’s COM specific, is the single statement ComInitialize init;, which ensures that COM is initialized for the process.

The code below is an example from Microsoft, loadDom.cpp, doing the same thing as the above app:

#include <stdio.h>
#include <tchar.h>
#import <msxml6.dll> raw_interfaces_only

// Macro that calls a COM method returning HRESULT value.
#define CHK_HR(stmt)        do { hr=(stmt); if (FAILED(hr)) goto CleanUp; } while(0)

// Macro to verify memory allcation.
#define CHK_ALLOC(p)        do { if (!(p)) { hr = E_OUTOFMEMORY; goto CleanUp; } } while(0)

// Macro that releases a COM object if not NULL.
#define SAFE_RELEASE(p)     do { if ((p)) { (p)->Release(); (p) = NULL; } } while(0)

// Helper function to create a VT_BSTR variant from a null terminated string. 
HRESULT VariantFromString(PCWSTR wszValue, VARIANT &Variant)
{
    HRESULT hr = S_OK;
    BSTR bstr = SysAllocString(wszValue);
    CHK_ALLOC(bstr);
    
    V_VT(&Variant)   = VT_BSTR;
    V_BSTR(&Variant) = bstr;

CleanUp:
    return hr;
}

// Helper function to create a DOM instance. 
HRESULT CreateAndInitDOM(IXMLDOMDocument **ppDoc)
{
    HRESULT hr = CoCreateInstance(__uuidof(MSXML2::DOMDocument60), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(ppDoc));
    if (SUCCEEDED(hr))
    {
        // these methods should not fail so don't inspect result
        (*ppDoc)->put_async(VARIANT_FALSE);  
        (*ppDoc)->put_validateOnParse(VARIANT_FALSE);
        (*ppDoc)->put_resolveExternals(VARIANT_FALSE);
    }
    return hr;
}

void loadDOM()
{
    HRESULT hr = S_OK;
    IXMLDOMDocument *pXMLDom=NULL;
    IXMLDOMParseError *pXMLErr = NULL;

    BSTR bstrXML = NULL;
    BSTR bstrErr = NULL;
    VARIANT_BOOL varStatus;
    VARIANT varFileName;
    VariantInit(&varFileName);
    
    CHK_HR(CreateAndInitDOM(&pXMLDom));    
    
    // XML file name to load
    CHK_HR(VariantFromString(L"stocks.xml", varFileName));
    CHK_HR(pXMLDom->load(varFileName, &varStatus));
    if (varStatus == VARIANT_TRUE)
    {
        CHK_HR(pXMLDom->get_xml(&bstrXML));
        printf("XML DOM loaded from stocks.xml:\n%S\n", bstrXML);
    }
    else
    {
        // Failed to load xml, get last parsing error
        CHK_HR(pXMLDom->get_parseError(&pXMLErr));
        CHK_HR(pXMLErr->get_reason(&bstrErr));
        printf("Failed to load DOM from stocks.xml. %S\n", bstrErr);
    }

CleanUp:
    SAFE_RELEASE(pXMLDom);
    SAFE_RELEASE(pXMLErr);
    SysFreeString(bstrXML);
    SysFreeString(bstrErr);
    VariantClear(&varFileName);
}

int _tmain(int argc, _TCHAR* argv[])
{
    HRESULT hr = CoInitialize(NULL);
    if(SUCCEEDED(hr))
    {
        loadDOM();
        CoUninitialize();
    }

    return 0;

}

That’s a lot of code, mostly dealing with error handling and resource lifetime management.

As demonstrated by the first example, the C++ wrapper classes, and supporting code, removes most the burden imposed on programmers by COM.

Unknown implementation details

The Unknown class wraps a pointer to the IUnknown interface ensuring that the lifetime of the wrapped interfaces are handled properly and efficiently.

unknown_ is the only data member of Unknown and derived classes must not add any additional data members, ensuring that Unknown, and derived classes, are binary compatible with the raw interface pointers they wrap.

class Unknown 
{
protected:
    IUnknown* unknown_;
public:

Unknown, and every derived class, is required to declare InterfaceType as the type of the interface they are implemented for.

    using InterfaceType = IUnknown;

Constructors

The default constructor ensures that the internal pointer to IUnknown is set to nullptr.

    constexpr Unknown( ) noexcept
        : unknown_( nullptr )
    { }

Constructs a new Unknown, taking ownership of the interface pointer held by unknown, a pointer to an IUnknown interface, or an interface derived from IUnknown.

If addref is set to true, this constructor calls AddRef on the interface pointer held by unknown.

    explicit Unknown( IUnknown* unknown, bool addref = false ) noexcept
        : unknown_( unknown )
    {
        if ( addref && ( unknown_ != nullptr ) )
        {
            unknown_->AddRef( );
        }
    }

Initializes the new object to the interface identified by the iid parameter by querying the interface held by unknown for the requested interface.

If throwIfNoInterface is set to false this prevents the constructor from throwing an exception if the requested interface is not supported.

    Unknown( REFIID iid, const Unknown& unknown, bool throwIfNoInterface = true )
        : unknown_( nullptr )
    {
        if ( unknown )
        {
            IUnknown* pInterface = nullptr;
            auto hr = unknown.unknown_->QueryInterface( iid, (void**)&pInterface );
            if ( FAILED( hr ) )
            {
                if ( throwIfNoInterface == false && hr == E_NOINTERFACE )
                {
                    return;
                }
                CheckHRESULT( hr );
            }
            unknown_ = pInterface;
        }
    }

The copy constructor, calls AddRef on the interface pointer held by other.

    Unknown( const Unknown& other ) noexcept
        : unknown_( other.unknown_ )
    {
        if ( unknown_ )
        {
            unknown_->AddRef( );
        }
    }

The move constructor takes ownership of the interface pointer held by other.

    Unknown( Unknown&& other ) noexcept
        : unknown_( other.unknown_ )
    {
        if ( unknown_ )
        {
            other.unknown_ = nullptr;
        }
    }

The destructor calls Release on the wrapped interface.

    ~Unknown( ) noexcept
    {
        IUnknown* tmp = unknown_;
        unknown_ = nullptr;
        if ( tmp )
        {
            tmp->Release( );
        }
    }

Operators and member functions

constexpr operator bool( ) const noexcept

Returns true if this object references an interface.

    constexpr operator bool( ) const noexcept
    {
        return unknown_ != nullptr;
    }
Unknown& operator = ( const Unknown& other ) noexcept

Copy assignment, which does nothing if other holds the same interface pointer as this object; and if not, calls Release on the currently held interface pointer, then copying the interface pointer from other, calling AddRef on the newly assigned interface pointer.

    Unknown& operator = ( const Unknown& other ) noexcept
    {
        if ( unknown_ != other.unknown_ )
        {
            if ( unknown_ )
            {
                unknown_->Release( );
            }
            unknown_ = other.unknown_;
            if ( unknown_ )
            {
                unknown_->AddRef( );
            }
        }
        return *this;
    }
Unknown& operator = ( Unknown&& other ) noexcept

Move assignment exchanges the current interface pointer, with the interface pointer held by other.

    Unknown& operator = ( Unknown&& other ) noexcept
    {
        std::swap( unknown_, other.unknown_ );
        return *this;
    }

void swap( Unknown& other ) noexcept

Exchanges the current interface pointer, with the interface pointer held by other.

    void swap( Unknown& other ) noexcept
    {
        std::swap( unknown_, other.unknown_ );
    }
friend void swap( Unknown& first, Unknown& second ) noexcept

Exchanges the current interface pointers between two Unknown objects.

    friend void swap( Unknown& first, Unknown& second ) noexcept
    {
        first.swap( second );
    }
void ResetPtr( IUnknown* other = nullptr, bool addRef = false ) noexcept

Calls Release on the currently held interface pointer if the interface pointer held by other points to a different interface.

if addRef is true, and other is not nullptr, ResetPtr will call AddRef on the newly assigned interface pointer.

    void ResetPtr( IUnknown* other = nullptr, bool addRef = false ) noexcept
    {
        if ( unknown_ != other )
        {
            if ( unknown_ )
            {
                unknown_->Release( );
            }
            unknown_ = other;
            if ( addRef && ( unknown_ != nullptr ) )
            {
                unknown_->AddRef( );
            }
        }
    }

Unknown& operator = ( nullptr_t )

Assigning nullptr to the object, releases the currently held interface pointer, and sets the interface pointer to nullptr.

    Unknown& operator = ( nullptr_t )
    {
        if ( unknown_ )
        {
            auto tmp = unknown_;
            unknown_ = nullptr;
            tmp->Release( );
        }
        return *this;
    }


IUnknown* Detach( )

Returns the currently held interface pointer, setting the interface pointer of the object to nullptr.

    IUnknown* Detach( )
    {
        auto tmp = unknown_;
        unknown_ = nullptr;
        return tmp;
    }

T As( ) const

Creates an instance of the interface wrapper class T, by querying the interface pointer for the interface type wrapped by T. Returns an instance of T, initialized to the interface wrapped by T, or nullptr if the interface is not supported.

    template<typename T>
        requires std::is_base_of_v<Unknown, T > 
    T As( ) const
    {
        const Unknown& self = *this;
        T result( __uuidof(T::InterfaceType), self, false );
        return result;
    }
bool Is( ) const noexcept

Returns true if the interface can successfully be queried for the interface wrapped by T.

    template<typename T>
        requires std::is_base_of_v<Unknown, T>
    bool Is( ) const noexcept
    {
        if ( unknown_ )
        {
            typename T::InterfaceType* pInterface = nullptr;
            auto hr = unknown_->QueryInterface( __uuidof( typename T::InterfaceType ), (void**)&pInterface );
            if ( hr == S_OK )
            {
                pInterface->Release( );
                return true;
            }
        }
        return false;
    }
ComPtr<T> As( ) const

Creates an instance of the interface smart pointer ComPtr<T> for the interface T, by querying the interface pointer for the interface type T. Returns an instance of the interface smart pointer ComPtr<T> for the interface T, or nullptr if the interface is not supported.

    template<typename T>
        requires std::is_base_of_v<IUnknown, T>
    ComPtr<T> As( ) const
    {
        ComPtr<T> result;

        if ( unknown_ )
        {
            T* pInterface = nullptr;
            auto hr = unknown_->QueryInterface( __uuidof( T ), (void**)&pInterface );
            if ( hr == S_OK )
            {
                result.ResetPtr( pInterface );
            }
        }
        return result;
    }
bool Is( ) const noexcept

This function can be used to detect if an object supports a particular COM interface derived from IUnknown. Returns true if the interface is supported, otherwise false.

If the object does not hold a pointer to a COM interface, this function returns false.

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

T* GetInterfacePointer( ) const noexcept

Retrieves a pointer to the interface wrapped by this object.

    template<typename T = IUnknown>
    T* GetInterfacePointer( ) const noexcept
    {
        return reinterpret_cast<T*>( unknown_ );
    }
bool QueryInterface( REFIID riid, void** itf ) const

Queries a COM object for a pointer to one of its interface; identifying the interface by a reference to its interface identifier (IID).

Returns true if the query succeeded, otherwise false.

    bool QueryInterface( REFIID riid, void** itf ) const
    {
        auto hr = unknown_->QueryInterface( riid, itf );
        if ( hr == S_OK )
        {
            return true;
        }
        else
        {
            if ( hr != E_NOINTERFACE )
            {
                CheckHRESULT( hr, unknown_ );
            }
            *itf = nullptr;
            return false;
        }
    }

bool QueryInterface( T** itf ) const

Queries the interface for an interface pointer of type T.

Returns true if the query succeeded, otherwise false.

    template<typename T>
        requires std::is_base_of_v<IUnknown, T>
    bool QueryInterface( T** itf ) const
    {
        return QueryInterface( __uuidof( T ), reinterpret_cast< void** >( itf ) );
    }

constexpr bool operator == ( const Unknown& other ) const noexcept

Returns true if the interface pointer held by this object is the same as the interface pointer held by the other object.

    constexpr bool operator == ( const Unknown& other ) const noexcept
    {
        return unknown_ == other.unknown_;
    }
constexpr bool operator != ( const Unknown& other ) const noexcept

Returns true if the interface pointer held by this object is not the same as the interface pointer held by the other object.

    constexpr bool operator != ( const Unknown& other ) const noexcept
    {
        return unknown_ != other.unknown_;
    }

constexpr bool operator == ( const IUnknown* other ) const noexcept

Returns true if the interface pointer held by this object is the same as the interface pointer held by other.

    constexpr bool operator == ( const IUnknown* other ) const noexcept
    {
        return unknown_ == other;
    }
constexpr bool operator != ( const IUnknown* other ) const noexcept

Returns true if the interface pointer held by this object is not the same as the interface pointer held by other.

    constexpr bool operator != ( const IUnknown* other ) const noexcept
    {
        return unknown_ != other;
    }

static T CoCreateInstanceFromClassId( const CLSID& clsid, DWORD classContext = CLSCTX_INPROC_SERVER )

Creates and default-initializes a single object of the class associated with a specified CLSID.

    template<typename T = Unknown>
        requires std::is_base_of_v<Unknown, T>
    static T CoCreateInstanceFromClassId( const CLSID& clsid, DWORD classContext = CLSCTX_INPROC_SERVER )
    {
        typename T::InterfaceType* result = nullptr;
        auto hr = CoCreateInstance( clsid, NULL, classContext, __uuidof( typename T::InterfaceType ), (void**)&result );
        CheckHRESULT( hr );
        return T( result );
    }
};