// This is a C example that tests the IExampleEvts2 COM component (in IExampleEvts2.dll).
//
// IExampleEvts2.DLL has a COM component known as an IExampleEvts2. This component
// also has an IConnectionPointContainer sub-object. So IExampleEvts2 supports us
// giving it some object that wraps some of our callback functions. Specifically,
// IExampleEvts2.h defines an IFeedback2 object we can create to wrap our callback
// functions. IFeedback2 contains one callback functions we can write. IFeedback2's
// VTable uses the name "Compare" for that function's pointer in the VTable.
// We'll use the same name for the function itself.
#include <windows.h>
#include <objbase.h>
#include <activscp.h>
#include <stdio.h>
#include "../IExampleEvts2/IExampleEvts2.h"
// For holding the ITypeInfo for IFeedback2's VTable
static ITypeInfo *MyTypeInfo;
// IExampleEvts2 supports us giving it only 1 IFeedback2 object, so for simplicity, we
// can just declare it as a global. That way, we don't need to allocate it, free it,
// nor maintain a reference count for it. Here's our 1 IFeedback2 object.
static IFeedback2 MyCompare2;
static HRESULT STDMETHODCALLTYPE QueryInterface(IFeedback2 *this, REFIID vTableGuid, void **ppv)
{
// This is an IFeedback2 object, so we must recognize IFeedback2 VTable's GUID,
// which is defined for us in IExampleEvts2.h. Our IFeedback2 can also masquerade
// as an IUnknown
if (!IsEqualIID(vTableGuid, &IID_IUnknown) && !IsEqualIID(vTableGuid, &DIID_IFeedback2))
{
*ppv = 0;
return(E_NOINTERFACE);
}
*ppv = this;
// Normally, we'd call our AddRef function here. But since our IFeedback2 isn't
// allocated, we don't need to bother with reference counting
return(NOERROR);
}
static ULONG STDMETHODCALLTYPE AddRef(IFeedback2 *this)
{
// Our one and only IFeedback2 isn't allocated. Instead, it is statically
// declared above (MyCompare2). So we'll just return a 1
return(1);
}
static ULONG STDMETHODCALLTYPE Release(IFeedback2 *this)
{
return(1);
}
// ================== The standard IDispatch functions
// This is just a helper function for the IDispatch Invoke() below
static HRESULT loadMyTypeInfo(void)
{
register HRESULT hr;
LPTYPELIB pTypeLib;
// Load IExampleEvts2 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 the loaded type library. We only
// need one of these, and we'll store it in a global Tell Microsoft this is for
// our IFeedback2's VTable, by passing that VTable's GUID
if (!(hr = pTypeLib->lpVtbl->GetTypeInfoOfGuid(pTypeLib, &DIID_IFeedback2, &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);
}
// IFeedback2's GetTypeInfoCount()
static ULONG STDMETHODCALLTYPE GetTypeInfoCount(IFeedback2 *this, UINT *pCount)
{
// Nobody should be calling our GetTypeInfoCount function, because this
// is a callback object. The only person that will be calling our
// IFeedback2's functions is the IExampleEvts2 object in IExampleEvts2.DLL.
// And it ought to know what functions are in our IFeedback2 (since it
// defined the VTable). So it doesn't need to get ahold of a ITypeInfo
// for its own IFeedback2 VTable definition
*pCount = 0;
return(S_OK);
}
// IFeedback2's GetTypeInfo()
static ULONG STDMETHODCALLTYPE GetTypeInfo(IFeedback2 *this, UINT itinfo, LCID lcid, ITypeInfo **pTypeInfo)
{
// Nobody should be calling our GetTypeInfo function, because this
// is a callback object. The only person that will be calling our
// IFeedback2's functions is the IExampleEvts2 object in IExampleEvts2.DLL.
// And it ought to know what functions are in our IFeedback2 (since it
// defined the VTable). So it doesn't need to get ahold of a ITypeInfo
// for its own IFeedback2 VTable definition
*pTypeInfo = 0;
return(E_NOTIMPL);
}
// IFeedback2's GetIDsOfNames()
static ULONG STDMETHODCALLTYPE GetIDsOfNames(IFeedback2 *this, REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgdispid)
{
// Nobody should be calling our GetIDsOfNames function, because this
// is a callback object. The only person that will be calling our
// IFeedback2's functions is the IExampleEvts2 object in IExampleEvts2.DLL.
// And it ought to know what functions are in our IFeedback2 (since it
// defined the VTable), and what are the DISPIDs of those functions.
// So it doesn't need us to return the DISPID for one of IFeedback2's
// function names
return(E_NOTIMPL);
}
// IFeedback2's Invoke()
static ULONG STDMETHODCALLTYPE Invoke(IFeedback2 *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 (callback) functions
static HRESULT STDMETHODCALLTYPE Callback1(IFeedback2 *this)
{
printf("Callback1 is called.\n");
return(S_OK);
}
static HRESULT STDMETHODCALLTYPE Callback2(IFeedback2 *this)
{
printf("Callback2 is called.\n");
return(S_OK);
}
static HRESULT STDMETHODCALLTYPE Callback3(IFeedback2 *this)
{
printf("Callback3 is called.\n");
return(S_OK);
}
static HRESULT STDMETHODCALLTYPE Callback4(IFeedback2 *this)
{
printf("Callback4 is called.\n");
return(S_OK);
}
static HRESULT STDMETHODCALLTYPE Callback5(IFeedback2 *this)
{
printf("Callback5 is called.\n");
return(S_OK);
}
// Our IFeedback2 VTable. We need only one of these, so we can
// declare it static
static const IFeedback2Vtbl IFeedback2_Vtbl = {QueryInterface,
AddRef,
Release,
GetTypeInfoCount,
GetTypeInfo,
GetIDsOfNames,
Invoke,
// Although IExampleEvts2.IDL does not use the "dual" keyword on the
// IFeedback2 VTable, we're nevertheless going to include the extra
// functions in our VTable (as if it were "dual"). In the way, we
// can use DispInvoke for our Invoke function above
Callback1,
Callback2,
Callback3,
Callback4,
Callback5};
int main(int argc, char **argv)
{
IExampleEvts2 *exampleObj;
IConnectionPointContainer *container;
IConnectionPoint *point;
HRESULT hr;
DWORD cookie;
// Although we don't have to allocate our IFeedback2, we do need to
// initialize it, by storing its VTable in its lpVtbl member
MyCompare2.lpVtbl = (IFeedback2Vtbl *)&IFeedback2_Vtbl;
MyTypeInfo = 0;
if (!CoInitialize(0))
{
// Get an IExampleEvts2 object
if ((hr = CoCreateInstance(&CLSID_IExampleEvts2, 0, CLSCTX_ALL, &IID_IExampleEvts2, &exampleObj)))
MessageBox(0, "Can't create IExampleEvts2 object", "CreateInstance error", MB_OK|MB_ICONEXCLAMATION);
else
{
// Get IExampleEvts2' IConnectionPointContainer sub-object. We do this by calling
// IExampleEvts2' QueryInterface, and pass it the standard GUID for an
// IConnectionPointContainer VTable
if ((hr = exampleObj->lpVtbl->QueryInterface(exampleObj, &IID_IConnectionPointContainer, &container)))
MessageBox(0, "Can't get IConnectionPointContainer object", "QueryInterface error", MB_OK|MB_ICONEXCLAMATION);
else
{
// Get IExampleEvts2's IConnectionPoint sub-object for specifically giving
// IExampleEvts2 our IFeedback2. We do this by calling IConnectionPointContainer's
// FindConnectionPoint, and pass it IFeedback2 VTable's GUID (defined for us in
// IExampleEvts2.h)
hr = container->lpVtbl->FindConnectionPoint(container, &DIID_IFeedback2, &point);
// We don't need the IConnectionPointContainer, now that we got the one IConnectionPoint
// we want (ie, the one we use to give IExampleEvts2 our IFeedback2)
container->lpVtbl->Release(container);
if (hr)
MessageBox(0, "Can't get IConnectionPoint object", "FindConnectionPoint error", MB_OK|MB_ICONEXCLAMATION);
else
{
// Now call the IConnectionPoint's Advise function, giving it some object
// whose QueryInterface it will call to get our IFeedback2 object. Let's
// just give it our IFeedback2 object. So Advise will call our IFeedback2's
// QueryInterface to tell us to give it a pointer to our IFeedback2. Dumb?
// You bet. 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.
//
// Advise() gives us back a "cookie" value that we'll need to later pass to
// Unadvise() (when we want to tell IExampleEvts2 to stop using our IFeedback2)
if ((hr = point->lpVtbl->Advise(point, (IUnknown *)&MyCompare2, &cookie)))
MessageBox(0, "Can't set our IFeedback2 object", "Advise error", MB_OK|MB_ICONEXCLAMATION);
else
{
DWORD i;
// Call IExampleEvts2' DoSomething() function 10 times.
for (i=0; i < 10; i++)
exampleObj->lpVtbl->DoSomething(exampleObj);
// Tell IExampleEvts2 to stop using our IFeedback2 (and Release it). We pass
// the cookie value we got from Advise() above
if ((hr = point->lpVtbl->Unadvise(point, cookie)))
MessageBox(0, "Can't unset our IFeedback2 object", "Unadvise error", MB_OK|MB_ICONEXCLAMATION);
// Release the IConnectionPoint now that we're done with it (and
// we've told IExampleEvts2 to stop calling our IFeedback2 functions)
point->lpVtbl->Release(point);
}
}
}
// Release the IExampleEvts2 now that we're done with it
exampleObj->lpVtbl->Release(exampleObj);
}
// Release any TYPEINFO that my IDispatch functions got
if (MyTypeInfo) MyTypeInfo->lpVtbl->Release(MyTypeInfo);
// When finally done with OLE, free it
CoUninitialize();
}
else
MessageBox(0, "Can't initialize COM", "CoInitialize error", MB_OK|MB_ICONEXCLAMATION);
return(0);
}