// 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);
}