1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/ole/automtn.cpp
3 // Purpose: OLE automation utilities
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 11/6/98
7 // Copyright: (c) 1998, Julian Smart
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
13
14
15 #ifndef WX_PRECOMP
16 #include "wx/log.h"
17 #include "wx/math.h"
18 #endif
19
20 #ifndef _FORCENAMELESSUNION
21 #define _FORCENAMELESSUNION
22 #endif
23
24 #include "wx/msw/private.h"
25 #include "wx/msw/ole/oleutils.h"
26 #include "wx/msw/ole/automtn.h"
27
28 #include <time.h>
29
30 #include <wtypes.h>
31 #include <unknwn.h>
32
33 #include <ole2.h>
34 #define _huge
35
36 #include <ole2ver.h>
37
38 #include <oleauto.h>
39
40 #if wxUSE_DATETIME
41 #include "wx/datetime.h"
42 #endif // wxUSE_DATETIME
43
44 #if wxUSE_OLE_AUTOMATION
45
46 #include <wx/vector.h>
47
48 // Report an OLE error when calling the specified method to the user via wxLog.
49 static void
50 ShowException(const wxString& member,
51 HRESULT hr,
52 EXCEPINFO *pexcep = NULL,
53 unsigned int uiArgErr = 0);
54
55 // wxAutomationObject
56
wxAutomationObject(WXIDISPATCH * dispatchPtr)57 wxAutomationObject::wxAutomationObject(WXIDISPATCH* dispatchPtr)
58 {
59 m_dispatchPtr = dispatchPtr;
60 m_lcid = LOCALE_SYSTEM_DEFAULT;
61 m_convertVariantFlags = wxOleConvertVariant_Default;
62 }
63
~wxAutomationObject()64 wxAutomationObject::~wxAutomationObject()
65 {
66 if (m_dispatchPtr)
67 {
68 ((IDispatch*)m_dispatchPtr)->Release();
69 m_dispatchPtr = NULL;
70 }
71 }
72
73 namespace
74 {
75
76 // A simple helper that ensures that VARIANT is destroyed on scope exit.
77 struct wxOleVariantArg : VARIANTARG
78 {
wxOleVariantArg__anon338fe87d0111::wxOleVariantArg79 wxOleVariantArg() { VariantInit(this); }
~wxOleVariantArg__anon338fe87d0111::wxOleVariantArg80 ~wxOleVariantArg() { VariantClear(this); }
81 };
82
83 } // anonymous namespace
84
85
86 #define INVOKEARG(i) (args ? args[i] : *(ptrArgs[i]))
87
88 // For Put/Get, no named arguments are allowed.
89 // WARNING: if args contain IDispatches, their reference count will be decreased
90 // by one after Invoke() returns!
Invoke(const wxString & member,int action,wxVariant & retValue,int noArgs,wxVariant args[],const wxVariant * ptrArgs[]) const91 bool wxAutomationObject::Invoke(const wxString& member, int action,
92 wxVariant& retValue, int noArgs, wxVariant args[], const wxVariant* ptrArgs[]) const
93 {
94 if (!m_dispatchPtr)
95 return false;
96
97 int ch = member.Find('.');
98 if (ch != -1)
99 {
100 // Use dot notation to get the next object
101 wxString member2(member.Left((size_t) ch));
102 wxString rest(member.Right(member.length() - ch - 1));
103 wxAutomationObject obj;
104 if (!GetObject(obj, member2))
105 return false;
106 return obj.Invoke(rest, action, retValue, noArgs, args, ptrArgs);
107 }
108
109 wxOleVariantArg vReturn;
110 wxOleVariantArg* vReturnPtr = & vReturn;
111
112 // Find number of names args
113 int namedArgCount = 0;
114 int i;
115 for (i = 0; i < noArgs; i++)
116 {
117 if ( !INVOKEARG(i).GetName().empty() )
118 {
119 namedArgCount ++;
120 }
121 }
122
123 int namedArgStringCount = namedArgCount + 1;
124 wxVector<wxBasicString> argNames(namedArgStringCount);
125 argNames[0].AssignFromString(member);
126
127 // Note that arguments are specified in reverse order
128 // (all totally logical; hey, we're dealing with OLE here.)
129
130 int j = 0;
131 for (i = 0; i < namedArgCount; i++)
132 {
133 if ( !INVOKEARG(i).GetName().empty() )
134 {
135 argNames[(namedArgCount-j)].AssignFromString(INVOKEARG(i).GetName());
136 j ++;
137 }
138 }
139
140 // + 1 for the member name, + 1 again in case we're a 'put'
141 wxVector<DISPID> dispIds(namedArgCount + 2);
142
143 HRESULT hr;
144 DISPPARAMS dispparams;
145 unsigned int uiArgErr;
146
147 // Get the IDs for the member and its arguments. GetIDsOfNames expects the
148 // member name as the first name, followed by argument names (if any).
149 hr = ((IDispatch*)m_dispatchPtr)->GetIDsOfNames(IID_NULL,
150 // We rely on the fact that wxBasicString is
151 // just BSTR with some methods here.
152 reinterpret_cast<BSTR *>(&argNames[0]),
153 1 + namedArgCount, m_lcid, &dispIds[0]);
154 if (FAILED(hr))
155 {
156 ShowException(member, hr);
157 return false;
158 }
159
160 // if doing a property put(ref), we need to adjust the first argument to have a
161 // named arg of DISPID_PROPERTYPUT.
162 if (action & (DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF))
163 {
164 namedArgCount = 1;
165 dispIds[1] = DISPID_PROPERTYPUT;
166 vReturnPtr = NULL;
167 }
168
169 // Convert the wxVariants to VARIANTARGs
170 wxVector<wxOleVariantArg> oleArgs(noArgs);
171 for (i = 0; i < noArgs; i++)
172 {
173 // Again, reverse args
174 if (!wxConvertVariantToOle(INVOKEARG((noArgs-1) - i), oleArgs[i]))
175 return false;
176 }
177
178 dispparams.rgdispidNamedArgs = &dispIds[0] + 1;
179 dispparams.rgvarg = oleArgs.empty() ? NULL : &oleArgs[0];
180 dispparams.cArgs = noArgs;
181 dispparams.cNamedArgs = namedArgCount;
182
183 EXCEPINFO excep;
184 wxZeroMemory(excep);
185
186 hr = ((IDispatch*)m_dispatchPtr)->Invoke(dispIds[0], IID_NULL, m_lcid,
187 (WORD)action, &dispparams, vReturnPtr, &excep, &uiArgErr);
188
189 if (FAILED(hr))
190 {
191 // display the exception information if appropriate:
192 ShowException(member, hr, &excep, uiArgErr);
193
194 // free exception structure information
195 SysFreeString(excep.bstrSource);
196 SysFreeString(excep.bstrDescription);
197 SysFreeString(excep.bstrHelpFile);
198
199 return false;
200 }
201 else
202 {
203 if (vReturnPtr)
204 {
205 // Convert result to wxVariant form
206 if (!wxConvertOleToVariant(vReturn, retValue, m_convertVariantFlags))
207 return false;
208 // Mustn't release the dispatch pointer
209 if (vReturn.vt == VT_DISPATCH)
210 {
211 vReturn.pdispVal = NULL;
212 }
213 // Mustn't free the SAFEARRAY if it is contained in the retValue
214 if ((vReturn.vt & VT_ARRAY) &&
215 retValue.GetType() == wxS("safearray"))
216 {
217 vReturn.parray = NULL;
218 }
219 }
220 }
221 return true;
222 }
223
224 // Invoke a member function
CallMethod(const wxString & member,int noArgs,wxVariant args[])225 wxVariant wxAutomationObject::CallMethod(const wxString& member, int noArgs, wxVariant args[])
226 {
227 wxVariant retVariant;
228 if (!Invoke(member, DISPATCH_METHOD, retVariant, noArgs, args))
229 {
230 retVariant.MakeNull();
231 }
232 return retVariant;
233 }
234
CallMethodArray(const wxString & member,int noArgs,const wxVariant ** args)235 wxVariant wxAutomationObject::CallMethodArray(const wxString& member, int noArgs, const wxVariant **args)
236 {
237 wxVariant retVariant;
238 if (!Invoke(member, DISPATCH_METHOD, retVariant, noArgs, NULL, args))
239 {
240 retVariant.MakeNull();
241 }
242 return retVariant;
243 }
244
CallMethod(const wxString & member,const wxVariant & arg1,const wxVariant & arg2,const wxVariant & arg3,const wxVariant & arg4,const wxVariant & arg5,const wxVariant & arg6)245 wxVariant wxAutomationObject::CallMethod(const wxString& member,
246 const wxVariant& arg1, const wxVariant& arg2,
247 const wxVariant& arg3, const wxVariant& arg4,
248 const wxVariant& arg5, const wxVariant& arg6)
249 {
250 const wxVariant** args = new const wxVariant*[6];
251 int i = 0;
252 if (!arg1.IsNull())
253 {
254 args[i] = & arg1;
255 i ++;
256 }
257 if (!arg2.IsNull())
258 {
259 args[i] = & arg2;
260 i ++;
261 }
262 if (!arg3.IsNull())
263 {
264 args[i] = & arg3;
265 i ++;
266 }
267 if (!arg4.IsNull())
268 {
269 args[i] = & arg4;
270 i ++;
271 }
272 if (!arg5.IsNull())
273 {
274 args[i] = & arg5;
275 i ++;
276 }
277 if (!arg6.IsNull())
278 {
279 args[i] = & arg6;
280 i ++;
281 }
282 wxVariant retVariant;
283 if (!Invoke(member, DISPATCH_METHOD, retVariant, i, NULL, args))
284 {
285 retVariant.MakeNull();
286 }
287 delete[] args;
288 return retVariant;
289 }
290
291 // Get/Set property
GetPropertyArray(const wxString & property,int noArgs,const wxVariant ** args) const292 wxVariant wxAutomationObject::GetPropertyArray(const wxString& property, int noArgs, const wxVariant **args) const
293 {
294 wxVariant retVariant;
295 if (!Invoke(property, DISPATCH_PROPERTYGET, retVariant, noArgs, NULL, args))
296 {
297 retVariant.MakeNull();
298 }
299 return retVariant;
300 }
GetProperty(const wxString & property,int noArgs,wxVariant args[]) const301 wxVariant wxAutomationObject::GetProperty(const wxString& property, int noArgs, wxVariant args[]) const
302 {
303 wxVariant retVariant;
304 if (!Invoke(property, DISPATCH_PROPERTYGET, retVariant, noArgs, args))
305 {
306 retVariant.MakeNull();
307 }
308 return retVariant;
309 }
310
GetProperty(const wxString & property,const wxVariant & arg1,const wxVariant & arg2,const wxVariant & arg3,const wxVariant & arg4,const wxVariant & arg5,const wxVariant & arg6)311 wxVariant wxAutomationObject::GetProperty(const wxString& property,
312 const wxVariant& arg1, const wxVariant& arg2,
313 const wxVariant& arg3, const wxVariant& arg4,
314 const wxVariant& arg5, const wxVariant& arg6)
315 {
316 const wxVariant** args = new const wxVariant*[6];
317 int i = 0;
318 if (!arg1.IsNull())
319 {
320 args[i] = & arg1;
321 i ++;
322 }
323 if (!arg2.IsNull())
324 {
325 args[i] = & arg2;
326 i ++;
327 }
328 if (!arg3.IsNull())
329 {
330 args[i] = & arg3;
331 i ++;
332 }
333 if (!arg4.IsNull())
334 {
335 args[i] = & arg4;
336 i ++;
337 }
338 if (!arg5.IsNull())
339 {
340 args[i] = & arg5;
341 i ++;
342 }
343 if (!arg6.IsNull())
344 {
345 args[i] = & arg6;
346 i ++;
347 }
348 wxVariant retVariant;
349 if (!Invoke(property, DISPATCH_PROPERTYGET, retVariant, i, NULL, args))
350 {
351 retVariant.MakeNull();
352 }
353 delete[] args;
354 return retVariant;
355 }
356
PutProperty(const wxString & property,int noArgs,wxVariant args[])357 bool wxAutomationObject::PutProperty(const wxString& property, int noArgs, wxVariant args[])
358 {
359 wxVariant retVariant;
360 if (!Invoke(property, DISPATCH_PROPERTYPUT, retVariant, noArgs, args))
361 {
362 return false;
363 }
364 return true;
365 }
366
PutPropertyArray(const wxString & property,int noArgs,const wxVariant ** args)367 bool wxAutomationObject::PutPropertyArray(const wxString& property, int noArgs, const wxVariant **args)
368 {
369 wxVariant retVariant;
370 if (!Invoke(property, DISPATCH_PROPERTYPUT, retVariant, noArgs, NULL, args))
371 {
372 return false;
373 }
374 return true;
375 }
376
PutProperty(const wxString & property,const wxVariant & arg1,const wxVariant & arg2,const wxVariant & arg3,const wxVariant & arg4,const wxVariant & arg5,const wxVariant & arg6)377 bool wxAutomationObject::PutProperty(const wxString& property,
378 const wxVariant& arg1, const wxVariant& arg2,
379 const wxVariant& arg3, const wxVariant& arg4,
380 const wxVariant& arg5, const wxVariant& arg6)
381 {
382 const wxVariant** args = new const wxVariant*[6];
383 int i = 0;
384 if (!arg1.IsNull())
385 {
386 args[i] = & arg1;
387 i ++;
388 }
389 if (!arg2.IsNull())
390 {
391 args[i] = & arg2;
392 i ++;
393 }
394 if (!arg3.IsNull())
395 {
396 args[i] = & arg3;
397 i ++;
398 }
399 if (!arg4.IsNull())
400 {
401 args[i] = & arg4;
402 i ++;
403 }
404 if (!arg5.IsNull())
405 {
406 args[i] = & arg5;
407 i ++;
408 }
409 if (!arg6.IsNull())
410 {
411 args[i] = & arg6;
412 i ++;
413 }
414 wxVariant retVariant;
415 bool ret = Invoke(property, DISPATCH_PROPERTYPUT, retVariant, i, NULL, args);
416 delete[] args;
417 return ret;
418 }
419
420
421 // Uses DISPATCH_PROPERTYGET
422 // and returns a dispatch pointer. The calling code should call Release
423 // on the pointer, though this could be implicit by constructing an wxAutomationObject
424 // with it and letting the destructor call Release.
GetDispatchProperty(const wxString & property,int noArgs,wxVariant args[]) const425 WXIDISPATCH* wxAutomationObject::GetDispatchProperty(const wxString& property, int noArgs, wxVariant args[]) const
426 {
427 wxVariant retVariant;
428 if (Invoke(property, DISPATCH_PROPERTYGET, retVariant, noArgs, args))
429 {
430 if (retVariant.GetType() == wxT("void*"))
431 {
432 return (WXIDISPATCH*) retVariant.GetVoidPtr();
433 }
434 }
435
436 return NULL;
437 }
438
439 // Uses DISPATCH_PROPERTYGET
440 // and returns a dispatch pointer. The calling code should call Release
441 // on the pointer, though this could be implicit by constructing an wxAutomationObject
442 // with it and letting the destructor call Release.
GetDispatchProperty(const wxString & property,int noArgs,const wxVariant ** args) const443 WXIDISPATCH* wxAutomationObject::GetDispatchProperty(const wxString& property, int noArgs, const wxVariant **args) const
444 {
445 wxVariant retVariant;
446 if (Invoke(property, DISPATCH_PROPERTYGET, retVariant, noArgs, NULL, args))
447 {
448 if (retVariant.GetType() == wxT("void*"))
449 {
450 return (WXIDISPATCH*) retVariant.GetVoidPtr();
451 }
452 }
453
454 return NULL;
455 }
456
457
458 // A way of initialising another wxAutomationObject with a dispatch object
GetObject(wxAutomationObject & obj,const wxString & property,int noArgs,wxVariant args[]) const459 bool wxAutomationObject::GetObject(wxAutomationObject& obj, const wxString& property, int noArgs, wxVariant args[]) const
460 {
461 WXIDISPATCH* dispatch = GetDispatchProperty(property, noArgs, args);
462 if (dispatch)
463 {
464 obj.SetDispatchPtr(dispatch);
465 obj.SetLCID(GetLCID());
466 obj.SetConvertVariantFlags(GetConvertVariantFlags());
467 return true;
468 }
469 else
470 return false;
471 }
472
473 // A way of initialising another wxAutomationObject with a dispatch object
GetObject(wxAutomationObject & obj,const wxString & property,int noArgs,const wxVariant ** args) const474 bool wxAutomationObject::GetObject(wxAutomationObject& obj, const wxString& property, int noArgs, const wxVariant **args) const
475 {
476 WXIDISPATCH* dispatch = GetDispatchProperty(property, noArgs, args);
477 if (dispatch)
478 {
479 obj.SetDispatchPtr(dispatch);
480 obj.SetLCID(GetLCID());
481 obj.SetConvertVariantFlags(GetConvertVariantFlags());
482 return true;
483 }
484 else
485 return false;
486 }
487
488 namespace
489 {
490
wxCLSIDFromProgID(const wxString & progId,CLSID & clsId)491 HRESULT wxCLSIDFromProgID(const wxString& progId, CLSID& clsId)
492 {
493 HRESULT hr = CLSIDFromProgID(wxBasicString(progId), &clsId);
494 if ( FAILED(hr) )
495 {
496 wxLogSysError(hr, _("Failed to find CLSID of \"%s\""), progId);
497 }
498 return hr;
499 }
500
DoCreateInstance(const wxString & progId,const CLSID & clsId)501 void *DoCreateInstance(const wxString& progId, const CLSID& clsId)
502 {
503 // get the server IDispatch interface
504 //
505 // NB: using CLSCTX_INPROC_HANDLER results in failure when getting
506 // Automation interface for Microsoft Office applications so don't use
507 // CLSCTX_ALL which includes it
508 void *pDispatch = NULL;
509 HRESULT hr = CoCreateInstance(clsId, NULL, CLSCTX_SERVER,
510 IID_IDispatch, &pDispatch);
511 if (FAILED(hr))
512 {
513 wxLogSysError(hr, _("Failed to create an instance of \"%s\""), progId);
514 return NULL;
515 }
516
517 return pDispatch;
518 }
519
520 } // anonymous namespace
521
522 // Get a dispatch pointer from the current object associated
523 // with a ProgID
GetInstance(const wxString & progId,int flags) const524 bool wxAutomationObject::GetInstance(const wxString& progId, int flags) const
525 {
526 if (m_dispatchPtr)
527 return false;
528
529 CLSID clsId;
530 HRESULT hr = wxCLSIDFromProgID(progId, clsId);
531 if (FAILED(hr))
532 return false;
533
534 IUnknown *pUnk = NULL;
535 hr = GetActiveObject(clsId, NULL, &pUnk);
536 if (FAILED(hr))
537 {
538 if ( flags & wxAutomationInstance_CreateIfNeeded )
539 {
540 const_cast<wxAutomationObject *>(this)->
541 m_dispatchPtr = DoCreateInstance(progId, clsId);
542 if ( m_dispatchPtr )
543 return true;
544 }
545 else
546 {
547 // Log an error except if we're supposed to fail silently when the
548 // error is that no current instance exists.
549 if ( hr != MK_E_UNAVAILABLE ||
550 !(flags & wxAutomationInstance_SilentIfNone) )
551 {
552 wxLogSysError(hr,
553 _("Cannot get an active instance of \"%s\""),
554 progId);
555 }
556 }
557
558 return false;
559 }
560
561 hr = pUnk->QueryInterface(IID_IDispatch, const_cast<void**>(&m_dispatchPtr));
562 if (FAILED(hr))
563 {
564 wxLogSysError(hr,
565 _("Failed to get OLE automation interface for \"%s\""),
566 progId);
567 return false;
568 }
569
570 return true;
571 }
572
573 // Get a dispatch pointer from a new object associated
574 // with the given ProgID
CreateInstance(const wxString & progId) const575 bool wxAutomationObject::CreateInstance(const wxString& progId) const
576 {
577 if (m_dispatchPtr)
578 return false;
579
580 CLSID clsId;
581 HRESULT hr = wxCLSIDFromProgID(progId, clsId);
582 if (FAILED(hr))
583 return false;
584
585 const_cast<wxAutomationObject *>(this)->
586 m_dispatchPtr = DoCreateInstance(progId, clsId);
587
588 return m_dispatchPtr != NULL;
589 }
590
GetLCID() const591 WXLCID wxAutomationObject::GetLCID() const
592 {
593 return m_lcid;
594 }
595
SetLCID(WXLCID lcid)596 void wxAutomationObject::SetLCID(WXLCID lcid)
597 {
598 m_lcid = lcid;
599 }
600
GetConvertVariantFlags() const601 long wxAutomationObject::GetConvertVariantFlags() const
602 {
603 return m_convertVariantFlags;
604 }
605
SetConvertVariantFlags(long flags)606 void wxAutomationObject::SetConvertVariantFlags(long flags)
607 {
608 m_convertVariantFlags = flags;
609 }
610
611
612 static void
ShowException(const wxString & member,HRESULT hr,EXCEPINFO * pexcep,unsigned int uiArgErr)613 ShowException(const wxString& member,
614 HRESULT hr,
615 EXCEPINFO *pexcep,
616 unsigned int uiArgErr)
617 {
618 wxString message;
619 switch (GetScode(hr))
620 {
621 case DISP_E_UNKNOWNNAME:
622 message = _("Unknown name or named argument.");
623 break;
624
625 case DISP_E_BADPARAMCOUNT:
626 message = _("Incorrect number of arguments.");
627 break;
628
629 case DISP_E_EXCEPTION:
630 if ( pexcep )
631 {
632 if ( pexcep->bstrDescription )
633 message << pexcep->bstrDescription << wxS(" ");
634 message += wxString::Format(wxS("error code %u"), pexcep->wCode);
635 }
636 else
637 {
638 message = _("Unknown exception");
639 }
640 break;
641
642 case DISP_E_MEMBERNOTFOUND:
643 message = _("Method or property not found.");
644 break;
645
646 case DISP_E_OVERFLOW:
647 message = _("Overflow while coercing argument values.");
648 break;
649
650 case DISP_E_NONAMEDARGS:
651 message = _("Object implementation does not support named arguments.");
652 break;
653
654 case DISP_E_UNKNOWNLCID:
655 message = _("The locale ID is unknown.");
656 break;
657
658 case DISP_E_PARAMNOTOPTIONAL:
659 message = _("Missing a required parameter.");
660 break;
661
662 case DISP_E_PARAMNOTFOUND:
663 message.Printf(_("Argument %u not found."), uiArgErr);
664 break;
665
666 case DISP_E_TYPEMISMATCH:
667 message.Printf(_("Type mismatch in argument %u."), uiArgErr);
668 break;
669
670 case ERROR_FILE_NOT_FOUND:
671 message = _("The system cannot find the file specified.");
672 break;
673
674 case REGDB_E_CLASSNOTREG:
675 message = _("Class not registered.");
676 break;
677
678 default:
679 message.Printf(_("Unknown error %08x"), hr);
680 break;
681 }
682
683 wxLogError(_("OLE Automation error in %s: %s"), member, message);
684 }
685
686 #endif // wxUSE_OLE_AUTOMATION
687