// This file contains functions for managing a list of "Network cards"
// via an INetwork (IDispatch) sub-object.
#include <windows.h>
#include <objbase.h>
#include "IExample5.h"
#include "IEnumVariant.h"
// A linked list of IENUMITEMs for our "INetwork objects"
static IENUMLIST NetworkObjectsList;
// The ITypeInfo for out INetwork
static ITypeInfo *NetTypeInfo;
// For wsprintf()
static const OLECHAR NameFormat[] = L"Network %u";
static const OLECHAR AddressFormat[] = L"%u:%u:%u:%u";
//==================================================================
//======================= INetwork functions ==========================
//==================================================================
typedef struct {
INetworkVtbl *lpVtbl;
DWORD count;
DWORD portNum;
DWORD address;
} MyRealINetwork;
// INetwork's AddRef()
static STDMETHODIMP_(ULONG) AddRef(INetwork *this)
{
return(++((MyRealINetwork *)this)->count);
}
// INetwork's QueryInterface()
static STDMETHODIMP QueryInterface(INetwork *this, REFIID riid, void **ppvObj)
{
if (!ppvObj) return(E_POINTER);
// It can masquerade as an IUnknown or an IDispatch. Of course, it really has an
// INetwork VTable, so if anyone passed the GUID we associated with that,
// then we confirm it
if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDispatch) || IsEqualIID(riid, &IID_INetwork))
{
*ppvObj = this;
AddRef(this);
return(NOERROR);
}
*ppvObj = 0;
return(E_NOINTERFACE);
}
// INetwork's Release()
static STDMETHODIMP_(ULONG) Release(INetwork *this)
{
if (--((MyRealINetwork *)this)->count) return(((MyRealINetwork *)this)->count);
// Free the MyRealINetwork
GlobalFree(this);
// One less outstanding object
decOutstandingObjects();
return(0);
}
// This is just a helper function for the IDispatch functions below
static HRESULT loadNetTypeInfo(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 INetwork, by passing that GUID
if (!(hr = pTypeLib->lpVtbl->GetTypeInfoOfGuid(pTypeLib, &IID_INetwork, &NetTypeInfo)))
{
// 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
NetTypeInfo->lpVtbl->AddRef(NetTypeInfo);
}
}
return(hr);
}
// INetwork's GetTypeInfoCount()
static ULONG STDMETHODCALLTYPE GetTypeInfoCount(INetwork *this, UINT *pCount)
{
// We do have type library information for our INetwork object, so return 1
*pCount = 1;
return(S_OK);
}
// INetwork's GetTypeInfo(). The caller uses this to get ahold of an ITypeInfo
// object that contains information about the extra functions in our INetwork's
// VTable (ie, Name and Address).
static ULONG STDMETHODCALLTYPE GetTypeInfo(INetwork *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 ITypeInfo and compare it to what the caller wants.
// If no match, unloaded the currently created ITypeInfo, and create the correct one. But since
// we support only one language in our IDL file anyway, we'll ignore this
else if (NetTypeInfo)
{
NetTypeInfo->lpVtbl->AddRef(NetTypeInfo);
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 = loadNetTypeInfo();
}
if (!hr) *pTypeInfo = NetTypeInfo;
return(hr);
}
// INetwork's GetIDsOfNames(). This is used to get the DISPID for any
// one of the extra functions in our INetwork's VTable (ie, Name and
// Address)
static ULONG STDMETHODCALLTYPE GetIDsOfNames(INetwork *this, REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgdispid)
{
if (!NetTypeInfo)
{
register HRESULT hr;
if ((hr = loadNetTypeInfo())) 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 INetwork
return(DispGetIDsOfNames(NetTypeInfo, rgszNames, cNames, rgdispid));
}
// INetwork's Invoke(). This is used to indirectly call the extra
// functions in our INetwork's VTable (ie, Name and Address)
static ULONG STDMETHODCALLTYPE Invoke(INetwork *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);
if (!NetTypeInfo)
{
register HRESULT hr;
if ((hr = loadNetTypeInfo())) 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, NetTypeInfo, dispid, wFlags, params, result, pexcepinfo, puArgErr));
}
// INetwork's Name(). Returns a BSTR of the card's name
static STDMETHODIMP Name(INetwork *this, BSTR *name)
{
wchar_t buffer[100];
// Construct the network card name as a UNICODE string
wsprintfW(&buffer[0], &NameFormat[0], ((MyRealINetwork *)this)->portNum);
// Alloc a BSTR of it. Caller is expected to SysFreeString it
if (!(*name = SysAllocString(&buffer[0])))
return(E_OUTOFMEMORY);
return(S_OK);
}
// INetwork's Address(). This returns the Mac address of the card
// (as a BSTR).
static STDMETHODIMP Address(INetwork *this, BSTR *address)
{
wchar_t buffer[100];
// Construct the network Mac address as a UNICODE string
wsprintfW(&buffer[0], &AddressFormat[0], (((MyRealINetwork *)this)->address >> 24) & 0x000000FF,
(((MyRealINetwork *)this)->address >> 16) & 0x000000FF,
(((MyRealINetwork *)this)->address >> 8) & 0x000000FF,
((MyRealINetwork *)this)->address & 0x000000FF);
// Alloc a BSTR of it. Caller is expected to SysFreeString it
if (!(*address = SysAllocString(&buffer[0])))
return(E_OUTOFMEMORY);
return(S_OK);
}
// Our INetwork object's VTable
static const INetworkVtbl INetworkVTable = {QueryInterface,
AddRef,
Release,
GetTypeInfoCount,
GetTypeInfo,
GetIDsOfNames,
Invoke,
Name,
Address};
// This is a help function to allocate an INetwork object
static IDispatch * allocINetworkObject(DWORD portNum, DWORD address)
{
MyRealINetwork *port;
if ((port = (MyRealINetwork *)GlobalAlloc(GMEM_FIXED, sizeof(MyRealINetwork))))
{
port->lpVtbl = (INetworkVtbl *)&INetworkVTable;
port->count = 1; // AddRef it
port->portNum = portNum;
port->address = address;
incOutstandingObjects();
}
return((IDispatch *)port);
}
//==================================================================
//======================= Helper functions =========================
//==================================================================
// This is just a helper function to allocate/initialize an IDispatch
// (really, a MyRealICollection) object for our NetworkObjectsList.
IDispatch * allocNetworkObjectsCollection(void)
{
// Unlike with our PortsList (in PortNames.c) we're going to
// create our NetworkObjectsList only when someone actually
// wants to return a collection wrapped around it. We could
// alternately allocate the list once in initNetworkObjectsCollection(),
// but we're going to instead demonstrate creating it on-the-fly as
// it is needed by an app, and then deleting it when the app no longer
// needs the collection.
// Do we already have the list of INetwork objects?
if (!NetworkObjectsList.head && !NetworkObjectsList.count)
{
// No. Let's create a list of 3 INetwork objects
VARIANT value;
IENUMITEM *item;
// Each IENUMITEM stores a pointer to an INetwork object (which
// can masquerade as an IDispatch)
value.vt = VT_DISPATCH;
// Alloc INetwork with a name of "Network 1" at Mac address "0:0:0:0"
if ((value.pdispVal = allocINetworkObject(1, 0)))
{
// Put an IENUMITEM into our list, with this INetwork as its value
if ((NetworkObjectsList.head = item = allocIENUMITEM(&value)))
{
// Alloc INetwork with a name of "Network 2" at Mac address "192:192:0:0"
if ((value.pdispVal = allocINetworkObject(2, (192 << 24) | (192 << 16))))
{
if ((item->next = allocIENUMITEM(&value)))
{
item = item->next;
// Alloc INetwork with a name of "Network 3" at Mac address "192:192:0:1"
if ((value.pdispVal = allocINetworkObject(3, (192 << 24) | (192 << 16) | 1)))
{
if ((item->next = allocIENUMITEM(&value)))
goto success;
}
}
}
}
}
// Failed creating our list of INetwork sub-objects
freeEnumList(&NetworkObjectsList);
return(0);
}
success:
{
IDispatch *networks;
// Bump up the count
++NetworkObjectsList.count;
// Allocate/return a collection object (actually, a MyRealICollection) wrapping NetworkObjectsList
networks = allocICollection(&NetworkObjectsList);
// The following call to freeEnumList will first decrement the IENUMLIST count.
// If allocICollection succeeded in getting a collection object, it will have
// bumped up the count too. So our call here just undoes the increment we
// did above, but does not free the list. If allocICollection fails, then it
// will not have incremented the count. If there are no other objects using
// our list, the list will end up being freed
freeEnumList(&NetworkObjectsList);
return(networks);
}
}
// This is just a helper function to free up our NetworkObjectsList. Called when our DLL unloads.
void freeNetworkObjectsCollection(void)
{
// Free all IENUMITEMs/INetwork objects in NetworkObjectsList
freeEnumList(&NetworkObjectsList);
if (NetTypeInfo) NetTypeInfo->lpVtbl->Release(NetTypeInfo);
}
// This is just a helper function to initialize our Network cards list.
// Called when our DLL first loads.
HRESULT initNetworkObjectsCollection(void)
{
// We don't create the list until we actually need it
NetworkObjectsList.head = 0;
NetworkObjectsList.count = 0;
NetTypeInfo = 0;
return(S_OK);
}