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