Click here to Skip to main content
15,897,334 members
Articles / Programming Languages / C

COM in plain C, Part 7

Rate me:
Please Sign up or sign in to vote.
5.00/5 (15 votes)
8 Aug 2006CPOL13 min read 93.6K   2K   83  
An ActiveX Script Host with custom COM objects. This allows a script to call C functions in your app.
// C source code to a simple COM object, compiled into an ordinary
// dynamic link library (DLL). This demonstrates adding
// IConnectionPointContainer and IConnectionPoint sub-objects to
// our IExampleEvts2 object for the purpose of letting an app
// supply some callback functions (for IExampleEvts2's extra functions
// to call). We implement IDispatch functions in our callback
// object so a script can supply the callback functions

#include <windows.h>
#include <objbase.h>
#include <activscp.h>
#include <olectl.h>
#include <stddef.h>
#include "IExampleEvts2.h"






static DWORD		OutstandingObjects;
static DWORD		LockCount;

// A pointer to our loaded ITypeInfo for our IExampleEvts2
// object's extra functions. We need only one of these
// so we'll make it static
static ITypeInfo	*MyTypeInfo;








// Our IExampleEvts2 object ////////////////////////////////////////////////////////////

// This is our IExampleEvts2 object. It can "source events" which just
// means that we allow an app to give us some object that has callback
// functions that IExampleEvts2's functions can call for whatever purpose
// we deem. We define a IFeedback2 object that an app can give us
// (containing the callback functions). This IFeedback2's extra functions
// must be exactly as we've defined in IExampleEvts2.h.
//
// Because our IExampleEvts2 sources events, it must have an
// IConnectionPointContainer sub-object. (ie, Our MyRealIExampleEvts2
// has multiple interfaces). The app uses our IConnectionPointContainer
// sub-object to get a IConnectionPoint sub-object that allows him to
// give us his IFeedback2 object. To make this easy, we'll just embed
// our IConnectionPointContainer sub-object right inside of our
// MyRealIExampleEvts2.
//
// We also embed an IConnectionPoint sub-object. We're going to
// support allowing the app only one IFeedback2 object per each of
// our IExampleEvts2 objects.
//
// Because we support only one IFeedback2 per IExampleEvts2, we'll
// store a pointer to the app's IFeedback2 in our IExampleEvts2.
typedef struct {
	IExampleEvts2Vtbl			*lpVtbl;
	DWORD						count;
	IConnectionPointContainer	container;
	IConnectionPoint			point;
	IFeedback2					*feedback;
} MyRealIExampleEvts2;

static HRESULT STDMETHODCALLTYPE QueryInterface(IExampleEvts2 *this, REFIID vTableGuid, void **ppv)
{
	// Because our IExampleEvts2 sources events, we must return an
	// IConnectionPointContainer sub-object if the app asks for one. Because we've
	// embedded our IConnectionPointContainer object inside of our MyRealIExampleEvts2,
	// we can get that sub-object very easily using pointer arithmetic
	if (IsEqualIID(vTableGuid, &IID_IConnectionPointContainer))
		*ppv = ((unsigned char *)this + offsetof(MyRealIExampleEvts2, container));

	else if (IsEqualIID(vTableGuid, &IID_IUnknown) || IsEqualIID(vTableGuid, &IID_IExampleEvts2) || IsEqualIID(vTableGuid, &IID_IDispatch))
		*ppv = this;

	else
	{
		*ppv = 0;
		return(E_NOINTERFACE);
	}

	this->lpVtbl->AddRef(this);

	return(NOERROR);
}

static ULONG STDMETHODCALLTYPE AddRef(IExampleEvts2 *this)
{
	return(++((MyRealIExampleEvts2 *)this)->count);
}

static ULONG STDMETHODCALLTYPE Release(IExampleEvts2 *this)
{
	// Note: This count includes any outstanding IConnectionPoint
	// and IConnectionPointContainer objects that the app is still
	// holding onto. So we don't actually free our IExampleEvts2
	// until all of those are released too
	if (--((MyRealIExampleEvts2 *)this)->count == 0)
	{
		GlobalFree(this);
		InterlockedDecrement(&OutstandingObjects);
		return(0);
	}
	return(((MyRealIExampleEvts2 *)this)->count);
}

// ================== The standard IDispatch functions

// This is just a helper function for the IDispatch functions below
static HRESULT loadMyTypeInfo(void)
{
	register HRESULT	hr;
	LPTYPELIB			pTypeLib;

	// Load our type library and get a ptr to its TYPELIB. Note: This does an
	// implicit pTypeLib->lpVtbl->AddRef(pTypeLib)
	if (!(hr = LoadRegTypeLib(&CLSID_TypeLib, 1, 0, 0, &pTypeLib)))
	{
		// Get Microsoft's generic ITypeInfo, giving it our loaded type library. We only
		// need one of these, and we'll store it in a global Tell Microsoft this is for
		// our IExampleEvts2's VTable, by passing that VTable's GUID
		if (!(hr = pTypeLib->lpVtbl->GetTypeInfoOfGuid(pTypeLib, &IID_IExampleEvts2, &MyTypeInfo)))
		{
			// We no longer need the ptr to the TYPELIB now that we've given it
			// to Microsoft's generic ITypeInfo. Note: The generic ITypeInfo has done
			// a pTypeLib->lpVtbl->AddRef(pTypeLib), so this TYPELIB ain't going away
			// until the generic ITypeInfo does a pTypeLib->lpVtbl->Release too
			pTypeLib->lpVtbl->Release(pTypeLib);

			// Since caller wants us to return our ITypeInfo pointer,
			// we need to increment its reference count. Caller is
			// expected to Release() it when done
			MyTypeInfo->lpVtbl->AddRef(MyTypeInfo);
		}
	}

	return(hr);
}

// IExampleEvts2's GetTypeInfoCount()
static ULONG STDMETHODCALLTYPE GetTypeInfoCount(IExampleEvts2 *this, UINT *pCount)
{
	*pCount = 1;
	return(S_OK);
}

// IExampleEvts2's GetTypeInfo()
static ULONG STDMETHODCALLTYPE GetTypeInfo(IExampleEvts2 *this, UINT itinfo, LCID lcid, ITypeInfo **pTypeInfo)
{
	register HRESULT	hr;

	// Assume an error
	*pTypeInfo = 0;
	
	if (itinfo)
		hr = ResultFromScode(DISP_E_BADINDEX);

	// If our ITypeInfo is already created, just increment its ref count. NOTE: We really should
	// store the LCID of the currently created TYPEINFO and compare it to what the caller wants.
	// If no match, unloaded the currently created TYPEINFO, and create the correct one. But since
	// we support only one language in our IDL file anyway, we'll ignore this
	else if (MyTypeInfo)
	{
		MyTypeInfo->lpVtbl->AddRef(MyTypeInfo);
		hr = 0;
	}
	else
	{
		// Load our type library and get Microsoft's generic ITypeInfo object. NOTE: We really
		// should pass the LCID to match, but since we support only one language in our IDL
		// file anyway, we'll ignore this
		hr = loadMyTypeInfo();
	}

	if (!hr) *pTypeInfo = MyTypeInfo;

	return(hr);
}

// IExampleEvts2's GetIDsOfNames()
static ULONG STDMETHODCALLTYPE GetIDsOfNames(IExampleEvts2 *this, REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgdispid)
{
	if (!MyTypeInfo)
	{
		register HRESULT	hr;

		if ((hr = loadMyTypeInfo())) return(hr);
	}
	
	// Let OLE32.DLL's DispGetIDsOfNames() do all the real work of using our type
	// library to look up the DISPID of the requested function in our object
	return(DispGetIDsOfNames(MyTypeInfo, rgszNames, cNames, rgdispid));
}

// IExampleEvts2's Invoke()
static ULONG STDMETHODCALLTYPE Invoke(IExampleEvts2 *this, DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *params, VARIANT *result, EXCEPINFO *pexcepinfo, UINT *puArgErr)
{
   // We implement only a "default" interface
   if (!IsEqualIID(riid, &IID_NULL))
      return(DISP_E_UNKNOWNINTERFACE);

	// We need our type lib's TYPEINFO (to pass to DispInvoke)
	if (!MyTypeInfo)
	{
		register HRESULT	hr;

		if ((hr = loadMyTypeInfo())) return(hr);
	}

	// Let OLE32.DLL's DispInvoke() do all the real work of calling the appropriate
	// function in our object, and massaging the passed args into the correct format
	return(DispInvoke(this, MyTypeInfo, dispid, wFlags, params, result, pexcepinfo, puArgErr));
}

// ================== Extra functions


// Our IFeedback2 has 5, extra (callback) functions that we can call.
#define MAX_CALLBACK_FUNCS	5

// Initially, DoSomething() calls IFeedback2's first callback function
static DWORD FuncNumber = 0;

// Our extra function for IExampleEvts2. DoSomething() is a totally contrived
// example of using the app's IFeedback2. Every time the app calls DoSomething,
// we'll call another one of IFeedback2's callback functions in turn
static HRESULT STDMETHODCALLTYPE DoSomething(IExampleEvts2 *this)
{
	register IFeedback2	*feedback;

	// Get the app's IFeedback2 object. We stored a pointer to it in
	// our MyRealIExampleEvts2->feedback member. NOTE: If this member is 0,
	// then the app has not yet gotten our IConnectionPoint object,
	// and called its Advise() function to give us the app's IFeedback2.
	// Or perhaps, the app has called our IConnectionPoint->Unadvise() to
	// tell us to Release(), and no longer use, its IFeedback2
	if ((feedback = ((MyRealIExampleEvts2 *)this)->feedback))
	{
		DISPPARAMS	args;

		// Ok, the app has already given us its IFeedback2.

		// Let's just cause a different IFeedback2 function to be called
		// each time the app calls our DoSomething routine. We'll simply
		// increment a global that tells which function to call (and roll
		// it over when necessary)
		++FuncNumber;
		if (FuncNumber > MAX_CALLBACK_FUNCS) FuncNumber = 1;

		// We can't call the IFeedback2's extra functions directly. (ie,
		// We must not assume that its VTable is "dual"). We must instead
		// call its extra functions indirectly via the IFeedback2's
		// Invoke function. In IEXAMPLEEVTS2.IDL, we gave a DISPID of 1 to
		// Callback1, a DISPID of 2 to Callback2, etc. So "FuncNumber" is
		// really our DISPID.
		//
		// NOTE: If we have any args to pass, we would have to put them in
		// an array of a VARIANTs, and store the array pointer in
		// args->rgvarg. And if the callback function is returning a value,
		// we have to pass a VARIANT for that too. But our callbacks don't
		// happen to take any args, nor return any value
		ZeroMemory(&args, sizeof(DISPPARAMS));

		feedback->lpVtbl->Invoke(feedback, FuncNumber, &IID_NULL, 0, DISPATCH_METHOD, &args, 0, 0, 0);
	}

	return(S_OK);
}


static const IExampleEvts2Vtbl IExampleEvts2_Vtbl = {QueryInterface,
AddRef,
Release,
GetTypeInfoCount,
GetTypeInfo,
GetIDsOfNames,
Invoke,
DoSomething};




















// Our IConnectionPointContainer sub-object (for IExampleEvts2) ////////////////////////

static STDMETHODIMP QueryInterface_Connect(IConnectionPointContainer *this, REFIID vTableGuid, void **ppv)
{
	// Because this is a sub-object of our IExampleEvts2 (ie, MyRealIExampleEvts2) object,
	// we delegate to IExampleEvts2's QueryInterface. And because we embedded the
	// IConnectionPointContainer directly inside of MyRealIExampleEvts2, all we need
	// is a little pointer arithmetic to get our IExampleEvts2
	return(QueryInterface((IExampleEvts2 *)((char *)this - offsetof(MyRealIExampleEvts2, container)), vTableGuid, ppv));
}

static STDMETHODIMP_(ULONG) AddRef_Connect(IConnectionPointContainer *this)
{
	// Because we're a sub-object of IExampleEvts2, delegate to its AddRef()
	// in order to increment IExampleEvts2's reference count
	return(AddRef((IExampleEvts2 *)((char *)this - offsetof(MyRealIExampleEvts2, container))));
}

static STDMETHODIMP_(ULONG) Release_Connect(IConnectionPointContainer *this)
{
	// Because we're a sub-object of IExampleEvts2, delegate to its Release()
	// in order to decrement IExampleEvts2's reference count
	return(Release((IExampleEvts2 *)((char *)this - offsetof(MyRealIExampleEvts2, container))));
}

static STDMETHODIMP EnumConnectionPoints(IConnectionPointContainer *this, IEnumConnectionPoints **enumPoints)
{
	// The app had better know the GUIDs of whatever objects our
	// IExampleEvts2 supports for callbacks (ie, an IFeedback2), because
	// we're not going to bother providing him with an object to
	// enumerate the VTable GUIDs of all those supported objects
	*enumPoints = 0;
	return(E_NOTIMPL);
}
 
static STDMETHODIMP FindConnectionPoint(IConnectionPointContainer *this, REFIID vTableGuid, IConnectionPoint **ppv) 
{
	// Is the app asking us to return an IConnectionPoint object it can use
	// to give us its IFeedback2 object? The app asks this by passing us
	// IFeedback2 VTable's GUID (which we defined in IExampleEvts2.h)
	if (IsEqualIID(vTableGuid, &DIID_IFeedback2))
	{
		register MyRealIExampleEvts2		*iExample;

		// The app obviously wants to connect its IFeedback2 object
		// to IExampleEvts2. In order to do that, we need to give the app a
		// standard IConnectionPoint, so the app can call its Advise function
		// to give us its IFeedback2. This is easy to do since we embedded both
		// our IConnectionPointContainer and IConnectionPoint inside of our 
		// IExampleEvts2. All we need is a little pointer arithmetic
		iExample = (MyRealIExampleEvts2 *)((char *)this - offsetof(MyRealIExampleEvts2, container));
		*ppv = &iExample->point;

		// Because we're giving the app a pointer to our IConnectionPoint, and
		// our IConnectionPoint is a sub-object of IExampleEvts2, we need to
		// increment IExampleEvts2's reference count. The easiest way to do this is to call
		// our IConnectionPointContainer's AddRef, because all we do there is delegate
		// to our IExampleEvts2's AddRef
		AddRef_Connect(this);

		return(S_OK);
	}

	// We don't support any other app objects connecting to IExampleEvts2
	// events. All we've defined, and support, is an IFeedback2 object. Tell
	// the app we don't know anything about the GUID he passed to us, and
	// do not give him any IConnectPoint object
	*ppv = 0;
	return(E_NOINTERFACE);
}


static const IConnectionPointContainerVtbl IConnectionPointContainer_Vtbl = {QueryInterface_Connect,
AddRef_Connect,
Release_Connect,
EnumConnectionPoints,
FindConnectionPoint};













// Our IConnectionPoint sub-object (for IExampleEvts2) ////////////////////////////

static STDMETHODIMP QueryInterface_Point(IConnectionPoint *this, REFIID vTableGuid, void **ppv)
{
	// Because this is a sub-object of our IExampleEvts2 (ie, MyRealIExampleEvts2) object,
	// we delegate to IExampleEvts2's QueryInterface. And because we embedded the
	// IConnectionPoint directly inside of MyRealIExampleEvts2, all we need
	// is a little pointer arithmetic to get our IExampleEvts2
	return(QueryInterface((IExampleEvts2 *)((char *)this - offsetof(MyRealIExampleEvts2, point)), vTableGuid, ppv));
}

static STDMETHODIMP_(ULONG) AddRef_Point(IConnectionPoint *this)
{
	// Because we're a sub-object of IExampleEvts2, delegate to its AddRef()
	// in order to increment IExampleEvts2's reference count
	return(AddRef((IExampleEvts2 *)((char *)this - offsetof(MyRealIExampleEvts2, point))));
}

static STDMETHODIMP_(ULONG) Release_Point(IConnectionPoint *this)
{
	// Because we're a sub-object of IExampleEvts2, delegate to its Release()
	// in order to decrement IExampleEvts2's reference count
	return(Release((IExampleEvts2 *)((char *)this - offsetof(MyRealIExampleEvts2, point))));
}

// Called by the app to get our IFeedback2 VTable's GUID (which we defined in IExampleEvts2.h).
// The app would call GetConnectionInterface() if it didn't link with IExampleEvts2.h, and
// therefore doesn't know our IFeedback2 VTable's GUID. The app needs to know this GUID
// because our Advise function below is going to pass this same GUID to some app object's
// QueryInterface. The app's QueryInterface had better recognize this GUID if it intends
// to honor our request to give us its IFeedback2 object
static STDMETHODIMP GetConnectionInterface(IConnectionPoint *this, IID *vTableGuid) 
{
	// Tell the app to recognize our IFeedback2 VTable GUID (defined as
	// DIID_IFeedback in IExampleEvts2.h) when our Advise function calls
	// some app QueryInterface function
	CopyMemory(vTableGuid, &DIID_IFeedback2, sizeof(GUID));
	return(S_OK);
}
 
// Called by the app to get the IConnectionPointContainer sub-object for our
// IExampleEvts2 object.
static STDMETHODIMP GetConnectionPointContainer(IConnectionPoint *this, IConnectionPointContainer **ppv) 
{
	register MyRealIExampleEvts2	*iExample;

	// Get the MyRealIExampleEvts2 that this IConnectionPoint sub-object belongs
	// to. Because this IConnectPoint sub-object is embedded directly inside its
	// MyRealIExampleEvts2, all we need is a little pointer arithmetic
	iExample = (MyRealIExampleEvts2 *)((char *)this - offsetof(MyRealIExampleEvts2, point));

	// Because the IConnectionPointContainer sub-object is also embedded right inside
	// the same MyRealIExampleEvts2, we can get a pointer to it easily as so
	*ppv = &iExample->container;

	// Because we're giving the app a pointer to our IConnectionPointContainer, and
	// our IConnectionPointContainer is a sub-object of IExampleEvts2, we need to
	// increment IExampleEvts2's reference count. The easiest way to do this is to call
	// our IConnectionPoint's AddRef, because all we do there is delegate
	// to our IExampleEvts2's AddRef
	AddRef_Point(this);

	return(S_OK);
}

// Called by the app to give us its IFeedback2 object. Actually, the app doesn't
// just give us its IFeedback2. Rather, the app calls our Advise, passing us some
// app object from which we can request the app to give us its IFeedback2. All of
// this convoluted stuff is a combination of poor pre-planning by Microsoft
// programmers when they designed this stuff, as well as the colossal blunder of
// designing COM to accomodate the limitations of early, primitive editions of
// Visual Basic.
//
// The second arg passed here is some app object whose QueryInterface function
// we call to request the app's IFeedback2. We pass the GUID DIID_IFeedback2 to
// this QueryInterface in order to tell the app to give us its IFeedback2
static STDMETHODIMP Advise(IConnectionPoint *this, IUnknown *obj, DWORD *cookie) 
{
	register HRESULT			hr;
	register MyRealIExampleEvts2	*iExample;

	// Get the MyRealIExampleEvts2 that this IConnectionPoint sub-object belongs
	// to. Because this IConnectPoint sub-object is embedded directly inside its
	// MyRealIExampleEvts2, all we need is a little pointer arithmetic
	iExample = (MyRealIExampleEvts2 *)((char *)this - offsetof(MyRealIExampleEvts2, point));

	// We allow only one IFeedback2 for our IExampleEvts2, so see if the app already
	// called our Advise(), and we got one. If so, let the app know that it is trying
	// to give us more IFeedbacks2 than we allow
	if (iExample->feedback) return(CONNECT_E_ADVISELIMIT);
 
	// Ok, we haven't yet gotten the one IFeedback2 we allow from the app. Get the app's 
	// IFeedback2 object. We do this by calling the QueryInterface function of the
	// app object passed to us. We pass IFeedback2 VTable's GUID (which we defined
	// in IExampleEvts2.h).
	//
	// Save the app's IFeedback2 pointer in our IExampleEvts2 feedback member, so we
	// can get it when we need it
	hr = obj->lpVtbl->QueryInterface(obj, &DIID_IFeedback2, (void **)&iExample->feedback);

	// We need to return (to the app) some value that will clue our Unadvise() function
	// below how to locate this app IFeedback2. The simpliest thing is to just use the
	// app's IFeedback2 pointer as that returned value
	*cookie = (DWORD)iExample->feedback;

	return(hr);
}

// Called by the app to tell us to stop using, and Release(), its IFeedback2 object.
// The second arg passed here is the value our Advise() function above returned when
// we got the IFeedback2 from the app. This value should help us locate wherever we
// stored that IFeedback2 pointer we got in Advise()
static STDMETHODIMP Unadvise(IConnectionPoint *this, DWORD cookie) 
{
	register MyRealIExampleEvts2	*iExample;

	// Get the MyRealIExampleEvts2 that this IConnectionPoint sub-object belongs
	// to. Because this IConnectPoint sub-object is embedded directly inside its
	// MyRealIExampleEvts2, all we need is a little pointer arithmetic
	iExample = (MyRealIExampleEvts2 *)((char *)this - offsetof(MyRealIExampleEvts2, point));

	// Use the passed value to find wherever we stored his IFeedback2 pointer.
	// Well, since we allow only one IFeedback2 for our IExampleEvts2, we already
	// know we stored it in our IExampleEvts2->feedback member. And Advise()
	// returned that pointer as the "cookie" value. So we already got the
	// IFeedback2 right now.
	//		
	// Let's just make sure the cookie he passed is really the pointer we expect
	if (cookie && (IFeedback2 *)cookie == iExample->feedback)
	{
		// Release the app's IFeedback2
		((IFeedback2 *)cookie)->lpVtbl->Release((IFeedback2 *)cookie);

		// We no longer have the app's IFeedback2, so clear the IExampleEvts2
		// feedback member
		iExample->feedback = 0;

		return(S_OK);
	}
	return(CONNECT_E_NOCONNECTION);
}

static STDMETHODIMP EnumConnections(IConnectionPoint *this, IEnumConnections **enumConnects)
{
	*enumConnects = 0; 
	return(E_NOTIMPL);
}


static const IConnectionPointVtbl IConnectionPoint_Vtbl = {
QueryInterface_Point,
AddRef_Point,
Release_Point,
GetConnectionInterface,
GetConnectionPointContainer,
Advise,
Unadvise,
EnumConnections};

















// Our 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 MyRealIExampleEvts2	*thisobj;

	*objHandle = 0;

	if (punkOuter)
		hr = CLASS_E_NOAGGREGATION;
	else
	{
		if (!(thisobj = (MyRealIExampleEvts2 *)GlobalAlloc(GMEM_FIXED, sizeof(MyRealIExampleEvts2))))
			hr = E_OUTOFMEMORY;
		else
		{
			thisobj->lpVtbl = (IExampleEvts2Vtbl *)&IExampleEvts2_Vtbl;
			thisobj->count = 1;

			// Our MyRealIExampleEvts2 is a multiple interface object. It has an
			// IConnectionPointContainer sub-object embedded directly inside of
			// it. And we just allocated it when we allocated the MyRealIExampleEvts2
			// above. Now we need to set its VTable into its lpVtbl member and
			// we're done initializing this sub-object
			thisobj->container.lpVtbl = (IConnectionPointContainerVtbl *)&IConnectionPointContainer_Vtbl;

			// Our MyRealIExampleEvts2 also has an IConnectionPoint sub-object
			// embedded directly inside of it. And we just allocated it when we
			// allocated the MyRealIExampleEvts2 above. Now we need to set its
			// VTable into its lpVtbl member and we're done initializing this sub-object
			thisobj->point.lpVtbl = (IConnectionPointVtbl *)&IConnectionPoint_Vtbl;

			// We don't have an app IFeedback2 object yet
			thisobj->feedback = 0;

			hr = IExampleEvts2_Vtbl.QueryInterface((IExampleEvts2 *)thisobj, vTableGuid, objHandle);
			IExampleEvts2_Vtbl.Release((IExampleEvts2 *)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_IExampleEvts2))
		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:
		{
			// No TypeInfo yet loaded
			MyTypeInfo = 0;

			OutstandingObjects = LockCount = 0;

			MyIClassFactoryObj.lpVtbl = (IClassFactoryVtbl *)&IClassFactory_Vtbl;

			DisableThreadLibraryCalls(instance);
			break;
		}

		case DLL_PROCESS_DETACH:
		{
			// Release any TYPEINFO that my IDispatch functions got
			if (MyTypeInfo) MyTypeInfo->lpVtbl->Release(MyTypeInfo);
		}
	}

	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