Implementations of QueryInterface in C are very similar to C++ implementations. There are two basic steps to the implementation:
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;
}