// This is a C example that tests the IExample4 COM component (in IExample4.dll).
#include <windows.h>
#include <objbase.h>
#include <stdio.h>
#include "../IExample4/IExample4.h"
int main(int argc, char **argv)
{
IExample4 *exampleObj;
IClassFactory *classFactory;
HRESULT hr;
// We must initialize OLE before we do anything with COM objects. NOTE:
// some COM components, such as the IE browser engine, require that you
// use OleInitialize() instead. But our COM component doesn't require this
if (!CoInitialize(0))
{
// Get IExample4.DLL's IClassFactory
if ((hr = CoGetClassObject(&CLSID_IExample4, CLSCTX_INPROC_SERVER, 0, &IID_IClassFactory, &classFactory)))
MessageBox(0, "Can't get IClassFactory", "CoGetClassObject error", MB_OK|MB_ICONEXCLAMATION);
else
{
// Create an IExample object
if ((hr = classFactory->lpVtbl->CreateInstance(classFactory, 0, &IID_IExample4, &exampleObj)))
{
classFactory->lpVtbl->Release(classFactory);
MessageBox(0, "Can't create IExample4 object", "CreateInstance error", MB_OK|MB_ICONEXCLAMATION);
}
else
{
// Release the IClassFactory. We don't need it now that we have the one
// IExample4 we want
classFactory->lpVtbl->Release(classFactory);
//==========================================================================
// STUDY THIS
//==========================================================================
{
// Get the IDispatch object for the Ports collection, and stuff it into our variable "portsObj".
// NOTE: What our DLL is really returning is a MyRealICollection object. But this app doesn't
// know that. All we know here is that this object is an IDispatch. So its VTable has the 3
// IUnknown functions (QueryInterface, AddRef, and Release) and the 4 IDispatch functions
// (GetTypeInfoCount, GetTypeInfo, GetIDsOfNames, and Invoke). And that's all as far as we're
// concerned. If the object has any other functions, we can't call them directly by referencing
// lpVtbl. We have to call those extra functions indirectly via the Invoke function, as we'll
// see below
IDispatch *portsObj;
if ((hr = exampleObj->lpVtbl->GetPorts(exampleObj, &portsObj)))
MessageBox(0, "Can't get the Ports collection (IDispatch) object", "GetPorts error", MB_OK|MB_ICONEXCLAMATION);
else
{
IEnumVARIANT *enumObj;
VARIANT ret;
ULONG count;
DISPPARAMS dspp;
// Get the IEnumVARIANT for the Ports IDispatch object. We do this by calling our collection's
// _NewEnum() function. We have to do that indirectly by calling our collection's Invoke function.
// Fortunately, the _NewEnum function is passed no args, so we can simply zero out the DISPPARAMS
// we pass to invoke
ZeroMemory(&dspp, sizeof(DISPPARAMS));
// We know that _NewEnum has a DISPID of DISPID_NEWENUM, so we don't need to call GetIDsOfNames
// to look that up. We do have to pass a VARIANT where a pointer to an IUnknown object will be
// returned to us. And we initialize it first too (just in case _NewEnum doesn't do that)
VariantInit(&ret);
if (!(hr = portsObj->lpVtbl->Invoke(portsObj, DISPID_NEWENUM, &IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD|DISPATCH_PROPERTYGET, &dspp, &ret, 0, 0))
// Make sure _NewEnum returned an IUnknown pointer in our VARIANT's punkVal
&& ret.vt == VT_UNKNOWN)
{
// Call the IUnknown's QueryInterface function, and ask it to return a pointer to the
// IEnumVARIANT object
hr = ret.punkVal->lpVtbl->QueryInterface(ret.punkVal, &IID_IEnumVARIANT, (void **)&enumObj);
}
// We don't need the IUnknown object now that we've got the IEnumVARIANT. By calling
// VariantClear(), it will do the Release on our VARIANT's punkVal member. (And if an
// error occurred, our VARIANT's vt member is still VT_NOTHING so VariantClear does nothing
VariantClear(&ret);
// We also don't need the Ports collection object anymore. Release it. Good riddance
portsObj->lpVtbl->Release(portsObj);
// Did we get that IEnumVARIANT?
if (hr)
MessageBox(0, "Can't get the IEnumVARIANT object", "Invoke error", MB_OK|MB_ICONEXCLAMATION);
else
{
// Call the IEnumVARIANT's Next(), and display the VARIANT value it returns each time.
// NOTE: We expect the VARIANT it returns to have a type of VT_BSTR, so we can display it
// as is. We break out of the loop when Next() finally returns no more items (ie, it
// sets our count variable to 0)
for (;;)
{
enumObj->lpVtbl->Next(enumObj, 1, &ret, &count);
if (!count) break;
printf("%S\n", ret.bstrVal);
// We need to SysFreeString the BSTR when we're done with it. By calling
// VariantClear(), it will do that on our VARIANT's bstrVal member
VariantClear(&ret);
}
// Release the IEnumVARIANT now that we're done with it
enumObj->lpVtbl->Release(enumObj);
}
}
}
//==========================================================================
// Release the IExample4 now that we're done with it
exampleObj->lpVtbl->Release(exampleObj);
}
}
// When finally done with OLE, free it
CoUninitialize();
}
else
MessageBox(0, "Can't initialize COM", "CoInitialize error", MB_OK|MB_ICONEXCLAMATION);
return(0);
}