1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/ole/automtn.cpp
3 // Purpose: OLE automation utilities
4 // Author: Julian Smart
8 // Copyright: (c) 1998, Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
15 #if defined(__BORLANDC__)
19 // With Borland C++, all samples crash if this is compiled in.
20 #if (defined(__BORLANDC__) && (__BORLANDC__ < 0x520)) || defined(__CYGWIN10__)
21 #undef wxUSE_OLE_AUTOMATION
22 #define wxUSE_OLE_AUTOMATION 0
30 #define _FORCENAMELESSUNION
31 #include "wx/msw/private.h"
32 #include "wx/msw/ole/oleutils.h"
33 #include "wx/msw/ole/automtn.h"
36 #include "wx/msw/wince/time.h"
54 #include "wx/datetime.h"
55 #endif // wxUSE_DATETIME
57 #if wxUSE_OLE_AUTOMATION
59 #include <wx/vector.h>
61 // Report an OLE error when calling the specified method to the user via wxLog.
63 ShowException(const wxString
& member
,
65 EXCEPINFO
*pexcep
= NULL
,
66 unsigned int uiArgErr
= 0);
70 wxAutomationObject::wxAutomationObject(WXIDISPATCH
* dispatchPtr
)
72 m_dispatchPtr
= dispatchPtr
;
73 m_lcid
= LOCALE_SYSTEM_DEFAULT
;
76 wxAutomationObject::~wxAutomationObject()
80 ((IDispatch
*)m_dispatchPtr
)->Release();
88 // A simple helper that ensures that VARIANT is destroyed on scope exit.
89 struct wxOleVariantArg
: VARIANTARG
91 wxOleVariantArg() { VariantInit(this); }
92 ~wxOleVariantArg() { VariantClear(this); }
95 } // anonymous namespace
98 #define INVOKEARG(i) (args ? args[i] : *(ptrArgs[i]))
100 // For Put/Get, no named arguments are allowed.
101 // WARNING: if args contain IDispatches, their reference count will be decreased
102 // by one after Invoke() returns!
103 bool wxAutomationObject::Invoke(const wxString
& member
, int action
,
104 wxVariant
& retValue
, int noArgs
, wxVariant args
[], const wxVariant
* ptrArgs
[]) const
109 int ch
= member
.Find('.');
112 // Use dot notation to get the next object
113 wxString
member2(member
.Left((size_t) ch
));
114 wxString
rest(member
.Right(member
.length() - ch
- 1));
115 wxAutomationObject obj
;
116 if (!GetObject(obj
, member2
))
118 return obj
.Invoke(rest
, action
, retValue
, noArgs
, args
, ptrArgs
);
121 wxOleVariantArg vReturn
;
122 wxOleVariantArg
* vReturnPtr
= & vReturn
;
124 // Find number of names args
125 int namedArgCount
= 0;
127 for (i
= 0; i
< noArgs
; i
++)
129 if ( !INVOKEARG(i
).GetName().empty() )
135 int namedArgStringCount
= namedArgCount
+ 1;
136 wxVector
<wxBasicString
> argNames(namedArgStringCount
, wxString());
137 argNames
[0] = member
;
139 // Note that arguments are specified in reverse order
140 // (all totally logical; hey, we're dealing with OLE here.)
143 for (i
= 0; i
< namedArgCount
; i
++)
145 if ( !INVOKEARG(i
).GetName().empty() )
147 argNames
[(namedArgCount
-j
)] = INVOKEARG(i
).GetName();
152 // + 1 for the member name, + 1 again in case we're a 'put'
153 wxVector
<DISPID
> dispIds(namedArgCount
+ 2);
156 DISPPARAMS dispparams
;
157 unsigned int uiArgErr
;
159 // Get the IDs for the member and its arguments. GetIDsOfNames expects the
160 // member name as the first name, followed by argument names (if any).
161 hr
= ((IDispatch
*)m_dispatchPtr
)->GetIDsOfNames(IID_NULL
,
162 // We rely on the fact that wxBasicString is
163 // just BSTR with some methods here.
164 reinterpret_cast<BSTR
*>(&argNames
[0]),
165 1 + namedArgCount
, m_lcid
, &dispIds
[0]);
168 ShowException(member
, hr
);
172 // if doing a property put(ref), we need to adjust the first argument to have a
173 // named arg of DISPID_PROPERTYPUT.
174 if (action
& (DISPATCH_PROPERTYPUT
| DISPATCH_PROPERTYPUTREF
))
177 dispIds
[1] = DISPID_PROPERTYPUT
;
181 // Convert the wxVariants to VARIANTARGs
182 wxVector
<wxOleVariantArg
> oleArgs(noArgs
);
183 for (i
= 0; i
< noArgs
; i
++)
185 // Again, reverse args
186 if (!wxConvertVariantToOle(INVOKEARG((noArgs
-1) - i
), oleArgs
[i
]))
190 dispparams
.rgdispidNamedArgs
= &dispIds
[0] + 1;
191 dispparams
.rgvarg
= oleArgs
.empty() ? NULL
: &oleArgs
[0];
192 dispparams
.cArgs
= noArgs
;
193 dispparams
.cNamedArgs
= namedArgCount
;
198 hr
= ((IDispatch
*)m_dispatchPtr
)->Invoke(dispIds
[0], IID_NULL
, m_lcid
,
199 (WORD
)action
, &dispparams
, vReturnPtr
, &excep
, &uiArgErr
);
203 // display the exception information if appropriate:
204 ShowException(member
, hr
, &excep
, uiArgErr
);
206 // free exception structure information
207 SysFreeString(excep
.bstrSource
);
208 SysFreeString(excep
.bstrDescription
);
209 SysFreeString(excep
.bstrHelpFile
);
217 // Convert result to wxVariant form
218 if (!wxConvertOleToVariant(vReturn
, retValue
))
220 // Mustn't release the dispatch pointer
221 if (vReturn
.vt
== VT_DISPATCH
)
223 vReturn
.pdispVal
= NULL
;
225 // Mustn't free the SAFEARRAY if it is contained in the retValue
226 if ((vReturn
.vt
& VT_ARRAY
) &&
227 retValue
.GetType() == wxS("safearray"))
229 vReturn
.parray
= NULL
;
236 // Invoke a member function
237 wxVariant
wxAutomationObject::CallMethod(const wxString
& member
, int noArgs
, wxVariant args
[])
239 wxVariant retVariant
;
240 if (!Invoke(member
, DISPATCH_METHOD
, retVariant
, noArgs
, args
))
242 retVariant
.MakeNull();
247 wxVariant
wxAutomationObject::CallMethodArray(const wxString
& member
, int noArgs
, const wxVariant
**args
)
249 wxVariant retVariant
;
250 if (!Invoke(member
, DISPATCH_METHOD
, retVariant
, noArgs
, NULL
, args
))
252 retVariant
.MakeNull();
257 wxVariant
wxAutomationObject::CallMethod(const wxString
& member
,
258 const wxVariant
& arg1
, const wxVariant
& arg2
,
259 const wxVariant
& arg3
, const wxVariant
& arg4
,
260 const wxVariant
& arg5
, const wxVariant
& arg6
)
262 const wxVariant
** args
= new const wxVariant
*[6];
294 wxVariant retVariant
;
295 if (!Invoke(member
, DISPATCH_METHOD
, retVariant
, i
, NULL
, args
))
297 retVariant
.MakeNull();
304 wxVariant
wxAutomationObject::GetPropertyArray(const wxString
& property
, int noArgs
, const wxVariant
**args
) const
306 wxVariant retVariant
;
307 if (!Invoke(property
, DISPATCH_PROPERTYGET
, retVariant
, noArgs
, NULL
, args
))
309 retVariant
.MakeNull();
313 wxVariant
wxAutomationObject::GetProperty(const wxString
& property
, int noArgs
, wxVariant args
[]) const
315 wxVariant retVariant
;
316 if (!Invoke(property
, DISPATCH_PROPERTYGET
, retVariant
, noArgs
, args
))
318 retVariant
.MakeNull();
323 wxVariant
wxAutomationObject::GetProperty(const wxString
& property
,
324 const wxVariant
& arg1
, const wxVariant
& arg2
,
325 const wxVariant
& arg3
, const wxVariant
& arg4
,
326 const wxVariant
& arg5
, const wxVariant
& arg6
)
328 const wxVariant
** args
= new const wxVariant
*[6];
360 wxVariant retVariant
;
361 if (!Invoke(property
, DISPATCH_PROPERTYGET
, retVariant
, i
, NULL
, args
))
363 retVariant
.MakeNull();
369 bool wxAutomationObject::PutProperty(const wxString
& property
, int noArgs
, wxVariant args
[])
371 wxVariant retVariant
;
372 if (!Invoke(property
, DISPATCH_PROPERTYPUT
, retVariant
, noArgs
, args
))
379 bool wxAutomationObject::PutPropertyArray(const wxString
& property
, int noArgs
, const wxVariant
**args
)
381 wxVariant retVariant
;
382 if (!Invoke(property
, DISPATCH_PROPERTYPUT
, retVariant
, noArgs
, NULL
, args
))
389 bool wxAutomationObject::PutProperty(const wxString
& property
,
390 const wxVariant
& arg1
, const wxVariant
& arg2
,
391 const wxVariant
& arg3
, const wxVariant
& arg4
,
392 const wxVariant
& arg5
, const wxVariant
& arg6
)
394 const wxVariant
** args
= new const wxVariant
*[6];
426 wxVariant retVariant
;
427 bool ret
= Invoke(property
, DISPATCH_PROPERTYPUT
, retVariant
, i
, NULL
, args
);
433 // Uses DISPATCH_PROPERTYGET
434 // and returns a dispatch pointer. The calling code should call Release
435 // on the pointer, though this could be implicit by constructing an wxAutomationObject
436 // with it and letting the destructor call Release.
437 WXIDISPATCH
* wxAutomationObject::GetDispatchProperty(const wxString
& property
, int noArgs
, wxVariant args
[]) const
439 wxVariant retVariant
;
440 if (Invoke(property
, DISPATCH_PROPERTYGET
, retVariant
, noArgs
, args
))
442 if (retVariant
.GetType() == wxT("void*"))
444 return (WXIDISPATCH
*) retVariant
.GetVoidPtr();
451 // Uses DISPATCH_PROPERTYGET
452 // and returns a dispatch pointer. The calling code should call Release
453 // on the pointer, though this could be implicit by constructing an wxAutomationObject
454 // with it and letting the destructor call Release.
455 WXIDISPATCH
* wxAutomationObject::GetDispatchProperty(const wxString
& property
, int noArgs
, const wxVariant
**args
) const
457 wxVariant retVariant
;
458 if (Invoke(property
, DISPATCH_PROPERTYGET
, retVariant
, noArgs
, NULL
, args
))
460 if (retVariant
.GetType() == wxT("void*"))
462 return (WXIDISPATCH
*) retVariant
.GetVoidPtr();
470 // A way of initialising another wxAutomationObject with a dispatch object
471 bool wxAutomationObject::GetObject(wxAutomationObject
& obj
, const wxString
& property
, int noArgs
, wxVariant args
[]) const
473 WXIDISPATCH
* dispatch
= GetDispatchProperty(property
, noArgs
, args
);
476 obj
.SetDispatchPtr(dispatch
);
477 obj
.SetLCID(GetLCID());
484 // A way of initialising another wxAutomationObject with a dispatch object
485 bool wxAutomationObject::GetObject(wxAutomationObject
& obj
, const wxString
& property
, int noArgs
, const wxVariant
**args
) const
487 WXIDISPATCH
* dispatch
= GetDispatchProperty(property
, noArgs
, args
);
490 obj
.SetDispatchPtr(dispatch
);
491 obj
.SetLCID(GetLCID());
501 HRESULT
wxCLSIDFromProgID(const wxString
& progId
, CLSID
& clsId
)
503 HRESULT hr
= CLSIDFromProgID(wxBasicString(progId
), &clsId
);
506 wxLogSysError(hr
, _("Failed to find CLSID of \"%s\""), progId
);
511 void *DoCreateInstance(const wxString
& progId
, const CLSID
& clsId
)
513 // get the server IDispatch interface
515 // NB: using CLSCTX_INPROC_HANDLER results in failure when getting
516 // Automation interface for Microsoft Office applications so don't use
517 // CLSCTX_ALL which includes it
518 void *pDispatch
= NULL
;
519 HRESULT hr
= CoCreateInstance(clsId
, NULL
, CLSCTX_SERVER
,
520 IID_IDispatch
, &pDispatch
);
523 wxLogSysError(hr
, _("Failed to create an instance of \"%s\""), progId
);
530 } // anonymous namespace
532 // Get a dispatch pointer from the current object associated
534 bool wxAutomationObject::GetInstance(const wxString
& progId
, int flags
) const
540 HRESULT hr
= wxCLSIDFromProgID(progId
, clsId
);
544 IUnknown
*pUnk
= NULL
;
545 hr
= GetActiveObject(clsId
, NULL
, &pUnk
);
548 if ( flags
& wxAutomationInstance_CreateIfNeeded
)
550 const_cast<wxAutomationObject
*>(this)->
551 m_dispatchPtr
= DoCreateInstance(progId
, clsId
);
557 // Log an error except if we're supposed to fail silently when the
558 // error is that no current instance exists.
559 if ( hr
!= MK_E_UNAVAILABLE
||
560 !(flags
& wxAutomationInstance_SilentIfNone
) )
563 _("Cannot get an active instance of \"%s\""),
571 hr
= pUnk
->QueryInterface(IID_IDispatch
, (LPVOID
*) &m_dispatchPtr
);
575 _("Failed to get OLE automation interface for \"%s\""),
583 // Get a dispatch pointer from a new object associated
584 // with the given ProgID
585 bool wxAutomationObject::CreateInstance(const wxString
& progId
) const
591 HRESULT hr
= wxCLSIDFromProgID(progId
, clsId
);
595 const_cast<wxAutomationObject
*>(this)->
596 m_dispatchPtr
= DoCreateInstance(progId
, clsId
);
598 return m_dispatchPtr
!= NULL
;
601 LCID
wxAutomationObject::GetLCID() const
606 void wxAutomationObject::SetLCID(LCID lcid
)
612 ShowException(const wxString
& member
,
615 unsigned int uiArgErr
)
618 switch (GetScode(hr
))
620 case DISP_E_UNKNOWNNAME
:
621 message
= _("Unknown name or named argument.");
624 case DISP_E_BADPARAMCOUNT
:
625 message
= _("Incorrect number of arguments.");
628 case DISP_E_EXCEPTION
:
631 if ( pexcep
->bstrDescription
)
632 message
<< pexcep
->bstrDescription
<< wxS(" ");
633 message
+= wxString::Format(wxS("error code %u"), pexcep
->wCode
);
637 message
= _("Unknown exception");
641 case DISP_E_MEMBERNOTFOUND
:
642 message
= _("Method or property not found.");
645 case DISP_E_OVERFLOW
:
646 message
= _("Overflow while coercing argument values.");
649 case DISP_E_NONAMEDARGS
:
650 message
= _("Object implementation does not support named arguments.");
653 case DISP_E_UNKNOWNLCID
:
654 message
= _("The locale ID is unknown.");
657 case DISP_E_PARAMNOTOPTIONAL
:
658 message
= _("Missing a required parameter.");
661 case DISP_E_PARAMNOTFOUND
:
662 message
.Printf(_("Argument %u not found."), uiArgErr
);
665 case DISP_E_TYPEMISMATCH
:
666 message
.Printf(_("Type mismatch in argument %u."), uiArgErr
);
669 case ERROR_FILE_NOT_FOUND
:
670 message
= _("The system cannot find the file specified.");
673 case REGDB_E_CLASSNOTREG
:
674 message
= _("Class not registered.");
678 message
.Printf(_("Unknown error %08x"), hr
);
682 wxLogError(_("OLE Automation error in %s: %s"), member
, message
);
685 #endif // wxUSE_OLE_AUTOMATION