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