Click here to Skip to main content
15,881,092 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.2K   2K   83  
An ActiveX Script Host with custom COM objects. This allows a script to call C functions in your app.
// 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);
}

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