Implementing IUnknown in C

Implementations of QueryInterface in C are very similar to C++ implementations. There are two basic steps to the implementation:

  1. Validating parameters.
  2. Checking the identifier of the requested interface against the list of interfaces supported by the object and returning either the E_NO_INTERFACE value or a valid interface pointer. If an interface pointer is returned, the implementation should also call the AddRef method to increment the reference count.

The main difference between an implementation of QueryInterface in C and C++ is the additional first parameter in the C version. Because the object pointer is added to the parameter list, a C implementation of QueryInterface must have more parameter validation than a C++ implementation. The logic for checking the interface identifier, incrementing the reference count, and returning an object pointer should be identical in both languages.

The following code sample shows how to implement QueryInterface in C for a status object:

STDMETHODIMP STATUS_QueryInterface(LPMYSTATUSOBJ lpMyObj, REFIID lpiid, 
                                   LPVOID FAR * lppvObj)
{

    HRESULT hr = hrSuccess;

    // Validate object pointer
    if (IsBadReadPtr(lpMyObj, sizeof(MYSTATUSOBJECT))
        || lpMyObj->lpVtbl != &vtblSTATUS )
    {
        hr = ResultFromScode(E_INVALIDARG);
        return hr;

    }

    // Validate other parameters
    if (IsBadReadPtr(lpiid, (UINT) sizeof(IID))
        || IsBadWritePtr(lppvObj, sizeof(LPVOID)))
    {
        hr = ResultFromScode(E_INVALIDARG);
        return hr;
    }

    // Set output pointer to NULL
    *lppvObj = NULL;    

    // Check interface identifier
    if (memcmp(lpiid, &IID_IUnknown, sizeof(IID)) &&
        memcmp(lpiid, &IID_IMAPIProp, sizeof(IID)) &&
        memcmp(lpiid, &IID_IMAPIStatus, sizeof(IID)))
    {
        hr = ResultFromScode(E_NOINTERFACE);
        return hr;
    }

    // Interface is supported. Increment reference count and return
    lpMyObj->lpVtbl->AddRef(lpMyObj);
    *lppvObj = lpMyObj;
    return hr;
}
 

Whereas the implementation of AddRef in C is similar to a C++ implementation, a C implementation of Release can get more elaborate than a C++ version. This is because much of the functionality involved with freeing an object can be incorporated into the C++ constructor and destructor and C has no such mechanism. All of this functionality must be included in the Release method. Also, because of the additional parameter and its explicit vtable, more validation is required.

The following AddRef method call illustrates a typical C implementation for a status object:

STDMETHODIMP_(ULONG) STATUS_AddRef(LPMYSTATUSOBJ lpMyObj)
{
    LONG cRef;

    // Check to see if it has a lpVtbl object member
    if (IsBadReadPtr(lpMyObj, 
            offsetof(MYSTATUSOBJECT, lpVtbl)+sizeof(STATUS_Vtbl *)))
    {
        return 1;
    }

    // Check size of vtable
    if (IsBadReadPtr(lpMyObj->lpVtbl,
         offsetof(STATUS_Vtbl, AddRef)+sizeof(STATUS_AddRef *)))
    {
        return 1;
    }

    // Check method
    if (STATUS_AddRef != lpMyObj->lpVtbl->AddRef)
    {
        return 1;
    }

    InterlockedIncrement(lpMyObj->cRef);
    cRef = ++lpMyObj->cRef;
    InterlockedDecrement (lpMyObj->cRef);

    return cRef;
}
 

A typical implementation of Release for a C status object follows. If after decrementing the reference count, it becomes zero, a C status object implementation should perform the following tasks:

STDMETHODIMP_(ULONG) STATUS_Release(LPMYSTATUSOBJ lpMyObj)
{
    LONG cRef;

    // Check size of vtable
    if (IsBadReadPtr(lpMyObj, sizeof(MYSTATUSOBJ)))
    {
        return 1;
    }

    // Check if correct vtable
    if (lpMyObj->lpVtbl != &vtblSTATUS)
    {
        return 1;
    }

    InterlockedIncrement(lpMyObj->cRef);
    cRef = --lpMyObj->cRef;
    InterlockedIncrement (lpMyObj->cRef);

    if (cRef == 0)
    {
        lpMyObj->lpVtbl->Release(lpMyObj);
        DeleteCriticalSection(&lpMyObj->cs);

        // release IMAPIProp pointer
        lpMyObj->lpProp->Release(lpMyObj->lpProp);
        lpMyObj->lpVtbl = NULL;    
        lpMyObj->lpFreeBuff(lpMyObj);
        return 0;
    }

    return cRef;

}