1//
2// Class for creating OLE automation controllers.
3//
4// CreateObject() creates an automation object
5// Invoke() will call a property or method of the automation object.
6// GetProperty() returns a property
7// SetProperty() changes a property
8// Method() invokes a method
9//
10// For example, the following VB code will control Microsoft Word:
11//
12//    Private Sub Form_Load()
13//    Dim wb As Object
14//    Set wb = CreateObject("Word.Basic")
15//    wb.AppShow
16//    wb.FileNewDefault
17//    wb.Insert "This is a test"
18//    wb.FileSaveAs "c:\sample.doc)"
19//    End Sub
20//
21// A C++ automation controller that does the same can be written as follows:
22// the helper functions:
23//
24//   Void FormLoad ()
25//   {
26//       COleAutomationControl Aut;
27//       Aut.CreateObject("Word.Basic");
28//       Aut.Method ("AppShow");
29//       Aut.Method ("FileNewDefault");
30//       Aut.Method ("Insert", "s", (LPOLESTR) OLESTR ("This is a test"));
31//       Aut.Method ("FileSaveAs", "s", OLESTR ("c:\\sample.doc"));
32//   }
33//
34//
35
36#include "stdafx.h"
37#include <stdarg.h>
38#include "oleaut.h"
39
40#ifdef _DEBUG
41#define new DEBUG_NEW
42#undef THIS_FILE
43static char THIS_FILE[] = __FILE__;
44#endif
45
46
47static bool CountArgsInFormat (LPCTSTR Format, UINT* nArgs);
48static LPCTSTR GetNextVarType (LPCTSTR Format, VARTYPE* pVarType);
49
50
51COleAutomationControl::COleAutomationControl ()
52{
53	m_pDispatch = NULL;
54	m_hResult = NOERROR;
55	m_nErrArg = 0;
56	VariantInit (&m_VariantResult);
57}
58
59COleAutomationControl::~COleAutomationControl ()
60{
61	DeleteObject ();
62}
63
64void COleAutomationControl::DeleteObject ()
65{
66	if (m_pDispatch)
67	{
68		m_pDispatch->Release ();
69		m_pDispatch = NULL;
70	}
71}
72
73// Creates an instance of the Automation object and
74// obtains it's IDispatch interface.
75//
76// Parameters:
77// ProgId	  ProgID of Automation object
78//
79bool COleAutomationControl::CreateObject (char* ProgId)
80{
81	CLSID ClsId;			// CLSID of automation object
82	LPUNKNOWN pUnknown = NULL;	// IUnknown of automation object
83
84	// Retrieve CLSID from the progID that the user specified
85	LPOLESTR OleProgId = TO_OLE_STR (ProgId);
86	m_hResult = CLSIDFromProgID (OleProgId, &ClsId);
87	if (FAILED (m_hResult))
88		goto error;
89
90	// Create an instance of the automation object and ask for the
91	// IDispatch interface
92	m_hResult = CoCreateInstance (ClsId, NULL, CLSCTX_SERVER,
93			       IID_IUnknown, (void**) &pUnknown);
94	if (FAILED (m_hResult))
95		goto error;
96
97	m_hResult = pUnknown->QueryInterface (IID_IDispatch, (void**) &m_pDispatch);
98	if (FAILED (m_hResult))
99		goto error;
100
101	pUnknown->Release ();
102	return true;
103
104error:
105	if (pUnknown)
106		pUnknown->Release ();
107	if (m_pDispatch)
108		m_pDispatch->Release ();
109	return false;
110}
111
112// Return the dispatch id of a named service
113// This id can be used in subsequent calls to GetProperty (), SetProperty () and
114// Method (). This is the preferred method when performance is important.
115//
116DISPID COleAutomationControl::GetDispatchId (char* Name)
117{
118	DISPID DispatchId;
119
120	ASSERT (m_pDispatch);
121
122	// Get DISPID of property/method
123	LPOLESTR OleName = TO_OLE_STR (Name);
124	m_hResult = m_pDispatch->GetIDsOfNames (IID_NULL, &OleName, 1,
125						LOCALE_USER_DEFAULT, &DispatchId);
126	if (FAILED (m_hResult))
127		return NULL;
128	return DispatchId;
129}
130
131//  The following functions use these parameters:
132//
133// Parameters:
134//
135//  Name      Name of property or method.
136//
137//  Format    Format string that describes the variable list of parameters that
138//	      follows. The format string can contain the following characters.
139//	      & = mark the following format character as VT_BYREF
140//	      B = VT_BOOL
141//	      i = VT_I2
142//	      I = VT_I4
143//	      r = VT_R2
144//	      R = VT_R4
145//	      c = VT_CY
146//	      s = VT_BSTR (string pointer can be passed,
147//			BSTR will be allocated by this function).
148//	      e = VT_ERROR
149//	      d = VT_DATE
150//	      v = VT_VARIANT. Use this to pass data types that are not described
151//			in the format string. (For example SafeArrays).
152//	      D = VT_DISPATCH
153//	      U = VT_UNKNOWN
154//
155//  ...       Arguments of the property or method.
156//	      Arguments are described by Format.
157//
158
159bool COleAutomationControl::GetProperty (char* Name)
160{
161	return Invoke (DISPATCH_PROPERTYGET, Name, NULL, NULL);
162}
163
164bool COleAutomationControl::GetProperty (DISPID DispatchId)
165{
166	return Invoke (DISPATCH_PROPERTYGET, DispatchId, NULL, NULL);
167}
168
169bool COleAutomationControl::PutProperty (char* Name, LPCTSTR Format, ...)
170{
171	va_list ArgList;
172
173	va_start (ArgList, Format);
174	bool bRet = Invoke (DISPATCH_PROPERTYPUT, Name, Format, ArgList);
175	va_end (ArgList);
176	return bRet;
177}
178
179bool COleAutomationControl::PutProperty (DISPID DispatchId, LPCTSTR Format, ...)
180{
181	va_list ArgList;
182
183	va_start (ArgList, Format);
184	bool bRet = Invoke (DISPATCH_PROPERTYPUT, DispatchId, Format, ArgList);
185	va_end (ArgList);
186	return bRet;
187}
188
189bool COleAutomationControl::Method (char* Name, LPCTSTR Format, ...)
190{
191	va_list ArgList;
192
193	va_start (ArgList, Format);
194	bool bRet = Invoke (DISPATCH_METHOD, Name, Format, ArgList);
195	va_end (ArgList);
196	return bRet;
197}
198
199bool COleAutomationControl::Method (DISPID DispatchId, LPCTSTR Format, ...)
200{
201	va_list ArgList;
202
203	va_start (ArgList, Format);
204	bool bRet = Invoke (DISPATCH_METHOD, DispatchId, Format, ArgList);
205	va_end (ArgList);
206	return bRet;
207}
208
209bool COleAutomationControl::Invoke (WORD Flags, char* Name,
210				    LPCTSTR Format, va_list ArgList)
211{
212	DISPID DispatchId = GetDispatchId (Name);
213	if (! DispatchId)
214		return false;
215	return Invoke (Flags, DispatchId, Format, ArgList);
216}
217
218bool COleAutomationControl::Invoke (WORD Flags, DISPID DispatchId,
219				    LPCTSTR Format, va_list ArgList)
220{
221	UINT ArgCount = 0;
222	VARIANTARG* ArgVector = NULL;
223
224	ASSERT (m_pDispatch);
225
226	DISPPARAMS DispatchParams;
227	memset (&DispatchParams, 0, sizeof (DispatchParams));
228
229	// Determine number of arguments
230	if (Format)
231		CountArgsInFormat (Format, &ArgCount);
232
233	// Property puts have a named argument that represents the value that
234	// the property is being assigned.
235	DISPID DispIdNamed = DISPID_PROPERTYPUT;
236	if (Flags & DISPATCH_PROPERTYPUT)
237	{
238		if (ArgCount == 0)
239		{
240			m_hResult = ResultFromScode (E_INVALIDARG);
241			return false;
242		}
243		DispatchParams.cNamedArgs = 1;
244		DispatchParams.rgdispidNamedArgs = &DispIdNamed;
245	}
246
247	if (ArgCount)
248	{
249		// Allocate memory for all VARIANTARG parameters
250		ArgVector = (VARIANTARG*) CoTaskMemAlloc (
251				ArgCount * sizeof (VARIANTARG));
252		if (! ArgVector)
253		{
254			m_hResult = ResultFromScode (E_OUTOFMEMORY);
255			return false;
256		}
257		memset (ArgVector, 0, sizeof (VARIANTARG) * ArgCount);
258
259		// Get ready to walk vararg list
260		LPCTSTR s = Format;
261
262		VARIANTARG *p = ArgVector + ArgCount - 1;  // Params go in opposite order
263
264		for (;;)
265		{
266			VariantInit (p);
267			if (! (s = GetNextVarType (s, &p->vt)))
268				break;
269
270			if (p < ArgVector)
271			{
272				m_hResult = ResultFromScode (E_INVALIDARG);
273				goto Cleanup;
274			}
275			switch (p->vt)
276			{
277			    case VT_I2:
278				V_I2 (p) = va_arg (ArgList, short);
279				break;
280			    case VT_I4:
281				V_I4 (p) = va_arg (ArgList, long);
282				break;
283			    case VT_R4:
284				V_R4 (p) = va_arg (ArgList, float);
285				break;
286			    case VT_DATE:
287			    case VT_R8:
288				V_R8 (p) = va_arg (ArgList, double);
289				break;
290			    case VT_CY:
291				V_CY (p) = va_arg (ArgList, CY);
292				break;
293			    case VT_BSTR:
294				V_BSTR (p) = SysAllocString (va_arg (ArgList,
295								     OLECHAR*));
296				if (! p->bstrVal)
297				{
298					m_hResult = ResultFromScode (E_OUTOFMEMORY);
299					p->vt = VT_EMPTY;
300					goto Cleanup;
301				}
302				break;
303			    case VT_DISPATCH:
304				V_DISPATCH (p) = va_arg (ArgList, LPDISPATCH);
305				break;
306			    case VT_ERROR:
307				V_ERROR (p) = va_arg (ArgList, SCODE);
308				break;
309			    case VT_BOOL:
310				V_BOOL (p) = va_arg (ArgList, BOOL) ? -1 : 0;
311				break;
312			    case VT_VARIANT:
313				*p = va_arg (ArgList, VARIANTARG);
314				break;
315			    case VT_UNKNOWN:
316				V_UNKNOWN (p) = va_arg (ArgList, LPUNKNOWN);
317				break;
318
319			    case VT_I2 | VT_BYREF:
320				V_I2REF (p) = va_arg (ArgList, short*);
321				break;
322			    case VT_I4 | VT_BYREF:
323				V_I4REF (p) = va_arg (ArgList, long*);
324				break;
325			    case VT_R4 | VT_BYREF:
326				V_R4REF (p) = va_arg (ArgList, float*);
327				break;
328			    case VT_R8 | VT_BYREF:
329				V_R8REF (p) = va_arg (ArgList, double*);
330				break;
331			    case VT_DATE | VT_BYREF:
332				V_DATEREF (p) = va_arg (ArgList, DATE*);
333				break;
334			    case VT_CY | VT_BYREF:
335				V_CYREF (p) = va_arg (ArgList, CY*);
336				break;
337			    case VT_BSTR | VT_BYREF:
338				V_BSTRREF (p) = va_arg (ArgList, BSTR*);
339				break;
340			    case VT_DISPATCH | VT_BYREF:
341				V_DISPATCHREF (p) = va_arg (ArgList, LPDISPATCH*);
342				break;
343			    case VT_ERROR | VT_BYREF:
344				V_ERRORREF (p) = va_arg (ArgList, SCODE*);
345				break;
346			    case VT_BOOL | VT_BYREF:
347				{
348					BOOL* pBool = va_arg (ArgList, BOOL*);
349
350					*pBool = 0;
351					V_BOOLREF (p) = (VARIANT_BOOL*) pBool;
352				}
353				break;
354			    case VT_VARIANT | VT_BYREF:
355				V_VARIANTREF (p) = va_arg (ArgList, VARIANTARG*);
356				break;
357			    case VT_UNKNOWN | VT_BYREF:
358				V_UNKNOWNREF (p) = va_arg (ArgList, LPUNKNOWN*);
359				break;
360
361			    default:
362				{
363					m_hResult = ResultFromScode (E_INVALIDARG);
364					goto Cleanup;
365				}
366				break;
367			}
368
369			--p;	// Get ready to fill next argument
370		}
371	}
372
373	DispatchParams.cArgs = ArgCount;
374	DispatchParams.rgvarg = ArgVector;
375
376	// Initialize return variant, in case caller forgot. Caller can pass
377	// NULL if return value is not expected.
378	VariantInit (&m_VariantResult);
379
380	// Make the call
381	m_hResult = m_pDispatch->Invoke (DispatchId, IID_NULL, LOCALE_USER_DEFAULT,
382					 Flags, &DispatchParams, &m_VariantResult,
383					 &m_ExceptionInfo, &m_nErrArg);
384
385    Cleanup:
386	// Cleanup any arguments that need cleanup
387	if (ArgCount)
388	{
389		VARIANTARG* p = ArgVector;
390
391		while (ArgCount--)
392		{
393			switch (p->vt)
394			{
395			    case VT_BSTR:
396				VariantClear (p);
397				break;
398			}
399			++p;
400		}
401		CoTaskMemFree (ArgVector);
402	}
403
404	return FAILED (m_hResult) ? false : true;
405}
406
407#define CASE_SCODE(sc)  \
408	case sc: \
409	lstrcpy((char*)ErrName, (char*)#sc); \
410	break;
411
412void COleAutomationControl::ErrDiag ()
413{
414	char ErrName[200];
415
416	SCODE sc = GetScode (m_hResult);
417	switch (sc)
418	{
419	    // SCODE's defined in SCODE.H
420	    CASE_SCODE (S_OK)
421	    CASE_SCODE (S_FALSE)
422	    CASE_SCODE (E_UNEXPECTED)
423	    CASE_SCODE (E_OUTOFMEMORY)
424	    CASE_SCODE (E_INVALIDARG)
425	    CASE_SCODE (E_NOINTERFACE)
426	    CASE_SCODE (E_POINTER)
427	    CASE_SCODE (E_HANDLE)
428	    CASE_SCODE (E_ABORT)
429	    CASE_SCODE (E_FAIL)
430	    CASE_SCODE (E_ACCESSDENIED)
431
432	    // SCODE's defined in OLE2.H
433	    CASE_SCODE (OLE_E_OLEVERB)
434	    CASE_SCODE (OLE_E_ADVF)
435	    CASE_SCODE (OLE_E_ENUM_NOMORE)
436	    CASE_SCODE (OLE_E_ADVISENOTSUPPORTED)
437	    CASE_SCODE (OLE_E_NOCONNECTION)
438	    CASE_SCODE (OLE_E_NOTRUNNING)
439	    CASE_SCODE (OLE_E_NOCACHE)
440	    CASE_SCODE (OLE_E_BLANK)
441	    CASE_SCODE (OLE_E_CLASSDIFF)
442	    CASE_SCODE (OLE_E_CANT_GETMONIKER)
443	    CASE_SCODE (OLE_E_CANT_BINDTOSOURCE)
444	    CASE_SCODE (OLE_E_STATIC)
445	    CASE_SCODE (OLE_E_PROMPTSAVECANCELLED)
446	    CASE_SCODE (OLE_E_INVALIDRECT)
447	    CASE_SCODE (OLE_E_WRONGCOMPOBJ)
448	    CASE_SCODE (OLE_E_INVALIDHWND)
449	    CASE_SCODE (OLE_E_NOT_INPLACEACTIVE)
450	    CASE_SCODE (OLE_E_CANTCONVERT)
451	    CASE_SCODE (OLE_E_NOSTORAGE)
452
453	    CASE_SCODE (DV_E_FORMATETC)
454	    CASE_SCODE (DV_E_DVTARGETDEVICE)
455	    CASE_SCODE (DV_E_STGMEDIUM)
456	    CASE_SCODE (DV_E_STATDATA)
457	    CASE_SCODE (DV_E_LINDEX)
458	    CASE_SCODE (DV_E_TYMED)
459	    CASE_SCODE (DV_E_CLIPFORMAT)
460	    CASE_SCODE (DV_E_DVASPECT)
461	    CASE_SCODE (DV_E_DVTARGETDEVICE_SIZE)
462	    CASE_SCODE (DV_E_NOIVIEWOBJECT)
463
464	    CASE_SCODE (OLE_S_USEREG)
465	    CASE_SCODE (OLE_S_STATIC)
466	    CASE_SCODE (OLE_S_MAC_CLIPFORMAT)
467
468	    CASE_SCODE (CONVERT10_E_OLESTREAM_GET)
469	    CASE_SCODE (CONVERT10_E_OLESTREAM_PUT)
470	    CASE_SCODE (CONVERT10_E_OLESTREAM_FMT)
471	    CASE_SCODE (CONVERT10_E_OLESTREAM_BITMAP_TO_DIB)
472	    CASE_SCODE (CONVERT10_E_STG_FMT)
473	    CASE_SCODE (CONVERT10_E_STG_NO_STD_STREAM)
474	    CASE_SCODE (CONVERT10_E_STG_DIB_TO_BITMAP)
475	    CASE_SCODE (CONVERT10_S_NO_PRESENTATION)
476
477	    CASE_SCODE (CLIPBRD_E_CANT_OPEN)
478	    CASE_SCODE (CLIPBRD_E_CANT_EMPTY)
479	    CASE_SCODE (CLIPBRD_E_CANT_SET)
480	    CASE_SCODE (CLIPBRD_E_BAD_DATA)
481	    CASE_SCODE (CLIPBRD_E_CANT_CLOSE)
482
483	    CASE_SCODE (DRAGDROP_E_NOTREGISTERED)
484	    CASE_SCODE (DRAGDROP_E_ALREADYREGISTERED)
485	    CASE_SCODE (DRAGDROP_E_INVALIDHWND)
486	    CASE_SCODE (DRAGDROP_S_DROP)
487	    CASE_SCODE (DRAGDROP_S_CANCEL)
488	    CASE_SCODE (DRAGDROP_S_USEDEFAULTCURSORS)
489
490	    CASE_SCODE (OLEOBJ_E_NOVERBS)
491	    CASE_SCODE (OLEOBJ_E_INVALIDVERB)
492	    CASE_SCODE (OLEOBJ_S_INVALIDVERB)
493	    CASE_SCODE (OLEOBJ_S_CANNOT_DOVERB_NOW)
494	    CASE_SCODE (OLEOBJ_S_INVALIDHWND)
495	    CASE_SCODE (INPLACE_E_NOTUNDOABLE)
496	    CASE_SCODE (INPLACE_E_NOTOOLSPACE)
497	    CASE_SCODE (INPLACE_S_TRUNCATED)
498
499	    // SCODE's defined in COMPOBJ.H
500	    CASE_SCODE (CO_E_NOTINITIALIZED)
501	    CASE_SCODE (CO_E_ALREADYINITIALIZED)
502	    CASE_SCODE (CO_E_CANTDETERMINECLASS)
503	    CASE_SCODE (CO_E_CLASSSTRING)
504	    CASE_SCODE (CO_E_IIDSTRING)
505	    CASE_SCODE (CO_E_APPNOTFOUND)
506	    CASE_SCODE (CO_E_APPSINGLEUSE)
507	    CASE_SCODE (CO_E_ERRORINAPP)
508	    CASE_SCODE (CO_E_DLLNOTFOUND)
509	    CASE_SCODE (CO_E_ERRORINDLL)
510	    CASE_SCODE (CO_E_WRONGOSFORAPP)
511	    CASE_SCODE (CO_E_OBJNOTREG)
512	    CASE_SCODE (CO_E_OBJISREG)
513	    CASE_SCODE (CO_E_OBJNOTCONNECTED)
514	    CASE_SCODE (CO_E_APPDIDNTREG)
515	    CASE_SCODE (CLASS_E_NOAGGREGATION)
516	    CASE_SCODE (CLASS_E_CLASSNOTAVAILABLE)
517	    CASE_SCODE (REGDB_E_READREGDB)
518	    CASE_SCODE (REGDB_E_WRITEREGDB)
519	    CASE_SCODE (REGDB_E_KEYMISSING)
520	    CASE_SCODE (REGDB_E_INVALIDVALUE)
521	    CASE_SCODE (REGDB_E_CLASSNOTREG)
522	    CASE_SCODE (REGDB_E_IIDNOTREG)
523	    CASE_SCODE (RPC_E_CALL_REJECTED)
524	    CASE_SCODE (RPC_E_CALL_CANCELED)
525	    CASE_SCODE (RPC_E_CANTPOST_INSENDCALL)
526	    CASE_SCODE (RPC_E_CANTCALLOUT_INASYNCCALL)
527	    CASE_SCODE (RPC_E_CANTCALLOUT_INEXTERNALCALL)
528	    CASE_SCODE (RPC_E_CONNECTION_TERMINATED)
529	    CASE_SCODE (RPC_E_SERVER_DIED)
530	    CASE_SCODE (RPC_E_CLIENT_DIED)
531	    CASE_SCODE (RPC_E_INVALID_DATAPACKET)
532	    CASE_SCODE (RPC_E_CANTTRANSMIT_CALL)
533	    CASE_SCODE (RPC_E_CLIENT_CANTMARSHAL_DATA)
534	    CASE_SCODE (RPC_E_CLIENT_CANTUNMARSHAL_DATA)
535	    CASE_SCODE (RPC_E_SERVER_CANTMARSHAL_DATA)
536	    CASE_SCODE (RPC_E_SERVER_CANTUNMARSHAL_DATA)
537	    CASE_SCODE (RPC_E_INVALID_DATA)
538	    CASE_SCODE (RPC_E_INVALID_PARAMETER)
539	    CASE_SCODE (RPC_E_CANTCALLOUT_AGAIN)
540	    CASE_SCODE (RPC_E_UNEXPECTED)
541
542	    // SCODE's defined in DVOBJ.H
543	    CASE_SCODE (DATA_S_SAMEFORMATETC)
544	    CASE_SCODE (VIEW_E_DRAW)
545	    CASE_SCODE (VIEW_S_ALREADY_FROZEN)
546	    CASE_SCODE (CACHE_E_NOCACHE_UPDATED)
547	    CASE_SCODE (CACHE_S_FORMATETC_NOTSUPPORTED)
548	    CASE_SCODE (CACHE_S_SAMECACHE)
549	    CASE_SCODE (CACHE_S_SOMECACHES_NOTUPDATED)
550
551	    // SCODE's defined in STORAGE.H
552	    CASE_SCODE (STG_E_INVALIDFUNCTION)
553	    CASE_SCODE (STG_E_FILENOTFOUND)
554	    CASE_SCODE (STG_E_PATHNOTFOUND)
555	    CASE_SCODE (STG_E_TOOMANYOPENFILES)
556	    CASE_SCODE (STG_E_ACCESSDENIED)
557	    CASE_SCODE (STG_E_INVALIDHANDLE)
558	    CASE_SCODE (STG_E_INSUFFICIENTMEMORY)
559	    CASE_SCODE (STG_E_INVALIDPOINTER)
560	    CASE_SCODE (STG_E_NOMOREFILES)
561	    CASE_SCODE (STG_E_DISKISWRITEPROTECTED)
562	    CASE_SCODE (STG_E_SEEKERROR)
563	    CASE_SCODE (STG_E_WRITEFAULT)
564	    CASE_SCODE (STG_E_READFAULT)
565	    CASE_SCODE (STG_E_SHAREVIOLATION)
566	    CASE_SCODE (STG_E_LOCKVIOLATION)
567	    CASE_SCODE (STG_E_FILEALREADYEXISTS)
568	    CASE_SCODE (STG_E_INVALIDPARAMETER)
569	    CASE_SCODE (STG_E_MEDIUMFULL)
570	    CASE_SCODE (STG_E_ABNORMALAPIEXIT)
571	    CASE_SCODE (STG_E_INVALIDHEADER)
572	    CASE_SCODE (STG_E_INVALIDNAME)
573	    CASE_SCODE (STG_E_UNKNOWN)
574	    CASE_SCODE (STG_E_UNIMPLEMENTEDFUNCTION)
575	    CASE_SCODE (STG_E_INVALIDFLAG)
576	    CASE_SCODE (STG_E_INUSE)
577	    CASE_SCODE (STG_E_NOTCURRENT)
578	    CASE_SCODE (STG_E_REVERTED)
579	    CASE_SCODE (STG_E_CANTSAVE)
580	    CASE_SCODE (STG_E_OLDFORMAT)
581	    CASE_SCODE (STG_E_OLDDLL)
582	    CASE_SCODE (STG_E_SHAREREQUIRED)
583	    CASE_SCODE (STG_E_NOTFILEBASEDSTORAGE)
584	    CASE_SCODE (STG_E_EXTANTMARSHALLINGS)
585	    CASE_SCODE (STG_S_CONVERTED)
586
587	    // SCODE's defined in STORAGE.H
588	    CASE_SCODE (MK_E_CONNECTMANUALLY)
589	    CASE_SCODE (MK_E_EXCEEDEDDEADLINE)
590	    CASE_SCODE (MK_E_NEEDGENERIC)
591	    CASE_SCODE (MK_E_UNAVAILABLE)
592	    CASE_SCODE (MK_E_SYNTAX)
593	    CASE_SCODE (MK_E_NOOBJECT)
594	    CASE_SCODE (MK_E_INVALIDEXTENSION)
595	    CASE_SCODE (MK_E_INTERMEDIATEINTERFACENOTSUPPORTED)
596	    CASE_SCODE (MK_E_NOTBINDABLE)
597	    CASE_SCODE (MK_E_NOTBOUND)
598	    CASE_SCODE (MK_E_CANTOPENFILE)
599	    CASE_SCODE (MK_E_MUSTBOTHERUSER)
600	    CASE_SCODE (MK_E_NOINVERSE)
601	    CASE_SCODE (MK_E_NOSTORAGE)
602	    CASE_SCODE (MK_E_NOPREFIX)
603	    CASE_SCODE (MK_S_REDUCED_TO_SELF)
604	    CASE_SCODE (MK_S_ME)
605	    CASE_SCODE (MK_S_HIM)
606	    CASE_SCODE (MK_S_US)
607	    CASE_SCODE (MK_S_MONIKERALREADYREGISTERED)
608
609	    // SCODE's defined in DISPATCH.H
610	    CASE_SCODE (DISP_E_UNKNOWNINTERFACE)
611	    CASE_SCODE (DISP_E_MEMBERNOTFOUND)
612	    CASE_SCODE (DISP_E_PARAMNOTFOUND)
613	    CASE_SCODE (DISP_E_TYPEMISMATCH)
614	    CASE_SCODE (DISP_E_UNKNOWNNAME)
615	    CASE_SCODE (DISP_E_NONAMEDARGS)
616	    CASE_SCODE (DISP_E_BADVARTYPE)
617	    CASE_SCODE (DISP_E_EXCEPTION)
618	    CASE_SCODE (DISP_E_OVERFLOW)
619	    CASE_SCODE (DISP_E_BADINDEX)
620	    CASE_SCODE (DISP_E_UNKNOWNLCID)
621	    CASE_SCODE (DISP_E_ARRAYISLOCKED)
622	    CASE_SCODE (DISP_E_BADPARAMCOUNT)
623	    CASE_SCODE (DISP_E_PARAMNOTOPTIONAL)
624	    CASE_SCODE (DISP_E_BADCALLEE)
625	    CASE_SCODE (DISP_E_NOTACOLLECTION)
626
627	    CASE_SCODE (TYPE_E_BUFFERTOOSMALL)
628	    CASE_SCODE (TYPE_E_INVDATAREAD)
629	    CASE_SCODE (TYPE_E_UNSUPFORMAT)
630	    CASE_SCODE (TYPE_E_REGISTRYACCESS)
631	    CASE_SCODE (TYPE_E_LIBNOTREGISTERED)
632	    CASE_SCODE (TYPE_E_UNDEFINEDTYPE)
633	    CASE_SCODE (TYPE_E_QUALIFIEDNAMEDISALLOWED)
634	    CASE_SCODE (TYPE_E_INVALIDSTATE)
635	    CASE_SCODE (TYPE_E_WRONGTYPEKIND)
636	    CASE_SCODE (TYPE_E_ELEMENTNOTFOUND)
637	    CASE_SCODE (TYPE_E_AMBIGUOUSNAME)
638	    CASE_SCODE (TYPE_E_NAMECONFLICT)
639	    CASE_SCODE (TYPE_E_UNKNOWNLCID)
640	    CASE_SCODE (TYPE_E_DLLFUNCTIONNOTFOUND)
641	    CASE_SCODE (TYPE_E_BADMODULEKIND)
642	    CASE_SCODE (TYPE_E_SIZETOOBIG)
643	    CASE_SCODE (TYPE_E_DUPLICATEID)
644	    CASE_SCODE (TYPE_E_TYPEMISMATCH)
645	    CASE_SCODE (TYPE_E_OUTOFBOUNDS)
646	    CASE_SCODE (TYPE_E_IOERROR)
647	    CASE_SCODE (TYPE_E_CANTCREATETMPFILE)
648	    CASE_SCODE (TYPE_E_CANTLOADLIBRARY)
649	    CASE_SCODE (TYPE_E_INCONSISTENTPROPFUNCS)
650	    CASE_SCODE (TYPE_E_CIRCULARTYPE)
651
652	    default:
653		lstrcpy (ErrName, "UNKNOWN SCODE");
654	}
655
656	char Buf[256];
657	sprintf (Buf, "An OLE error occured:\r\nCode = %s\r\nResult = %lx.",
658		 (char*) ErrName, m_hResult);
659	MessageBox (NULL, Buf, "OLE Error", MB_OK);
660}
661
662
663static bool CountArgsInFormat (LPCTSTR Format, UINT* pArgCount)
664{
665	*pArgCount = 0;
666
667	if (! Format)
668		return true;
669
670	while (*Format)
671	{
672		if (*Format == '&')
673			Format++;
674
675		switch (*Format)
676		{
677		    case 'b':
678		    case 'i':
679		    case 'I':
680		    case 'r':
681		    case 'R':
682		    case 'c':
683		    case 's':
684		    case 'e':
685		    case 'd':
686		    case 'v':
687		    case 'D':
688		    case 'U':
689			++ (*pArgCount);
690			Format++;
691			break;
692		    case '\0':
693		    default:
694			return false;
695		}
696	}
697	return true;
698}
699
700static LPCTSTR GetNextVarType (LPCTSTR Format, VARTYPE* pVarType)
701{
702	*pVarType = 0;
703	if (*Format == '&')
704	{
705		*pVarType = VT_BYREF;
706		Format++;
707		if (!*Format)
708			return NULL;
709	}
710	switch (*Format)
711	{
712	    case 'b':
713		*pVarType |= VT_BOOL;
714		break;
715	    case 'i':
716		*pVarType |= VT_I2;
717		break;
718	    case 'I':
719		*pVarType |= VT_I4;
720		break;
721	    case 'r':
722		*pVarType |= VT_R4;
723		break;
724	    case 'R':
725		*pVarType |= VT_R8;
726		break;
727	    case 'c':
728		*pVarType |= VT_CY;
729		break;
730	    case 's':
731		*pVarType |= VT_BSTR;
732		break;
733	    case 'e':
734		*pVarType |= VT_ERROR;
735		break;
736	    case 'd':
737		*pVarType |= VT_DATE;
738		break;
739	    case 'v':
740		*pVarType |= VT_VARIANT;
741		break;
742	    case 'U':
743		*pVarType |= VT_UNKNOWN;
744		break;
745	    case 'D':
746		*pVarType |= VT_DISPATCH;
747		break;
748	    case '\0':
749		return NULL;	// End of Format string
750	    default:
751		return NULL;
752	}
753	return ++Format;
754}
755
756#ifndef UNICODE
757char* ConvertToAnsi (OLECHAR* sUnicode)
758{
759	static char BufAscii[MAX_OLE_STR];
760	return ConvertToAnsiBuf (sUnicode, BufAscii);
761}
762
763char* ConvertToAnsiBuf (OLECHAR* sUnicode, char* BufAscii)
764{
765	WideCharToMultiByte (CP_ACP, 0, sUnicode, -1, BufAscii, MAX_OLE_STR, NULL, NULL);
766	return BufAscii;
767}
768
769OLECHAR* ConvertToUnicode (char* sAscii)
770{
771	static OLECHAR BufUnicode[MAX_OLE_STR];
772	return ConvertToUnicodeBuf (sAscii, BufUnicode);
773}
774
775OLECHAR* ConvertToUnicodeBuf (char* sAscii, OLECHAR* BufUnicode)
776{
777	MultiByteToWideChar (CP_ACP, 0, sAscii, -1, BufUnicode, MAX_OLE_STR);
778	return BufUnicode;
779}
780#endif
781
782