//
// Class for creating OLE automation controllers.
//
// CreateObject() creates an automation object
// Invoke() will call a property or method of the automation object.
// GetProperty() returns a property
// SetProperty() changes a property
// Method() invokes a method
//
// For example, the following VB code will control Microsoft Word:
//
//    Private Sub Form_Load()
//    Dim wb As Object
//    Set wb = CreateObject("Word.Basic")
//    wb.AppShow
//    wb.FileNewDefault
//    wb.Insert "This is a test"
//    wb.FileSaveAs "c:\sample.doc)"
//    End Sub
//
// A C++ automation controller that does the same can be written as follows:
// the helper functions:
//
//   Void FormLoad ()
//   {
//       COleAutomationControl Aut;
//       Aut.CreateObject("Word.Basic");
//       Aut.Method ("AppShow");
//       Aut.Method ("FileNewDefault");
//       Aut.Method ("Insert", "s", (LPOLESTR) OLESTR ("This is a test"));
//       Aut.Method ("FileSaveAs", "s", OLESTR ("c:\\sample.doc"));
//   }
//
//

#include "stdafx.h"
#include <stdarg.h>
#include "oleaut.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


static bool CountArgsInFormat (LPCTSTR Format, UINT* nArgs);
static LPCTSTR GetNextVarType (LPCTSTR Format, VARTYPE* pVarType);


COleAutomationControl::COleAutomationControl ()
{
	m_pDispatch = NULL;
	m_hResult = NOERROR;
	m_nErrArg = 0;
	VariantInit (&m_VariantResult);
}

COleAutomationControl::~COleAutomationControl ()
{
	DeleteObject ();
}

void COleAutomationControl::DeleteObject ()
{
	if (m_pDispatch)
	{
		m_pDispatch->Release ();
		m_pDispatch = NULL;
	}
}

// Creates an instance of the Automation object and
// obtains its IDispatch interface.
//
// Parameters:
// ProgId	  ProgID of Automation object
//
bool COleAutomationControl::CreateObject (char* ProgId)
{
	CLSID ClsId;			// CLSID of automation object
	LPUNKNOWN pUnknown = NULL;	// IUnknown of automation object

	// Retrieve CLSID from the progID that the user specified
	LPOLESTR OleProgId = TO_OLE_STR (ProgId);
	m_hResult = CLSIDFromProgID (OleProgId, &ClsId);
	if (FAILED (m_hResult))
		goto error;

	// Create an instance of the automation object and ask for the
	// IDispatch interface
	m_hResult = CoCreateInstance (ClsId, NULL, CLSCTX_SERVER,
			       IID_IUnknown, (void**) &pUnknown);
	if (FAILED (m_hResult))
		goto error;

	m_hResult = pUnknown->QueryInterface (IID_IDispatch, (void**) &m_pDispatch);
	if (FAILED (m_hResult))
		goto error;

	pUnknown->Release ();
	return true;

error:
	if (pUnknown)
		pUnknown->Release ();
	if (m_pDispatch)
		m_pDispatch->Release ();
	return false;
}

// Return the dispatch id of a named service
// This id can be used in subsequent calls to GetProperty (), SetProperty () and
// Method (). This is the preferred method when performance is important.
//
DISPID COleAutomationControl::GetDispatchId (char* Name)
{
	DISPID DispatchId;

	ASSERT (m_pDispatch);

	// Get DISPID of property/method
	LPOLESTR OleName = TO_OLE_STR (Name);
	m_hResult = m_pDispatch->GetIDsOfNames (IID_NULL, &OleName, 1,
						LOCALE_USER_DEFAULT, &DispatchId);
	if (FAILED (m_hResult))
		return NULL;
	return DispatchId;
}

//  The following functions use these parameters:
//
// Parameters:
//
//  Name      Name of property or method.
//
//  Format    Format string that describes the variable list of parameters that
//	      follows. The format string can contain the following characters.
//	      & = mark the following format character as VT_BYREF
//	      B = VT_BOOL
//	      i = VT_I2
//	      I = VT_I4
//	      r = VT_R2
//	      R = VT_R4
//	      c = VT_CY
//	      s = VT_BSTR (string pointer can be passed,
//			BSTR will be allocated by this function).
//	      e = VT_ERROR
//	      d = VT_DATE
//	      v = VT_VARIANT. Use this to pass data types that are not described
//			in the format string. (For example SafeArrays).
//	      D = VT_DISPATCH
//	      U = VT_UNKNOWN
//
//  ...       Arguments of the property or method.
//	      Arguments are described by Format.
//

bool COleAutomationControl::GetProperty (char* Name)
{
	return Invoke (DISPATCH_PROPERTYGET, Name, NULL, NULL);
}

bool COleAutomationControl::GetProperty (DISPID DispatchId)
{
	return Invoke (DISPATCH_PROPERTYGET, DispatchId, NULL, NULL);
}

bool COleAutomationControl::PutProperty (char* Name, LPCTSTR Format, ...)
{
	va_list ArgList;

	va_start (ArgList, Format);
	bool bRet = Invoke (DISPATCH_PROPERTYPUT, Name, Format, ArgList);
	va_end (ArgList);
	return bRet;
}

bool COleAutomationControl::PutProperty (DISPID DispatchId, LPCTSTR Format, ...)
{
	va_list ArgList;

	va_start (ArgList, Format);
	bool bRet = Invoke (DISPATCH_PROPERTYPUT, DispatchId, Format, ArgList);
	va_end (ArgList);
	return bRet;
}

bool COleAutomationControl::Method (char* Name, LPCTSTR Format, ...)
{
	va_list ArgList;

	va_start (ArgList, Format);
	bool bRet = Invoke (DISPATCH_METHOD, Name, Format, ArgList);
	va_end (ArgList);
	return bRet;
}

bool COleAutomationControl::Method (DISPID DispatchId, LPCTSTR Format, ...)
{
	va_list ArgList;

	va_start (ArgList, Format);
	bool bRet = Invoke (DISPATCH_METHOD, DispatchId, Format, ArgList);
	va_end (ArgList);
	return bRet;
}

bool COleAutomationControl::Invoke (WORD Flags, char* Name,
				    LPCTSTR Format, va_list ArgList)
{
	DISPID DispatchId = GetDispatchId (Name);
	if (! DispatchId)
		return false;
	return Invoke (Flags, DispatchId, Format, ArgList);
}

bool COleAutomationControl::Invoke (WORD Flags, DISPID DispatchId,
				    LPCTSTR Format, va_list ArgList)
{
	UINT ArgCount = 0;
	VARIANTARG* ArgVector = NULL;

	ASSERT (m_pDispatch);

	DISPPARAMS DispatchParams;
	memset (&DispatchParams, 0, sizeof (DispatchParams));

	// Determine number of arguments
	if (Format)
		CountArgsInFormat (Format, &ArgCount);

	// Property puts have a named argument that represents the value that
	// the property is being assigned.
	DISPID DispIdNamed = DISPID_PROPERTYPUT;
	if (Flags & DISPATCH_PROPERTYPUT)
	{
		if (ArgCount == 0)
		{
			m_hResult = ResultFromScode (E_INVALIDARG);
			return false;
		}
		DispatchParams.cNamedArgs = 1;
		DispatchParams.rgdispidNamedArgs = &DispIdNamed;
	}

	if (ArgCount)
	{
		// Allocate memory for all VARIANTARG parameters
		ArgVector = (VARIANTARG*) CoTaskMemAlloc (
				ArgCount * sizeof (VARIANTARG));
		if (! ArgVector)
		{
			m_hResult = ResultFromScode (E_OUTOFMEMORY);
			return false;
		}
		memset (ArgVector, 0, sizeof (VARIANTARG) * ArgCount);

		// Get ready to walk vararg list
		LPCTSTR s = Format;

		VARIANTARG *p = ArgVector + ArgCount - 1;  // Params go in opposite order

		for (;;)
		{
			VariantInit (p);
			if (! (s = GetNextVarType (s, &p->vt)))
				break;

			if (p < ArgVector)
			{
				m_hResult = ResultFromScode (E_INVALIDARG);
				goto Cleanup;
			}
			switch (p->vt)
			{
			    case VT_I2:
				V_I2 (p) = va_arg (ArgList, short);
				break;
			    case VT_I4:
				V_I4 (p) = va_arg (ArgList, long);
				break;
			    case VT_R4:
				V_R4 (p) = va_arg (ArgList, float);
				break;
			    case VT_DATE:
			    case VT_R8:
				V_R8 (p) = va_arg (ArgList, double);
				break;
			    case VT_CY:
				V_CY (p) = va_arg (ArgList, CY);
				break;
			    case VT_BSTR:
				V_BSTR (p) = SysAllocString (va_arg (ArgList,
								     OLECHAR*));
				if (! p->bstrVal)
				{
					m_hResult = ResultFromScode (E_OUTOFMEMORY);
					p->vt = VT_EMPTY;
					goto Cleanup;
				}
				break;
			    case VT_DISPATCH:
				V_DISPATCH (p) = va_arg (ArgList, LPDISPATCH);
				break;
			    case VT_ERROR:
				V_ERROR (p) = va_arg (ArgList, SCODE);
				break;
			    case VT_BOOL:
				V_BOOL (p) = va_arg (ArgList, BOOL) ? -1 : 0;
				break;
			    case VT_VARIANT:
				*p = va_arg (ArgList, VARIANTARG);
				break;
			    case VT_UNKNOWN:
				V_UNKNOWN (p) = va_arg (ArgList, LPUNKNOWN);
				break;

			    case VT_I2 | VT_BYREF:
				V_I2REF (p) = va_arg (ArgList, short*);
				break;
			    case VT_I4 | VT_BYREF:
				V_I4REF (p) = va_arg (ArgList, long*);
				break;
			    case VT_R4 | VT_BYREF:
				V_R4REF (p) = va_arg (ArgList, float*);
				break;
			    case VT_R8 | VT_BYREF:
				V_R8REF (p) = va_arg (ArgList, double*);
				break;
			    case VT_DATE | VT_BYREF:
				V_DATEREF (p) = va_arg (ArgList, DATE*);
				break;
			    case VT_CY | VT_BYREF:
				V_CYREF (p) = va_arg (ArgList, CY*);
				break;
			    case VT_BSTR | VT_BYREF:
				V_BSTRREF (p) = va_arg (ArgList, BSTR*);
				break;
			    case VT_DISPATCH | VT_BYREF:
				V_DISPATCHREF (p) = va_arg (ArgList, LPDISPATCH*);
				break;
			    case VT_ERROR | VT_BYREF:
				V_ERRORREF (p) = va_arg (ArgList, SCODE*);
				break;
			    case VT_BOOL | VT_BYREF:
				{
					BOOL* pBool = va_arg (ArgList, BOOL*);

					*pBool = 0;
					V_BOOLREF (p) = (VARIANT_BOOL*) pBool;
				}
				break;
			    case VT_VARIANT | VT_BYREF:
				V_VARIANTREF (p) = va_arg (ArgList, VARIANTARG*);
				break;
			    case VT_UNKNOWN | VT_BYREF:
				V_UNKNOWNREF (p) = va_arg (ArgList, LPUNKNOWN*);
				break;

			    default:
				{
					m_hResult = ResultFromScode (E_INVALIDARG);
					goto Cleanup;
				}
				break;
			}

			--p;	// Get ready to fill next argument
		}
	}

	DispatchParams.cArgs = ArgCount;
	DispatchParams.rgvarg = ArgVector;

	// Initialize return variant, in case caller forgot. Caller can pass
	// NULL if return value is not expected.
	VariantInit (&m_VariantResult);

	// Make the call
	m_hResult = m_pDispatch->Invoke (DispatchId, IID_NULL, LOCALE_USER_DEFAULT,
					 Flags, &DispatchParams, &m_VariantResult,
					 &m_ExceptionInfo, &m_nErrArg);

    Cleanup:
	// Cleanup any arguments that need cleanup
	if (ArgCount)
	{
		VARIANTARG* p = ArgVector;

		while (ArgCount--)
		{
			switch (p->vt)
			{
			    case VT_BSTR:
				VariantClear (p);
				break;
			}
			++p;
		}
		CoTaskMemFree (ArgVector);
	}

	return FAILED (m_hResult) ? false : true;
}

#define CASE_SCODE(sc)  \
	case sc: \
	lstrcpy((char*)ErrName, (char*)#sc); \
	break;

void COleAutomationControl::ErrDiag ()
{
	char ErrName[200];

	SCODE sc = GetScode (m_hResult);
	switch (sc)
	{
	    // SCODE's defined in SCODE.H
	    CASE_SCODE (S_OK)
	    CASE_SCODE (S_FALSE)
	    CASE_SCODE (E_UNEXPECTED)
	    CASE_SCODE (E_OUTOFMEMORY)
	    CASE_SCODE (E_INVALIDARG)
	    CASE_SCODE (E_NOINTERFACE)
	    CASE_SCODE (E_POINTER)
	    CASE_SCODE (E_HANDLE)
	    CASE_SCODE (E_ABORT)
	    CASE_SCODE (E_FAIL)
	    CASE_SCODE (E_ACCESSDENIED)

	    // SCODE's defined in OLE2.H
	    CASE_SCODE (OLE_E_OLEVERB)
	    CASE_SCODE (OLE_E_ADVF)
	    CASE_SCODE (OLE_E_ENUM_NOMORE)
	    CASE_SCODE (OLE_E_ADVISENOTSUPPORTED)
	    CASE_SCODE (OLE_E_NOCONNECTION)
	    CASE_SCODE (OLE_E_NOTRUNNING)
	    CASE_SCODE (OLE_E_NOCACHE)
	    CASE_SCODE (OLE_E_BLANK)
	    CASE_SCODE (OLE_E_CLASSDIFF)
	    CASE_SCODE (OLE_E_CANT_GETMONIKER)
	    CASE_SCODE (OLE_E_CANT_BINDTOSOURCE)
	    CASE_SCODE (OLE_E_STATIC)
	    CASE_SCODE (OLE_E_PROMPTSAVECANCELLED)
	    CASE_SCODE (OLE_E_INVALIDRECT)
	    CASE_SCODE (OLE_E_WRONGCOMPOBJ)
	    CASE_SCODE (OLE_E_INVALIDHWND)
	    CASE_SCODE (OLE_E_NOT_INPLACEACTIVE)
	    CASE_SCODE (OLE_E_CANTCONVERT)
	    CASE_SCODE (OLE_E_NOSTORAGE)

	    CASE_SCODE (DV_E_FORMATETC)
	    CASE_SCODE (DV_E_DVTARGETDEVICE)
	    CASE_SCODE (DV_E_STGMEDIUM)
	    CASE_SCODE (DV_E_STATDATA)
	    CASE_SCODE (DV_E_LINDEX)
	    CASE_SCODE (DV_E_TYMED)
	    CASE_SCODE (DV_E_CLIPFORMAT)
	    CASE_SCODE (DV_E_DVASPECT)
	    CASE_SCODE (DV_E_DVTARGETDEVICE_SIZE)
	    CASE_SCODE (DV_E_NOIVIEWOBJECT)

	    CASE_SCODE (OLE_S_USEREG)
	    CASE_SCODE (OLE_S_STATIC)
	    CASE_SCODE (OLE_S_MAC_CLIPFORMAT)

	    CASE_SCODE (CONVERT10_E_OLESTREAM_GET)
	    CASE_SCODE (CONVERT10_E_OLESTREAM_PUT)
	    CASE_SCODE (CONVERT10_E_OLESTREAM_FMT)
	    CASE_SCODE (CONVERT10_E_OLESTREAM_BITMAP_TO_DIB)
	    CASE_SCODE (CONVERT10_E_STG_FMT)
	    CASE_SCODE (CONVERT10_E_STG_NO_STD_STREAM)
	    CASE_SCODE (CONVERT10_E_STG_DIB_TO_BITMAP)
	    CASE_SCODE (CONVERT10_S_NO_PRESENTATION)

	    CASE_SCODE (CLIPBRD_E_CANT_OPEN)
	    CASE_SCODE (CLIPBRD_E_CANT_EMPTY)
	    CASE_SCODE (CLIPBRD_E_CANT_SET)
	    CASE_SCODE (CLIPBRD_E_BAD_DATA)
	    CASE_SCODE (CLIPBRD_E_CANT_CLOSE)

	    CASE_SCODE (DRAGDROP_E_NOTREGISTERED)
	    CASE_SCODE (DRAGDROP_E_ALREADYREGISTERED)
	    CASE_SCODE (DRAGDROP_E_INVALIDHWND)
	    CASE_SCODE (DRAGDROP_S_DROP)
	    CASE_SCODE (DRAGDROP_S_CANCEL)
	    CASE_SCODE (DRAGDROP_S_USEDEFAULTCURSORS)

	    CASE_SCODE (OLEOBJ_E_NOVERBS)
	    CASE_SCODE (OLEOBJ_E_INVALIDVERB)
	    CASE_SCODE (OLEOBJ_S_INVALIDVERB)
	    CASE_SCODE (OLEOBJ_S_CANNOT_DOVERB_NOW)
	    CASE_SCODE (OLEOBJ_S_INVALIDHWND)
	    CASE_SCODE (INPLACE_E_NOTUNDOABLE)
	    CASE_SCODE (INPLACE_E_NOTOOLSPACE)
	    CASE_SCODE (INPLACE_S_TRUNCATED)

	    // SCODE's defined in COMPOBJ.H
	    CASE_SCODE (CO_E_NOTINITIALIZED)
	    CASE_SCODE (CO_E_ALREADYINITIALIZED)
	    CASE_SCODE (CO_E_CANTDETERMINECLASS)
	    CASE_SCODE (CO_E_CLASSSTRING)
	    CASE_SCODE (CO_E_IIDSTRING)
	    CASE_SCODE (CO_E_APPNOTFOUND)
	    CASE_SCODE (CO_E_APPSINGLEUSE)
	    CASE_SCODE (CO_E_ERRORINAPP)
	    CASE_SCODE (CO_E_DLLNOTFOUND)
	    CASE_SCODE (CO_E_ERRORINDLL)
	    CASE_SCODE (CO_E_WRONGOSFORAPP)
	    CASE_SCODE (CO_E_OBJNOTREG)
	    CASE_SCODE (CO_E_OBJISREG)
	    CASE_SCODE (CO_E_OBJNOTCONNECTED)
	    CASE_SCODE (CO_E_APPDIDNTREG)
	    CASE_SCODE (CLASS_E_NOAGGREGATION)
	    CASE_SCODE (CLASS_E_CLASSNOTAVAILABLE)
	    CASE_SCODE (REGDB_E_READREGDB)
	    CASE_SCODE (REGDB_E_WRITEREGDB)
	    CASE_SCODE (REGDB_E_KEYMISSING)
	    CASE_SCODE (REGDB_E_INVALIDVALUE)
	    CASE_SCODE (REGDB_E_CLASSNOTREG)
	    CASE_SCODE (REGDB_E_IIDNOTREG)
	    CASE_SCODE (RPC_E_CALL_REJECTED)
	    CASE_SCODE (RPC_E_CALL_CANCELED)
	    CASE_SCODE (RPC_E_CANTPOST_INSENDCALL)
	    CASE_SCODE (RPC_E_CANTCALLOUT_INASYNCCALL)
	    CASE_SCODE (RPC_E_CANTCALLOUT_INEXTERNALCALL)
	    CASE_SCODE (RPC_E_CONNECTION_TERMINATED)
	    CASE_SCODE (RPC_E_SERVER_DIED)
	    CASE_SCODE (RPC_E_CLIENT_DIED)
	    CASE_SCODE (RPC_E_INVALID_DATAPACKET)
	    CASE_SCODE (RPC_E_CANTTRANSMIT_CALL)
	    CASE_SCODE (RPC_E_CLIENT_CANTMARSHAL_DATA)
	    CASE_SCODE (RPC_E_CLIENT_CANTUNMARSHAL_DATA)
	    CASE_SCODE (RPC_E_SERVER_CANTMARSHAL_DATA)
	    CASE_SCODE (RPC_E_SERVER_CANTUNMARSHAL_DATA)
	    CASE_SCODE (RPC_E_INVALID_DATA)
	    CASE_SCODE (RPC_E_INVALID_PARAMETER)
	    CASE_SCODE (RPC_E_CANTCALLOUT_AGAIN)
	    CASE_SCODE (RPC_E_UNEXPECTED)

	    // SCODE's defined in DVOBJ.H
	    CASE_SCODE (DATA_S_SAMEFORMATETC)
	    CASE_SCODE (VIEW_E_DRAW)
	    CASE_SCODE (VIEW_S_ALREADY_FROZEN)
	    CASE_SCODE (CACHE_E_NOCACHE_UPDATED)
	    CASE_SCODE (CACHE_S_FORMATETC_NOTSUPPORTED)
	    CASE_SCODE (CACHE_S_SAMECACHE)
	    CASE_SCODE (CACHE_S_SOMECACHES_NOTUPDATED)

	    // SCODE's defined in STORAGE.H
	    CASE_SCODE (STG_E_INVALIDFUNCTION)
	    CASE_SCODE (STG_E_FILENOTFOUND)
	    CASE_SCODE (STG_E_PATHNOTFOUND)
	    CASE_SCODE (STG_E_TOOMANYOPENFILES)
	    CASE_SCODE (STG_E_ACCESSDENIED)
	    CASE_SCODE (STG_E_INVALIDHANDLE)
	    CASE_SCODE (STG_E_INSUFFICIENTMEMORY)
	    CASE_SCODE (STG_E_INVALIDPOINTER)
	    CASE_SCODE (STG_E_NOMOREFILES)
	    CASE_SCODE (STG_E_DISKISWRITEPROTECTED)
	    CASE_SCODE (STG_E_SEEKERROR)
	    CASE_SCODE (STG_E_WRITEFAULT)
	    CASE_SCODE (STG_E_READFAULT)
	    CASE_SCODE (STG_E_SHAREVIOLATION)
	    CASE_SCODE (STG_E_LOCKVIOLATION)
	    CASE_SCODE (STG_E_FILEALREADYEXISTS)
	    CASE_SCODE (STG_E_INVALIDPARAMETER)
	    CASE_SCODE (STG_E_MEDIUMFULL)
	    CASE_SCODE (STG_E_ABNORMALAPIEXIT)
	    CASE_SCODE (STG_E_INVALIDHEADER)
	    CASE_SCODE (STG_E_INVALIDNAME)
	    CASE_SCODE (STG_E_UNKNOWN)
	    CASE_SCODE (STG_E_UNIMPLEMENTEDFUNCTION)
	    CASE_SCODE (STG_E_INVALIDFLAG)
	    CASE_SCODE (STG_E_INUSE)
	    CASE_SCODE (STG_E_NOTCURRENT)
	    CASE_SCODE (STG_E_REVERTED)
	    CASE_SCODE (STG_E_CANTSAVE)
	    CASE_SCODE (STG_E_OLDFORMAT)
	    CASE_SCODE (STG_E_OLDDLL)
	    CASE_SCODE (STG_E_SHAREREQUIRED)
	    CASE_SCODE (STG_E_NOTFILEBASEDSTORAGE)
	    CASE_SCODE (STG_E_EXTANTMARSHALLINGS)
	    CASE_SCODE (STG_S_CONVERTED)

	    // SCODE's defined in STORAGE.H
	    CASE_SCODE (MK_E_CONNECTMANUALLY)
	    CASE_SCODE (MK_E_EXCEEDEDDEADLINE)
	    CASE_SCODE (MK_E_NEEDGENERIC)
	    CASE_SCODE (MK_E_UNAVAILABLE)
	    CASE_SCODE (MK_E_SYNTAX)
	    CASE_SCODE (MK_E_NOOBJECT)
	    CASE_SCODE (MK_E_INVALIDEXTENSION)
	    CASE_SCODE (MK_E_INTERMEDIATEINTERFACENOTSUPPORTED)
	    CASE_SCODE (MK_E_NOTBINDABLE)
	    CASE_SCODE (MK_E_NOTBOUND)
	    CASE_SCODE (MK_E_CANTOPENFILE)
	    CASE_SCODE (MK_E_MUSTBOTHERUSER)
	    CASE_SCODE (MK_E_NOINVERSE)
	    CASE_SCODE (MK_E_NOSTORAGE)
	    CASE_SCODE (MK_E_NOPREFIX)
	    CASE_SCODE (MK_S_REDUCED_TO_SELF)
	    CASE_SCODE (MK_S_ME)
	    CASE_SCODE (MK_S_HIM)
	    CASE_SCODE (MK_S_US)
	    CASE_SCODE (MK_S_MONIKERALREADYREGISTERED)

	    // SCODE's defined in DISPATCH.H
	    CASE_SCODE (DISP_E_UNKNOWNINTERFACE)
	    CASE_SCODE (DISP_E_MEMBERNOTFOUND)
	    CASE_SCODE (DISP_E_PARAMNOTFOUND)
	    CASE_SCODE (DISP_E_TYPEMISMATCH)
	    CASE_SCODE (DISP_E_UNKNOWNNAME)
	    CASE_SCODE (DISP_E_NONAMEDARGS)
	    CASE_SCODE (DISP_E_BADVARTYPE)
	    CASE_SCODE (DISP_E_EXCEPTION)
	    CASE_SCODE (DISP_E_OVERFLOW)
	    CASE_SCODE (DISP_E_BADINDEX)
	    CASE_SCODE (DISP_E_UNKNOWNLCID)
	    CASE_SCODE (DISP_E_ARRAYISLOCKED)
	    CASE_SCODE (DISP_E_BADPARAMCOUNT)
	    CASE_SCODE (DISP_E_PARAMNOTOPTIONAL)
	    CASE_SCODE (DISP_E_BADCALLEE)
	    CASE_SCODE (DISP_E_NOTACOLLECTION)

	    CASE_SCODE (TYPE_E_BUFFERTOOSMALL)
	    CASE_SCODE (TYPE_E_INVDATAREAD)
	    CASE_SCODE (TYPE_E_UNSUPFORMAT)
	    CASE_SCODE (TYPE_E_REGISTRYACCESS)
	    CASE_SCODE (TYPE_E_LIBNOTREGISTERED)
	    CASE_SCODE (TYPE_E_UNDEFINEDTYPE)
	    CASE_SCODE (TYPE_E_QUALIFIEDNAMEDISALLOWED)
	    CASE_SCODE (TYPE_E_INVALIDSTATE)
	    CASE_SCODE (TYPE_E_WRONGTYPEKIND)
	    CASE_SCODE (TYPE_E_ELEMENTNOTFOUND)
	    CASE_SCODE (TYPE_E_AMBIGUOUSNAME)
	    CASE_SCODE (TYPE_E_NAMECONFLICT)
	    CASE_SCODE (TYPE_E_UNKNOWNLCID)
	    CASE_SCODE (TYPE_E_DLLFUNCTIONNOTFOUND)
	    CASE_SCODE (TYPE_E_BADMODULEKIND)
	    CASE_SCODE (TYPE_E_SIZETOOBIG)
	    CASE_SCODE (TYPE_E_DUPLICATEID)
	    CASE_SCODE (TYPE_E_TYPEMISMATCH)
	    CASE_SCODE (TYPE_E_OUTOFBOUNDS)
	    CASE_SCODE (TYPE_E_IOERROR)
	    CASE_SCODE (TYPE_E_CANTCREATETMPFILE)
	    CASE_SCODE (TYPE_E_CANTLOADLIBRARY)
	    CASE_SCODE (TYPE_E_INCONSISTENTPROPFUNCS)
	    CASE_SCODE (TYPE_E_CIRCULARTYPE)

	    default:
		lstrcpy (ErrName, "UNKNOWN SCODE");
	}

	char Buf[256];
	sprintf (Buf, "An OLE error occurred:\r\nCode = %s\r\nResult = %lx.",
		 (char*) ErrName, m_hResult);
	MessageBox (NULL, Buf, "OLE Error", MB_OK);
}


static bool CountArgsInFormat (LPCTSTR Format, UINT* pArgCount)
{
	*pArgCount = 0;

	if (! Format)
		return true;

	while (*Format)
	{
		if (*Format == '&')
			Format++;

		switch (*Format)
		{
		    case 'b':
		    case 'i':
		    case 'I':
		    case 'r':
		    case 'R':
		    case 'c':
		    case 's':
		    case 'e':
		    case 'd':
		    case 'v':
		    case 'D':
		    case 'U':
			++ (*pArgCount);
			Format++;
			break;
		    case '\0':
		    default:
			return false;
		}
	}
	return true;
}

static LPCTSTR GetNextVarType (LPCTSTR Format, VARTYPE* pVarType)
{
	*pVarType = 0;
	if (*Format == '&')
	{
		*pVarType = VT_BYREF;
		Format++;
		if (!*Format)
			return NULL;
	}
	switch (*Format)
	{
	    case 'b':
		*pVarType |= VT_BOOL;
		break;
	    case 'i':
		*pVarType |= VT_I2;
		break;
	    case 'I':
		*pVarType |= VT_I4;
		break;
	    case 'r':
		*pVarType |= VT_R4;
		break;
	    case 'R':
		*pVarType |= VT_R8;
		break;
	    case 'c':
		*pVarType |= VT_CY;
		break;
	    case 's':
		*pVarType |= VT_BSTR;
		break;
	    case 'e':
		*pVarType |= VT_ERROR;
		break;
	    case 'd':
		*pVarType |= VT_DATE;
		break;
	    case 'v':
		*pVarType |= VT_VARIANT;
		break;
	    case 'U':
		*pVarType |= VT_UNKNOWN;
		break;
	    case 'D':
		*pVarType |= VT_DISPATCH;
		break;
	    case '\0':
		return NULL;	// End of Format string
	    default:
		return NULL;
	}
	return ++Format;
}

#ifndef UNICODE
char* ConvertToAnsi (OLECHAR* sUnicode)
{
	static char BufAscii[MAX_OLE_STR];
	return ConvertToAnsiBuf (sUnicode, BufAscii);
}

char* ConvertToAnsiBuf (OLECHAR* sUnicode, char* BufAscii)
{
	WideCharToMultiByte (CP_ACP, 0, sUnicode, -1, BufAscii, MAX_OLE_STR, NULL, NULL);
	return BufAscii;
}

OLECHAR* ConvertToUnicode (char* sAscii)
{
	static OLECHAR BufUnicode[MAX_OLE_STR];
	return ConvertToUnicodeBuf (sAscii, BufUnicode);
}

OLECHAR* ConvertToUnicodeBuf (char* sAscii, OLECHAR* BufUnicode)
{
	MultiByteToWideChar (CP_ACP, 0, sAscii, -1, BufUnicode, MAX_OLE_STR);
	return BufUnicode;
}
#endif

