Click here to Skip to main content
15,888,527 members
Articles / Programming Languages / C++

COM in plain C, Part 5

Rate me:
Please Sign up or sign in to vote.
4.94/5 (35 votes)
21 May 2006CPOL18 min read 120.3K   2.3K   101  
Add a connectable object (source/sink).
// Our IBase sub-object of IMultInterface. We have chosen IBase to be the base object.

#include <windows.h>
#include <objbase.h>
#include <stddef.h>
#include "IMultInterface2.h"





static DWORD		OutstandingObjects;
static DWORD		LockCount;









// Our IBase object ////////////////////////////////////////////////////////////

HRESULT STDMETHODCALLTYPE IBase_QueryInterface(IBase *this, REFIID vTableGuid, void **ppv)
{
	// Because we have an ISub1 sub-object, we must return a pointer to that ISub1
	// sub-object if an app asks for it here. (ie, The app is allowed to call our IBase's
	// QueryInterface to obtain the ISub1 sub-object). And because we embedded that ISub1
	// sub-object right inside of our IMultInterface, we can get it using pointer arithmetic
	if (IsEqualIID(vTableGuid, &IID_ISub1))
		this = (IBase *)((unsigned char *)this + offsetof(IMultInterface, sub1));

	// Because we have an ISub2 sub-object for our IMultInterface, we must return a
	// pointer to that ISub2 sub-object if an app asks for it here. But we didn't
	// embed our ISub2 sub-object right inside of our IMultInterface. Instead,
	// we put a pointer to it in IMultInterface, and we're allocating it here (ie, we
	// allocate it when the app finally asks for it)
	else if (IsEqualIID(vTableGuid, &IID_ISub2))
	{
		register IMultInterface	*myObj;

		// Because our IBase is the base object, "this" is effectively pointing to our
		// IMultInterface too
		myObj = (IMultInterface *)this;

		// If we've already allocated the ISub2, then our IMultInterface->speakerOut
		// member points to it. We just need to return this object
		if (!myObj->sub2 &&

			// We didn't allocate the ISub2 yet. Let's do so now, and save
			// the pointer in our IMultInterface->sub2 member
			!(myObj->sub2 = allocISub2(this)))
		{
			// We had a problem allocating/initializing our ISub2. The only reason
			// that allocISub2() fails is due to a memory error, so we'll return
			// E_OUTOFMEMORY. If there are other possible failures, we should redefine
			// allocISub2() to be passed a pointer to a HRESULT variable, and then
			// return whatever HRESULT is generated
			return(E_OUTOFMEMORY);
		}

		this = (IBase *)(myObj->sub2);
	}

	else if (!IsEqualIID(vTableGuid, &IID_IUnknown) && !IsEqualIID(vTableGuid, &IID_IBase) && !IsEqualIID(vTableGuid, &IID_IMultInterface))
	{
		*ppv = 0;
		return(E_NOINTERFACE);
	}

	*ppv = this;

	this->lpVtbl->AddRef(this);

	return(NOERROR);
}

ULONG STDMETHODCALLTYPE IBase_AddRef(IBase *this)
{
	return(++((IMultInterface *)this)->count);
}

ULONG STDMETHODCALLTYPE IBase_Release(IBase *this)
{
	// NOTE: This count includes all outstanding references not only to this
	// IBase base object, but also all outstanding references to other sub-objects.
	// In other words, we won't be GlobalFree'ing this IMultInterface
	// until all of its sub-objects are also Release'ed
	if (--((IMultInterface *)this)->count == 0)
	{
#ifndef ISub2_NOPERSISTDATA
		if (((IMultInterface *)this)->sub2) GlobalFree(((IMultInterface *)this)->sub2);
#endif
		GlobalFree(this);
		InterlockedDecrement(&OutstandingObjects);
		return(0);
	}
	return(((IMultInterface *)this)->count);
}


static HRESULT STDMETHODCALLTYPE Sum(IBase *this, long value1, long value2, long *sum)
{
	// Add the 2 values and return the sum
	*sum = value1 + value2;
	
	return(NOERROR);
}

static const IBaseVtbl IBase_Vtbl = {IBase_QueryInterface,
IBase_AddRef,
IBase_Release,
Sum};














// The IClassFactory object ///////////////////////////////////////////////////////

static IClassFactory	MyIClassFactoryObj;

static ULONG STDMETHODCALLTYPE classAddRef(IClassFactory *this)
{
	InterlockedIncrement(&OutstandingObjects);
	return(1);
}

static HRESULT STDMETHODCALLTYPE classQueryInterface(IClassFactory *this, REFIID factoryGuid, void **ppv)
{
	if (IsEqualIID(factoryGuid, &IID_IUnknown) || IsEqualIID(factoryGuid, &IID_IClassFactory))
	{
		this->lpVtbl->AddRef(this);
		*ppv = this;
		return(NOERROR);
	}

	*ppv = 0;
	return(E_NOINTERFACE);
}

static ULONG STDMETHODCALLTYPE classRelease(IClassFactory *this)
{
	return(InterlockedDecrement(&OutstandingObjects));
}

static HRESULT STDMETHODCALLTYPE classCreateInstance(IClassFactory *this, IUnknown *punkOuter, REFIID vTableGuid, void **objHandle)
{
	HRESULT							hr;
	register IMultInterface	*thisobj;

	*objHandle = 0;

	if (punkOuter)
		hr = CLASS_E_NOAGGREGATION;
	else
	{
		if (!(thisobj = (IMultInterface *)GlobalAlloc(GMEM_FIXED, sizeof(IMultInterface))))
			hr = E_OUTOFMEMORY;
		else
		{
			thisobj->base.lpVtbl = (IBaseVtbl *)&IBase_Vtbl;

			// Because we've embedded this IMultInterface's ISub1 sub-object
			// directly inside of our IMultInterface, we have that sub-object
			// available right now. So, we may as well initialize it now
			initISub1(thisobj);

			// Zero out the pointer to ISub2 sub-object. It doesn't exist yet
			thisobj->sub2 = 0;

			// We're returning the base (IBase) object to the app, so AddRef() it
			thisobj->count = 1;

			// Fill in the pointer to IMultInterface's base object, and AddRef it
			hr = IBase_Vtbl.QueryInterface((IBase *)thisobj, vTableGuid, objHandle);

			IBase_Vtbl.Release((IBase *)thisobj);

			if (!hr) InterlockedIncrement(&OutstandingObjects);
		}
	}

	return(hr);
}

static HRESULT STDMETHODCALLTYPE classLockServer(IClassFactory *this, BOOL flock)
{
	if (flock) InterlockedIncrement(&LockCount);
	else InterlockedDecrement(&LockCount);

	return(NOERROR);
}

static const IClassFactoryVtbl IClassFactory_Vtbl = {classQueryInterface,
classAddRef,
classRelease,
classCreateInstance,
classLockServer};














// Miscellaneous functions ///////////////////////////////////////////////////////

HRESULT PASCAL DllGetClassObject(REFCLSID objGuid, REFIID factoryGuid, void **factoryHandle)
{
	register HRESULT		hr;

	if (IsEqualCLSID(objGuid, &CLSID_IMultInterface))
		hr = classQueryInterface(&MyIClassFactoryObj, factoryGuid, factoryHandle);
	else
	{
		*factoryHandle = 0;
		hr = CLASS_E_CLASSNOTAVAILABLE;
	}

	return(hr);
}

HRESULT PASCAL DllCanUnloadNow(void)
{
	return((OutstandingObjects | LockCount) ? S_FALSE : S_OK);
}


BOOL WINAPI DllMain(HINSTANCE instance, DWORD fdwReason, LPVOID lpvReserved)
{
	switch (fdwReason)
	{
		case DLL_PROCESS_ATTACH:
		{
			OutstandingObjects = LockCount = 0;

			MyIClassFactoryObj.lpVtbl = (IClassFactoryVtbl *)&IClassFactory_Vtbl;

			DisableThreadLibraryCalls(instance);
		}
	}

	return(1);
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

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


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions