1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/ole/automtn.cpp
3 // Purpose: OLE automation utilities
4 // Author: Julian Smart
7 // Copyright: (c) 1998, Julian Smart
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
14 #if defined(__BORLANDC__)
18 // With Borland C++, all samples crash if this is compiled in.
19 #if (defined(__BORLANDC__) && (__BORLANDC__ < 0x520)) || defined(__CYGWIN10__)
20 #undef wxUSE_OLE_AUTOMATION
21 #define wxUSE_OLE_AUTOMATION 0
29 #define _FORCENAMELESSUNION
30 #include "wx/msw/private.h"
31 #include "wx/msw/ole/oleutils.h"
32 #include "wx/msw/ole/automtn.h"
35 #include "wx/msw/wince/time.h"
53 #include "wx/datetime.h"
54 #endif // wxUSE_DATETIME
56 #if wxUSE_OLE_AUTOMATION
58 #include <wx/vector.h>
60 // Report an OLE error when calling the specified method to the user via wxLog.
62 ShowException(const wxString
& member
,
64 EXCEPINFO
*pexcep
= NULL
,
65 unsigned int uiArgErr
= 0);
69 wxAutomationObject::wxAutomationObject(WXIDISPATCH
* dispatchPtr
)
71 m_dispatchPtr
= dispatchPtr
;
72 m_lcid
= LOCALE_SYSTEM_DEFAULT
;
75 wxAutomationObject::~wxAutomationObject()
79 ((IDispatch
*)m_dispatchPtr
)->Release();
87 // A simple helper that ensures that VARIANT is destroyed on scope exit.
88 struct wxOleVariantArg
: VARIANTARG
90 wxOleVariantArg() { VariantInit(this); }
91 ~wxOleVariantArg() { VariantClear(this); }
94 } // anonymous namespace
97 #define INVOKEARG(i) (args ? args[i] : *(ptrArgs[i]))
99 // For Put/Get, no named arguments are allowed.
100 // WARNING: if args contain IDispatches, their reference count will be decreased
101 // by one after Invoke() returns!
102 bool wxAutomationObject::Invoke(const wxString
& member
, int action
,
103 wxVariant
& retValue
, int noArgs
, wxVariant args
[], const wxVariant
* ptrArgs
[]) const
108 int ch
= member
.Find('.');
111 // Use dot notation to get the next object
112 wxString
member2(member
.Left((size_t) ch
));
113 wxString
rest(member
.Right(member
.length() - ch
- 1));
114 wxAutomationObject obj
;
115 if (!GetObject(obj
, member2
))
117 return obj
.Invoke(rest
, action
, retValue
, noArgs
, args
, ptrArgs
);
120 wxOleVariantArg vReturn
;
121 wxOleVariantArg
* vReturnPtr
= & vReturn
;
123 // Find number of names args
124 int namedArgCount
= 0;
126 for (i
= 0; i
< noArgs
; i
++)
128 if ( !INVOKEARG(i
).GetName().empty() )
134 int namedArgStringCount
= namedArgCount
+ 1;
135 wxVector
<wxBasicString
> argNames(namedArgStringCount
, wxString());
136 argNames
[0] = member
;
138 // Note that arguments are specified in reverse order
139 // (all totally logical; hey, we're dealing with OLE here.)
142 for (i
= 0; i
< namedArgCount
; i
++)
144 if ( !INVOKEARG(i
).GetName().empty() )
146 argNames
[(namedArgCount
-j
)] = INVOKEARG(i
).GetName();
151 // + 1 for the member name, + 1 again in case we're a 'put'
152 wxVector
<DISPID
> dispIds(namedArgCount
+ 2);
155 DISPPARAMS dispparams
;
156 unsigned int uiArgErr
;
158 // Get the IDs for the member and its arguments. GetIDsOfNames expects the
159 // member name as the first name, followed by argument names (if any).
160 hr
= ((IDispatch
*)m_dispatchPtr
)->GetIDsOfNames(IID_NULL
,
161 // We rely on the fact that wxBasicString is
162 // just BSTR with some methods here.
163 reinterpret_cast<BSTR
*>(&argNames
[0]),
164 1 + namedArgCount
, m_lcid
, &dispIds
[0]);
167 ShowException(member
, hr
);
171 // if doing a property put(ref), we need to adjust the first argument to have a
172 // named arg of DISPID_PROPERTYPUT.
173 if (action
& (DISPATCH_PROPERTYPUT
| DISPATCH_PROPERTYPUTREF
))
176 dispIds
[1] = DISPID_PROPERTYPUT
;
180 // Convert the wxVariants to VARIANTARGs
181 wxVector
<wxOleVariantArg
> oleArgs(noArgs
);
182 for (i
= 0; i
< noArgs
; i
++)
184 // Again, reverse args
185 if (!wxConvertVariantToOle(INVOKEARG((noArgs
-1) - i
), oleArgs
[i
]))
189 dispparams
.rgdispidNamedArgs
= &dispIds
[0] + 1;
190 dispparams
.rgvarg
= oleArgs
.empty() ? NULL
: &oleArgs
[0];
191 dispparams
.cArgs
= noArgs
;
192 dispparams
.cNamedArgs
= namedArgCount
;
197 hr
= ((IDispatch
*)m_dispatchPtr
)->Invoke(dispIds
[0], IID_NULL
, m_lcid
,
198 (WORD
)action
, &dispparams
, vReturnPtr
, &excep
, &uiArgErr
);
202 // display the exception information if appropriate:
203 ShowException(member
, hr
, &excep
, uiArgErr
);
205 // free exception structure information
206 SysFreeString(excep
.bstrSource
);
207 SysFreeString(excep
.bstrDescription
);
208 SysFreeString(excep
.bstrHelpFile
);
216 // Convert result to wxVariant form
217 if (!wxConvertOleToVariant(vReturn
, retValue
))
219 // Mustn't release the dispatch pointer
220 if (vReturn
.vt
== VT_DISPATCH
)
222 vReturn
.pdispVal
= NULL
;
224 // Mustn't free the SAFEARRAY if it is contained in the retValue
225 if ((vReturn
.vt
& VT_ARRAY
) &&
226 retValue
.GetType() == wxS("safearray"))
228 vReturn
.parray
= NULL
;
235 // Invoke a member function
236 wxVariant
wxAutomationObject::CallMethod(const wxString
& member
, int noArgs
, wxVariant args
[])
238 wxVariant retVariant
;
239 if (!Invoke(member
, DISPATCH_METHOD
, retVariant
, noArgs
, args
))
241 retVariant
.MakeNull();
246 wxVariant
wxAutomationObject::CallMethodArray(const wxString
& member
, int noArgs
, const wxVariant
**args
)
248 wxVariant retVariant
;
249 if (!Invoke(member
, DISPATCH_METHOD
, retVariant
, noArgs
, NULL
, args
))
251 retVariant
.MakeNull();
256 wxVariant
wxAutomationObject::CallMethod(const wxString
& member
,
257 const wxVariant
& arg1
, const wxVariant
& arg2
,
258 const wxVariant
& arg3
, const wxVariant
& arg4
,
259 const wxVariant
& arg5
, const wxVariant
& arg6
)
261 const wxVariant
** args
= new const wxVariant
*[6];
293 wxVariant retVariant
;
294 if (!Invoke(member
, DISPATCH_METHOD
, retVariant
, i
, NULL
, args
))
296 retVariant
.MakeNull();
303 wxVariant
wxAutomationObject::GetPropertyArray(const wxString
& property
, int noArgs
, const wxVariant
**args
) const
305 wxVariant retVariant
;
306 if (!Invoke(property
, DISPATCH_PROPERTYGET
, retVariant
, noArgs
, NULL
, args
))
308 retVariant
.MakeNull();
312 wxVariant
wxAutomationObject::GetProperty(const wxString
& property
, int noArgs
, wxVariant args
[]) const
314 wxVariant retVariant
;
315 if (!Invoke(property
, DISPATCH_PROPERTYGET
, retVariant
, noArgs
, args
))
317 retVariant
.MakeNull();
322 wxVariant
wxAutomationObject::GetProperty(const wxString
& property
,
323 const wxVariant
& arg1
, const wxVariant
& arg2
,
324 const wxVariant
& arg3
, const wxVariant
& arg4
,
325 const wxVariant
& arg5
, const wxVariant
& arg6
)
327 const wxVariant
** args
= new const wxVariant
*[6];
359 wxVariant retVariant
;
360 if (!Invoke(property
, DISPATCH_PROPERTYGET
, retVariant
, i
, NULL
, args
))
362 retVariant
.MakeNull();
368 bool wxAutomationObject::PutProperty(const wxString
& property
, int noArgs
, wxVariant args
[])
370 wxVariant retVariant
;
371 if (!Invoke(property
, DISPATCH_PROPERTYPUT
, retVariant
, noArgs
, args
))
378 bool wxAutomationObject::PutPropertyArray(const wxString
& property
, int noArgs
, const wxVariant
**args
)
380 wxVariant retVariant
;
381 if (!Invoke(property
, DISPATCH_PROPERTYPUT
, retVariant
, noArgs
, NULL
, args
))
388 bool wxAutomationObject::PutProperty(const wxString
& property
,
389 const wxVariant
& arg1
, const wxVariant
& arg2
,
390 const wxVariant
& arg3
, const wxVariant
& arg4
,
391 const wxVariant
& arg5
, const wxVariant
& arg6
)
393 const wxVariant
** args
= new const wxVariant
*[6];
425 wxVariant retVariant
;
426 bool ret
= Invoke(property
, DISPATCH_PROPERTYPUT
, retVariant
, i
, NULL
, args
);
432 // Uses DISPATCH_PROPERTYGET
433 // and returns a dispatch pointer. The calling code should call Release
434 // on the pointer, though this could be implicit by constructing an wxAutomationObject
435 // with it and letting the destructor call Release.
436 WXIDISPATCH
* wxAutomationObject::GetDispatchProperty(const wxString
& property
, int noArgs
, wxVariant args
[]) const
438 wxVariant retVariant
;
439 if (Invoke(property
, DISPATCH_PROPERTYGET
, retVariant
, noArgs
, args
))
441 if (retVariant
.GetType() == wxT("void*"))
443 return (WXIDISPATCH
*) retVariant
.GetVoidPtr();
450 // Uses DISPATCH_PROPERTYGET
451 // and returns a dispatch pointer. The calling code should call Release
452 // on the pointer, though this could be implicit by constructing an wxAutomationObject
453 // with it and letting the destructor call Release.
454 WXIDISPATCH
* wxAutomationObject::GetDispatchProperty(const wxString
& property
, int noArgs
, const wxVariant
**args
) const
456 wxVariant retVariant
;
457 if (Invoke(property
, DISPATCH_PROPERTYGET
, retVariant
, noArgs
, NULL
, args
))
459 if (retVariant
.GetType() == wxT("void*"))
461 return (WXIDISPATCH
*) retVariant
.GetVoidPtr();
469 // A way of initialising another wxAutomationObject with a dispatch object
470 bool wxAutomationObject::GetObject(wxAutomationObject
& obj
, const wxString
& property
, int noArgs
, wxVariant args
[]) const
472 WXIDISPATCH
* dispatch
= GetDispatchProperty(property
, noArgs
, args
);
475 obj
.SetDispatchPtr(dispatch
);
476 obj
.SetLCID(GetLCID());
483 // A way of initialising another wxAutomationObject with a dispatch object
484 bool wxAutomationObject::GetObject(wxAutomationObject
& obj
, const wxString
& property
, int noArgs
, const wxVariant
**args
) const
486 WXIDISPATCH
* dispatch
= GetDispatchProperty(property
, noArgs
, args
);
489 obj
.SetDispatchPtr(dispatch
);
490 obj
.SetLCID(GetLCID());
500 HRESULT
wxCLSIDFromProgID(const wxString
& progId
, CLSID
& clsId
)
502 HRESULT hr
= CLSIDFromProgID(wxBasicString(progId
), &clsId
);
505 wxLogSysError(hr
, _("Failed to find CLSID of \"%s\""), progId
);
510 void *DoCreateInstance(const wxString
& progId
, const CLSID
& clsId
)
512 // get the server IDispatch interface
514 // NB: using CLSCTX_INPROC_HANDLER results in failure when getting
515 // Automation interface for Microsoft Office applications so don't use
516 // CLSCTX_ALL which includes it
517 void *pDispatch
= NULL
;
518 HRESULT hr
= CoCreateInstance(clsId
, NULL
, CLSCTX_SERVER
,
519 IID_IDispatch
, &pDispatch
);
522 wxLogSysError(hr
, _("Failed to create an instance of \"%s\""), progId
);
529 } // anonymous namespace
531 // Get a dispatch pointer from the current object associated
533 bool wxAutomationObject::GetInstance(const wxString
& progId
, int flags
) const
539 HRESULT hr
= wxCLSIDFromProgID(progId
, clsId
);
543 IUnknown
*pUnk
= NULL
;
544 hr
= GetActiveObject(clsId
, NULL
, &pUnk
);
547 if ( flags
& wxAutomationInstance_CreateIfNeeded
)
549 const_cast<wxAutomationObject
*>(this)->
550 m_dispatchPtr
= DoCreateInstance(progId
, clsId
);
556 // Log an error except if we're supposed to fail silently when the
557 // error is that no current instance exists.
558 if ( hr
!= MK_E_UNAVAILABLE
||
559 !(flags
& wxAutomationInstance_SilentIfNone
) )
562 _("Cannot get an active instance of \"%s\""),
570 hr
= pUnk
->QueryInterface(IID_IDispatch
, (LPVOID
*) &m_dispatchPtr
);
574 _("Failed to get OLE automation interface for \"%s\""),
582 // Get a dispatch pointer from a new object associated
583 // with the given ProgID
584 bool wxAutomationObject::CreateInstance(const wxString
& progId
) const
590 HRESULT hr
= wxCLSIDFromProgID(progId
, clsId
);
594 const_cast<wxAutomationObject
*>(this)->
595 m_dispatchPtr
= DoCreateInstance(progId
, clsId
);
597 return m_dispatchPtr
!= NULL
;
600 LCID
wxAutomationObject::GetLCID() const
605 void wxAutomationObject::SetLCID(LCID lcid
)
611 ShowException(const wxString
& member
,
614 unsigned int uiArgErr
)
617 switch (GetScode(hr
))
619 case DISP_E_UNKNOWNNAME
:
620 message
= _("Unknown name or named argument.");
623 case DISP_E_BADPARAMCOUNT
:
624 message
= _("Incorrect number of arguments.");
627 case DISP_E_EXCEPTION
:
630 if ( pexcep
->bstrDescription
)
631 message
<< pexcep
->bstrDescription
<< wxS(" ");
632 message
+= wxString::Format(wxS("error code %u"), pexcep
->wCode
);
636 message
= _("Unknown exception");
640 case DISP_E_MEMBERNOTFOUND
:
641 message
= _("Method or property not found.");
644 case DISP_E_OVERFLOW
:
645 message
= _("Overflow while coercing argument values.");
648 case DISP_E_NONAMEDARGS
:
649 message
= _("Object implementation does not support named arguments.");
652 case DISP_E_UNKNOWNLCID
:
653 message
= _("The locale ID is unknown.");
656 case DISP_E_PARAMNOTOPTIONAL
:
657 message
= _("Missing a required parameter.");
660 case DISP_E_PARAMNOTFOUND
:
661 message
.Printf(_("Argument %u not found."), uiArgErr
);
664 case DISP_E_TYPEMISMATCH
:
665 message
.Printf(_("Type mismatch in argument %u."), uiArgErr
);
668 case ERROR_FILE_NOT_FOUND
:
669 message
= _("The system cannot find the file specified.");
672 case REGDB_E_CLASSNOTREG
:
673 message
= _("Class not registered.");
677 message
.Printf(_("Unknown error %08x"), hr
);
681 wxLogError(_("OLE Automation error in %s: %s"), member
, message
);
684 #endif // wxUSE_OLE_AUTOMATION