source: trunk/src/activeqt/container/qaxbase.cpp@ 618

Last change on this file since 618 was 561, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.1 sources.

File size: 144.1 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the ActiveQt framework of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:BSD$
10** You may use this file under the terms of the BSD license as follows:
11**
12** "Redistribution and use in source and binary forms, with or without
13** modification, are permitted provided that the following conditions are
14** met:
15** * Redistributions of source code must retain the above copyright
16** notice, this list of conditions and the following disclaimer.
17** * Redistributions in binary form must reproduce the above copyright
18** notice, this list of conditions and the following disclaimer in
19** the documentation and/or other materials provided with the
20** distribution.
21** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
22** the names of its contributors may be used to endorse or promote
23** products derived from this software without specific prior written
24** permission.
25**
26** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
37** $QT_END_LICENSE$
38**
39****************************************************************************/
40
41//#define QAX_NO_CLASSINFO
42
43#define QT_CHECK_STATE
44
45#include "qaxobject.h"
46
47#ifndef QT_NO_WIN_ACTIVEQT
48
49#include <qfile.h>
50#include <qwidget.h>
51
52#include <quuid.h>
53#include <qhash.h>
54#include <qset.h>
55#include <qpair.h>
56#include <qmetaobject.h>
57#include <qsettings.h>
58
59#ifndef QT_NO_THREAD
60# include <qmutex.h>
61#endif
62
63#include <qt_windows.h>
64#include <ocidl.h>
65#include <ctype.h>
66
67#include "../shared/qaxtypes.h"
68
69QT_BEGIN_NAMESPACE
70
71/*
72 \internal
73 \class QAxMetaObject
74
75 \brief The QAxMetaObject class stores extended information
76*/
77struct QAxMetaObject : public QMetaObject
78{
79 QAxMetaObject()
80 {
81 d.data = 0;
82 d.stringdata = 0;
83 }
84 ~QAxMetaObject()
85 {
86 delete [] (int*)d.data;
87 delete [] (char*)d.stringdata;
88 }
89
90 int numParameter(const QByteArray &prototype);
91 QByteArray paramType(const QByteArray &signature, int index, bool *out = 0);
92 QByteArray propertyType(const QByteArray &propertyName);
93 void parsePrototype(const QByteArray &prototype);
94 DISPID dispIDofName(const QByteArray &name, IDispatch *disp);
95
96private:
97 friend class MetaObjectGenerator;
98 // save information about QAxEventSink connections, and connect when found in cache
99 QList<QUuid> connectionInterfaces;
100 // DISPID -> signal name
101 QMap< QUuid, QMap<DISPID, QByteArray> > sigs;
102 // DISPID -> property changed signal name
103 QMap< QUuid, QMap<DISPID, QByteArray> > propsigs;
104 // DISPID -> property name
105 QMap< QUuid, QMap<DISPID, QByteArray> > props;
106
107 // Prototype -> member info
108 QHash<QByteArray, QList<QByteArray> > memberInfo;
109 QMap<QByteArray, QByteArray> realPrototype;
110
111 // DISPID cache
112 QHash<QByteArray, DISPID> dispIDs;
113};
114
115void QAxMetaObject::parsePrototype(const QByteArray &prototype)
116{
117 QByteArray realProto = realPrototype.value(prototype, prototype);
118 QByteArray parameters = realProto.mid(realProto.indexOf('(') + 1);
119 parameters.truncate(parameters.length() - 1);
120
121 if (parameters.isEmpty()) {
122 memberInfo.insert(prototype, QList<QByteArray>());
123 } else {
124 QList<QByteArray> plist = parameters.split(',');
125 memberInfo.insert(prototype, plist);
126 }
127}
128
129inline QByteArray QAxMetaObject::propertyType(const QByteArray &propertyName)
130{
131 return realPrototype.value(propertyName);
132}
133
134int QAxMetaObject::numParameter(const QByteArray &prototype)
135{
136 if (!memberInfo.contains(prototype))
137 parsePrototype(prototype);
138
139 return memberInfo.value(prototype).count();
140}
141
142QByteArray QAxMetaObject::paramType(const QByteArray &prototype, int index, bool *out)
143{
144 if (!memberInfo.contains(prototype))
145 parsePrototype(prototype);
146
147 if (out)
148 *out = false;
149
150 QList<QByteArray> plist = memberInfo.value(prototype);
151 if (index > plist.count() - 1)
152 return QByteArray();
153
154 QByteArray param(plist.at(index));
155 if (param.isEmpty())
156 return QByteArray();
157
158 bool byRef = param.endsWith('&') || param.endsWith("**");
159 if (byRef) {
160 param.truncate(param.length() - 1);
161 if (out)
162 *out = true;
163 }
164
165 return param;
166}
167
168inline DISPID QAxMetaObject::dispIDofName(const QByteArray &name, IDispatch *disp)
169{
170 DISPID dispid = dispIDs.value(name, DISPID_UNKNOWN);
171 if (dispid == DISPID_UNKNOWN) {
172 // get the Dispatch ID from the object
173 QString unicodeName = QLatin1String(name);
174 OLECHAR *names = (wchar_t*)unicodeName.utf16();
175 disp->GetIDsOfNames(IID_NULL, &names, 1, LOCALE_USER_DEFAULT, &dispid);
176 if (dispid != DISPID_UNKNOWN)
177 dispIDs.insert(name, dispid);
178 }
179 return dispid;
180}
181
182
183static QHash<QString, QAxMetaObject*> mo_cache;
184static QHash<QUuid, QMap<QByteArray, QList<QPair<QByteArray, int> > > > enum_cache;
185static int mo_cache_ref = 0;
186static QMutex cache_mutex;
187
188
189static const char *const type_conversion[][2] =
190{
191 { "float", "double"},
192 { "short", "int"},
193 { "char", "int"},
194 { "QList<int>", "QVariantList" },
195 { "QList<uint>", "QVariantList" },
196 { "QList<double>", "QVariantList" },
197 { "QList<bool>", "QVariantList" },
198 { "QList<QDateTime>", "QVariantList" },
199 { "QList<qlonglong>", "QVariantList" },
200 { 0, 0 }
201};
202
203/*
204 \internal
205 \class QAxEventSink
206
207 \brief The QAxEventSink class implements the event sink for all
208 IConnectionPoints implemented in the COM object.
209*/
210
211class QAxEventSink : public IDispatch, public IPropertyNotifySink
212{
213public:
214 QAxEventSink(QAxBase *com)
215 : cpoint(0), ciid(IID_NULL), combase(com), ref(1)
216 {}
217 virtual ~QAxEventSink()
218 {
219 Q_ASSERT(!cpoint);
220 }
221
222 QUuid connectionInterface() const
223 {
224 return ciid;
225 }
226 QMap<DISPID, QByteArray> signalMap() const
227 {
228 return sigs;
229 }
230 QMap<DISPID, QByteArray> propertyMap() const
231 {
232 return props;
233 }
234 QMap<DISPID, QByteArray> propSignalMap() const
235 {
236 return propsigs;
237 }
238
239 // add a connection
240 void advise(IConnectionPoint *cp, IID iid)
241 {
242 cpoint = cp;
243 cpoint->AddRef();
244 ciid = iid;
245 cpoint->Advise((IUnknown*)(IDispatch*)this, &cookie);
246 }
247
248 // disconnect from all connection points
249 void unadvise()
250 {
251 combase = 0;
252 if (cpoint) {
253 cpoint->Unadvise(cookie);
254 cpoint->Release();
255 cpoint = 0;
256 }
257 }
258
259 void addSignal(DISPID memid, const char *name)
260 {
261 QByteArray signalname = name;
262 int pi = signalname.indexOf('(');
263 int i = 0;
264 while (type_conversion[i][0]) {
265 int ti = pi;
266 int len = int(strlen(type_conversion[i][0]));
267 while ((ti = signalname.indexOf(type_conversion[i][0], ti)) != -1)
268 signalname.replace(ti, len, type_conversion[i][1]);
269 ++i;
270 }
271
272 sigs.insert(memid, signalname);
273 QMap<DISPID,QByteArray>::ConstIterator it;
274 DISPID id = -1;
275 for (it = propsigs.constBegin(); it!= propsigs.constEnd(); ++it) {
276 if (it.value() == signalname) {
277 id = it.key();
278 break;
279 }
280 }
281 if (id != -1)
282 propsigs.remove(id);
283 }
284 void addProperty(DISPID propid, const char *name, const char *signal)
285 {
286 props.insert(propid, name);
287 propsigs.insert(propid, signal);
288 }
289
290 // IUnknown
291 unsigned long __stdcall AddRef()
292 {
293 return ref++;
294 }
295 unsigned long __stdcall Release()
296 {
297 if (!--ref) {
298 delete this;
299 return 0;
300 }
301 return ref;
302 }
303 HRESULT __stdcall QueryInterface(REFIID riid, void **ppvObject)
304 {
305 *ppvObject = 0;
306 if (riid == IID_IUnknown)
307 *ppvObject = (IUnknown*)(IDispatch*)this;
308 else if (riid == IID_IDispatch)
309 *ppvObject = (IDispatch*)this;
310 else if (riid == IID_IPropertyNotifySink)
311 *ppvObject = (IPropertyNotifySink*)this;
312 else if (ciid == riid)
313 *ppvObject = (IDispatch*)this;
314 else
315 return E_NOINTERFACE;
316
317 AddRef();
318 return S_OK;
319 }
320
321 // IDispatch
322 HRESULT __stdcall GetTypeInfoCount(unsigned int *) { return E_NOTIMPL; }
323 HRESULT __stdcall GetTypeInfo(UINT, LCID, ITypeInfo **) { return E_NOTIMPL; }
324 HRESULT __stdcall GetIDsOfNames(const _GUID &, wchar_t **, unsigned int, unsigned long, long *) { return E_NOTIMPL; }
325
326 HRESULT __stdcall Invoke(DISPID dispIdMember,
327 REFIID riid,
328 LCID,
329 WORD wFlags,
330 DISPPARAMS *pDispParams,
331 VARIANT*,
332 EXCEPINFO*,
333 UINT*)
334 {
335 // verify input
336 if (riid != IID_NULL)
337 return DISP_E_UNKNOWNINTERFACE;
338 if (!(wFlags & DISPATCH_METHOD))
339 return DISP_E_MEMBERNOTFOUND;
340 if (!combase)
341 return E_UNEXPECTED;
342
343 QByteArray signame = sigs.value(dispIdMember);
344 if (signame.isEmpty())
345 return DISP_E_MEMBERNOTFOUND;
346
347 QObject *qobject = combase->qObject();
348 if (qobject->signalsBlocked())
349 return S_OK;
350
351 QAxMetaObject *axmeta = combase->internalMetaObject();
352 const QMetaObject *meta = combase->metaObject();
353
354 int index = -1;
355 // emit the generic signal "as is"
356 if (signalHasReceivers(qobject, "signal(QString,int,void*)")) {
357 index = meta->indexOfSignal("signal(QString,int,void*)");
358 Q_ASSERT(index != -1);
359
360 QString nameString = QLatin1String(signame);
361 void *argv[] = {0, &nameString, &pDispParams->cArgs, &pDispParams->rgvarg};
362 combase->qt_metacall(QMetaObject::InvokeMetaMethod, index, argv);
363 }
364
365 HRESULT hres = S_OK;
366
367 // get the signal information from the metaobject
368 index = -1;
369 if (signalHasReceivers(qobject, signame)) {
370 index = meta->indexOfSignal(signame);
371 Q_ASSERT(index != -1);
372 const QMetaMethod signal = meta->method(index);
373 Q_ASSERT(signal.methodType() == QMetaMethod::Signal);
374 Q_ASSERT(signame == signal.signature());
375 // verify parameter count
376 int pcount = axmeta->numParameter(signame);
377 int argcount = pDispParams->cArgs;
378 if (pcount > argcount)
379 return DISP_E_PARAMNOTOPTIONAL;
380 else if (pcount < argcount)
381 return DISP_E_BADPARAMCOUNT;
382
383 // setup parameters (no return values in signals)
384 bool ok = true;
385 void *static_argv[QAX_NUM_PARAMS + 1];
386 void *static_argv_pointer[QAX_NUM_PARAMS + 1];
387 QVariant static_varp[QAX_NUM_PARAMS + 1];
388
389 void **argv = 0;
390 void **argv_pointer = 0; // in case we need an additional level of indirection
391 QVariant *varp = 0;
392
393 if (pcount) {
394 if (pcount <= QAX_NUM_PARAMS) {
395 argv = static_argv;
396 argv_pointer = static_argv_pointer;
397 varp = static_varp;
398 } else {
399 argv = new void*[pcount + 1];
400 argv_pointer = new void*[pcount + 1];
401 varp = new QVariant[pcount + 1];
402 }
403
404 argv[0] = 0;
405 argv_pointer[0] = 0;
406 }
407
408 int p;
409 for (p = 0; p < pcount && ok; ++p) {
410 // map the VARIANT to the void*
411 QByteArray ptype = axmeta->paramType(signame, p);
412 varp[p + 1] = VARIANTToQVariant(pDispParams->rgvarg[pcount - p - 1], ptype);
413 argv_pointer[p + 1] = 0;
414 if (varp[p + 1].isValid()) {
415 if (varp[p + 1].type() == QVariant::UserType) {
416 argv[p + 1] = varp[p + 1].data();
417 } else if (ptype == "QVariant") {
418 argv[p + 1] = varp + p + 1;
419 } else {
420 argv[p + 1] = const_cast<void*>(varp[p + 1].constData());
421 if (ptype.endsWith('*')) {
422 argv_pointer[p + 1] = argv[p + 1];
423 argv[p + 1] = argv_pointer + p + 1;
424 }
425 }
426 } else if (ptype == "QVariant") {
427 argv[p + 1] = varp + p + 1;
428 } else {
429 ok = false;
430 }
431 }
432
433 if (ok) {
434 // emit the generated signal if everything went well
435 combase->qt_metacall(QMetaObject::InvokeMetaMethod, index, argv);
436 // update the VARIANT for references and free memory
437 for (p = 0; p < pcount; ++p) {
438 bool out;
439 QByteArray ptype = axmeta->paramType(signame, p, &out);
440 if (out) {
441 if (!QVariantToVARIANT(varp[p + 1], pDispParams->rgvarg[pcount - p - 1], ptype, out))
442 ok = false;
443 }
444 }
445 }
446
447 if (argv != static_argv) {
448 delete [] argv;
449 delete [] argv_pointer;
450 delete [] varp;
451 }
452 hres = ok ? S_OK : (ok ? DISP_E_MEMBERNOTFOUND : DISP_E_TYPEMISMATCH);
453 }
454
455 return hres;
456 }
457
458 QByteArray findProperty(DISPID dispID);
459
460 // IPropertyNotifySink
461 HRESULT __stdcall OnChanged(DISPID dispID)
462 {
463 // verify input
464 if (dispID == DISPID_UNKNOWN || !combase)
465 return S_OK;
466
467 const QMetaObject *meta = combase->metaObject();
468 if (!meta)
469 return S_OK;
470
471 QByteArray propname(findProperty(dispID));
472 if (propname.isEmpty())
473 return S_OK;
474
475 QObject *qobject = combase->qObject();
476 if (qobject->signalsBlocked())
477 return S_OK;
478
479 // emit the generic signal
480 int index = meta->indexOfSignal("propertyChanged(QString)");
481 if (index != -1) {
482 QString propnameString = QString::fromLatin1(propname);
483 void *argv[] = {0, &propnameString};
484 combase->qt_metacall(QMetaObject::InvokeMetaMethod, index, argv);
485 }
486
487 QByteArray signame = propsigs.value(dispID);
488 if (signame.isEmpty())
489 return S_OK;
490
491 index = meta->indexOfSignal(signame);
492 if (index == -1) // bindable but not marked as bindable in typelib
493 return S_OK;
494
495 // get the signal information from the metaobject
496 if (signalHasReceivers(qobject, signame)) {
497 index = meta->indexOfSignal(signame);
498 Q_ASSERT(index != -1);
499 // setup parameters
500 QVariant var = qobject->property(propname);
501 if (!var.isValid())
502 return S_OK;
503
504 const QMetaProperty metaProp = meta->property(meta->indexOfProperty(propname));
505 void *argv[] = {0, var.data()};
506 if (metaProp.type() == QVariant::LastType)
507 argv[1] = &var;
508
509 // emit the "changed" signal
510 combase->qt_metacall(QMetaObject::InvokeMetaMethod, index, argv);
511 }
512 return S_OK;
513 }
514 HRESULT __stdcall OnRequestEdit(DISPID dispID)
515 {
516 if (dispID == DISPID_UNKNOWN || !combase)
517 return S_OK;
518
519 QByteArray propname(findProperty(dispID));
520 if (propname.isEmpty())
521 return S_OK;
522
523 return combase->propertyWritable(propname) ? S_OK : S_FALSE;
524 }
525
526 static bool signalHasReceivers(QObject *qobject, const char *signalName)
527 {
528 Q_ASSERT(qobject);
529 return ((QAxObject*)qobject)->receivers(QByteArray::number(QSIGNAL_CODE) + signalName);
530 }
531
532 IConnectionPoint *cpoint;
533 IID ciid;
534 ULONG cookie;
535
536 QMap<DISPID, QByteArray> sigs;
537 QMap<DISPID, QByteArray> propsigs;
538 QMap<DISPID, QByteArray> props;
539
540 QAxBase *combase;
541 long ref;
542};
543
544/*
545 \internal
546 \class QAxBasePrivate
547*/
548
549class QAxBasePrivate
550{
551public:
552 QAxBasePrivate()
553 : useEventSink(true), useMetaObject(true), useClassInfo(true),
554 cachedMetaObject(false), initialized(false), tryCache(false),
555 ptr(0), disp(0), metaobj(0)
556 {
557 // protect initialization
558 QMutexLocker locker(&cache_mutex);
559 mo_cache_ref++;
560
561 qRegisterMetaType<IUnknown*>("IUnknown*", &ptr);
562 qRegisterMetaType<IDispatch*>("IDispatch*", &disp);
563 }
564
565 ~QAxBasePrivate()
566 {
567 Q_ASSERT(!ptr);
568 Q_ASSERT(!disp);
569
570 // protect cleanup
571 QMutexLocker locker(&cache_mutex);
572 if (!--mo_cache_ref) {
573 qDeleteAll(mo_cache);
574 mo_cache.clear();
575 }
576
577 CoFreeUnusedLibraries();
578 }
579
580 inline IDispatch *dispatch() const
581 {
582 if (disp)
583 return disp;
584
585 if (ptr)
586 ptr->QueryInterface(IID_IDispatch, (void**)&disp);
587 return disp;
588 }
589
590 QString ctrl;
591
592 QHash<QUuid, QAxEventSink*> eventSink;
593 uint useEventSink :1;
594 uint useMetaObject :1;
595 uint useClassInfo :1;
596 uint cachedMetaObject :1;
597 uint initialized :1;
598 uint tryCache :1;
599
600 IUnknown *ptr;
601 mutable IDispatch *disp;
602
603 QMap<QByteArray, bool> propWritable;
604
605 inline QAxMetaObject *metaObject()
606 {
607 if (!metaobj)
608 metaobj = new QAxMetaObject;
609 return metaobj;
610 }
611
612 mutable QMap<QString, LONG> verbs;
613
614 QAxMetaObject *metaobj;
615};
616
617
618QByteArray QAxEventSink::findProperty(DISPID dispID)
619{
620 // look up in cache, and fall back to
621 // type info for precompiled metaobjects
622 QByteArray propname(props.value(dispID));
623
624 if (!propname.isEmpty())
625 return propname;
626
627 IDispatch *dispatch = combase->d->dispatch();
628 ITypeInfo *typeinfo = 0;
629 if (dispatch)
630 dispatch->GetTypeInfo(0, LOCALE_USER_DEFAULT, &typeinfo);
631 if (!typeinfo)
632 return propname;
633
634 BSTR names;
635 UINT cNames;
636 typeinfo->GetNames(dispID, &names, 1, &cNames);
637 if (cNames) {
638 propname = QString::fromWCharArray(names).toLatin1();
639 SysFreeString(names);
640 }
641 typeinfo->Release();
642
643 QByteArray propsignal(propname + "Changed(");
644 const QMetaObject *mo = combase->metaObject();
645 int index = mo->indexOfProperty(propname);
646 const QMetaProperty prop = mo->property(index);
647 propsignal += prop.typeName();
648 propsignal += ')';
649 addProperty(dispID, propname, propsignal);
650
651 return propname;
652}
653
654/*!
655 \class QAxBase
656 \brief The QAxBase class is an abstract class that provides an API
657 to initialize and access a COM object.
658
659 \inmodule QAxContainer
660
661 QAxBase is an abstract class that cannot be used directly, and is
662 instantiated through the subclasses QAxObject and QAxWidget. This
663 class provides the API to access the COM object directly
664 through its IUnknown implementation. If the COM object implements
665 the IDispatch interface, the properties and methods of that object
666 become available as Qt properties and slots.
667
668 \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 0
669
670 Properties exposed by the object's IDispatch implementation can
671 be read and written through the property system provided by the
672 Qt Object Model (both subclasses are QObjects, so you can use
673 QObject::setProperty() and QObject::property()). Properties with
674 multiple parameters are not supported.
675
676 \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 1
677
678 Write-functions for properties and other methods exposed by the
679 object's IDispatch implementation can be called directly using
680 dynamicCall(), or indirectly as slots connected to a signal.
681
682 \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 2
683
684 Outgoing events supported by the COM object are emitted as
685 standard Qt signals.
686
687 \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 3
688
689 QAxBase transparently converts between COM data types and the
690 equivalent Qt data types. Some COM types have no equivalent Qt data structure.
691
692 Supported COM datatypes are listed in the first column of following table.
693 The second column is the Qt type that can be used with the QObject property
694 functions. The third column is the Qt type that is used in the prototype of
695 generated signals and slots for in-parameters, and the last column is the Qt
696 type that is used in the prototype of signals and slots for out-parameters.
697 \table
698 \header
699 \i COM type
700 \i Qt property
701 \i in-parameter
702 \i out-parameter
703 \row
704 \i VARIANT_BOOL
705 \i bool
706 \i bool
707 \i bool&
708 \row
709 \i BSTR
710 \i QString
711 \i const QString&
712 \i QString&
713 \row
714 \i char, short, int, long
715 \i int
716 \i int
717 \i int&
718 \row
719 \i uchar, ushort, uint, ulong
720 \i uint
721 \i uint
722 \i uint&
723 \row
724 \i float, double
725 \i double
726 \i double
727 \i double&
728 \row
729 \i DATE
730 \i QDateTime
731 \i const QDateTime&
732 \i QDateTime&
733 \row
734 \i CY
735 \i qlonglong
736 \i qlonglong
737 \i qlonglong&
738 \row
739 \i OLE_COLOR
740 \i QColor
741 \i const QColor&
742 \i QColor&
743 \row
744 \i SAFEARRAY(VARIANT)
745 \i QList\<QVariant\>
746 \i const QList\<QVariant\>&
747 \i QList\<QVariant\>&
748 \row
749 \i SAFEARRAY(int), SAFEARRAY(double), SAFEARRAY(Date)
750 \i QList\<QVariant\>
751 \i const QList\<QVariant\>&
752 \i QList\<QVariant\>&
753 \row
754 \i SAFEARRAY(BYTE)
755 \i QByteArray
756 \i const QByteArray&
757 \i QByteArray&
758 \row
759 \i SAFEARRAY(BSTR)
760 \i QStringList
761 \i const QStringList&
762 \i QStringList&
763 \row
764 \i VARIANT
765 \i type-dependent
766 \i const QVariant&
767 \i QVariant&
768 \row
769 \i IFontDisp*
770 \i QFont
771 \i const QFont&
772 \i QFont&
773 \row
774 \i IPictureDisp*
775 \i QPixmap
776 \i const QPixmap&
777 \i QPixmap&
778 \row
779 \i IDispatch*
780 \i QAxObject*
781 \i \c QAxBase::asVariant()
782 \i QAxObject* (return value)
783 \row
784 \i IUnknown*
785 \i QAxObject*
786 \i \c QAxBase::asVariant()
787 \i QAxObject* (return value)
788 \row
789 \i SCODE, DECIMAL
790 \i \e unsupported
791 \i \e unsupported
792 \i \e unsupported
793 \row
794 \i VARIANT* (Since Qt 4.5)
795 \i \e unsupported
796 \i \e QVariant&
797 \i \e QVariant&
798 \endtable
799
800 Supported are also enumerations, and typedefs to supported types.
801
802 To call the methods of a COM interface described by the following IDL
803
804 \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 4
805
806 use the QAxBase API like this:
807
808 \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 5
809
810 Note that the QList the object should fill has to be provided as an
811 element in the parameter list of \l{QVariant}s.
812
813 If you need to access properties or pass parameters of
814 unsupported datatypes you must access the COM object directly
815 through its \c IDispatch implementation or other interfaces.
816 Those interfaces can be retrieved through queryInterface().
817
818 \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 6
819
820 To get the definition of the COM interfaces you will have to use the header
821 files provided with the component you want to use. Some compilers can also
822 import type libraries using the #import compiler directive. See the component
823 documentation to find out which type libraries you have to import, and how to use
824 them.
825
826 If you need to react to events that pass parameters of unsupported
827 datatypes you can use the generic signal that delivers the event
828 data as provided by the COM event.
829
830 \sa QAxObject, QAxWidget, QAxScript, {ActiveQt Framework}
831*/
832
833/*!
834 \typedef QAxBase::PropertyBag
835
836 A QMap<QString,QVariant> that can store properties as name:value pairs.
837*/
838
839/*!
840 Creates a QAxBase object that wraps the COM object \a iface. If \a
841 iface is 0 (the default), use setControl() to instantiate a COM
842 object.
843*/
844QAxBase::QAxBase(IUnknown *iface)
845{
846 d = new QAxBasePrivate();
847 d->ptr = iface;
848 if (d->ptr) {
849 d->ptr->AddRef();
850 d->initialized = true;
851 }
852#if defined(Q_OS_WINCE)
853 CoInitializeEx(0, COINIT_MULTITHREADED);
854#endif
855}
856
857/*!
858 Shuts down the COM object and destroys the QAxBase object.
859
860 \sa clear()
861*/
862QAxBase::~QAxBase()
863{
864#if defined(Q_OS_WINCE)
865 CoUninitialize();
866#endif
867
868 clear();
869
870 delete d;
871 d = 0;
872}
873
874/*!
875 \internal
876
877 Used by subclasses generated with dumpcpp to balance reference count.
878*/
879void QAxBase::internalRelease()
880{
881 if (d->ptr)
882 d->ptr->Release();
883}
884
885/*!
886 \internal
887
888 Used by subclasses generated with dumpcpp to implement cast-operators.
889*/
890void QAxBase::initializeFrom(QAxBase *that)
891{
892 if (d->ptr)
893 return;
894
895 d->ptr = that->d->ptr;
896 if (d->ptr) {
897 d->ptr->AddRef();
898 d->initialized = true;
899 }
900}
901
902
903QAxMetaObject *QAxBase::internalMetaObject() const
904{
905 return d->metaObject();
906}
907
908/*!
909 \property QAxBase::control
910 \brief the name of the COM object wrapped by this QAxBase object.
911
912 Setting this property initializes the COM object. Any COM object
913 previously set is shut down.
914
915 The most efficient way to set this property is by using the
916 registered component's UUID, e.g.
917
918 \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 7
919
920 The second fastest way is to use the registered control's class
921 name (with or without version number), e.g.
922
923 \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 8
924
925 The slowest, but easiest way to use is to use the control's full
926 name, e.g.
927
928 \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 9
929
930 It is also possible to initialize the object from a file, e.g.
931
932 \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 10
933
934 If the component's UUID is used the following patterns can be used
935 to initialize the control on a remote machine, to initialize a
936 licensed control or to connect to a running object:
937 \list
938 \i To initialize the control on a different machine use the following
939 pattern:
940
941 \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 11
942
943 \i To initialize a licensed control use the following pattern:
944
945 \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 12
946
947 \i To connect to an already running object use the following pattern:
948
949 \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 13
950
951 \endlist
952 The first two patterns can be combined, e.g. to initialize a licensed
953 control on a remote machine:
954
955 \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 14
956
957 The control's read function always returns the control's UUID, if provided including the license
958 key, and the name of the server, but not including the username, the domain or the password.
959*/
960bool QAxBase::setControl(const QString &c)
961{
962 if (c.toLower() == d->ctrl.toLower())
963 return !d->ctrl.isEmpty();
964
965 QString search = c;
966 // don't waste time for DCOM requests
967 int dcomIDIndex = search.indexOf(QLatin1String("/{"));
968 if ((dcomIDIndex == -1 || dcomIDIndex != search.length()-39) && !search.endsWith(QLatin1String("}&"))) {
969 QUuid uuid(search);
970 if (uuid.isNull()) {
971 CLSID clsid;
972 HRESULT res = CLSIDFromProgID((wchar_t*)c.utf16(), &clsid);
973 if (res == S_OK)
974 search = QUuid(clsid).toString();
975 else {
976 QSettings controls(QLatin1String("HKEY_LOCAL_MACHINE\\Software\\Classes\\"), QSettings::NativeFormat);
977 search = controls.value(c + QLatin1String("/CLSID/Default")).toString();
978 if (search.isEmpty()) {
979 controls.beginGroup(QLatin1String("/CLSID"));
980 QStringList clsids = controls.childGroups();
981 for (QStringList::Iterator it = clsids.begin(); it != clsids.end(); ++it) {
982 QString clsid = *it;
983 QString name = controls.value(clsid + QLatin1String("/Default")).toString();
984 if (name == c) {
985 search = clsid;
986 break;
987 }
988 }
989 controls.endGroup();
990 }
991 }
992 }
993 if (search.isEmpty())
994 search = c;
995 }
996
997 if (search.toLower() == d->ctrl.toLower())
998 return !d->ctrl.isEmpty();
999
1000 clear();
1001 d->ctrl = search;
1002
1003 d->tryCache = true;
1004 if (!initialize(&d->ptr))
1005 d->initialized = true;
1006 if (isNull()) {
1007 qWarning("QAxBase::setControl: requested control %s could not be instantiated", c.toLatin1().data());
1008 clear();
1009 return false;
1010 }
1011 return true;
1012}
1013
1014QString QAxBase::control() const
1015{
1016 return d->ctrl;
1017}
1018
1019/*!
1020 Disables the event sink implementation for this ActiveX container.
1021 If you don't intend to listen to the ActiveX control's events use
1022 this function to speed up the meta object generation.
1023
1024 Some ActiveX controls might be unstable when connected to an event
1025 sink. To get OLE events you must use standard COM methods to
1026 register your own event sink. Use queryInterface() to get access
1027 to the raw COM object.
1028
1029 Note that this function should be called immediately after
1030 construction of the object.
1031*/
1032void QAxBase::disableEventSink()
1033{
1034 d->useEventSink = false;
1035}
1036
1037/*!
1038 Disables the meta object generation for this ActiveX container.
1039 This also disables the event sink and class info generation. If
1040 you don't intend to use the Qt meta object implementation call
1041 this function to speed up instantiation of the control. You will
1042 still be able to call the object through \l dynamicCall(), but
1043 signals, slots and properties will not be available with QObject
1044 APIs.
1045
1046 Some ActiveX controls might be unstable when used with OLE
1047 automation. Use standard COM methods to use those controls through
1048 the COM interfaces provided by queryInterface().
1049
1050 Note that this function must be called immediately after
1051 construction of the object.
1052*/
1053void QAxBase::disableMetaObject()
1054{
1055 d->useMetaObject = false;
1056 d->useEventSink = false;
1057 d->useClassInfo = false;
1058}
1059
1060/*!
1061 Disables the class info generation for this ActiveX container. If
1062 you don't require any class information about the ActiveX control
1063 use this function to speed up the meta object generation.
1064
1065 Note that this function must be called immediately after
1066 construction of the object
1067*/
1068void QAxBase::disableClassInfo()
1069{
1070 d->useClassInfo = false;
1071}
1072
1073/*!
1074 Disconnects and destroys the COM object.
1075
1076 If you reimplement this function you must also reimplement the
1077 destructor to call clear(), and call this implementation at the
1078 end of your clear() function.
1079*/
1080void QAxBase::clear()
1081{
1082 QHash<QUuid, QAxEventSink*>::Iterator it = d->eventSink.begin();
1083 while (it != d->eventSink.end()) {
1084 QAxEventSink *eventSink = it.value();
1085 ++it;
1086 if (eventSink) {
1087 eventSink->unadvise();
1088 eventSink->Release();
1089 }
1090 }
1091 d->eventSink.clear();
1092 if (d->disp) {
1093 d->disp->Release();
1094 d->disp = 0;
1095 }
1096 if (d->ptr) {
1097 d->ptr->Release();
1098 d->ptr = 0;
1099 d->initialized = false;
1100 }
1101
1102 d->ctrl.clear();
1103
1104 if (!d->cachedMetaObject)
1105 delete d->metaobj;
1106 d->metaobj = 0;
1107}
1108
1109/*!
1110 \since 4.1
1111
1112 Returns the list of verbs that the COM object can execute. If
1113 the object does not implement IOleObject, or does not support
1114 any verbs, then this function returns an empty stringlist.
1115
1116 Note that the OLE default verbs (OLEIVERB_SHOW etc) are not
1117 included in the list.
1118*/
1119QStringList QAxBase::verbs() const
1120{
1121 if (!d->ptr)
1122 return QStringList();
1123
1124 if (d->verbs.isEmpty()) {
1125 IOleObject *ole = 0;
1126 d->ptr->QueryInterface(IID_IOleObject, (void**)&ole);
1127 if (ole) {
1128 IEnumOLEVERB *enumVerbs = 0;
1129 ole->EnumVerbs(&enumVerbs);
1130 if (enumVerbs) {
1131 enumVerbs->Reset();
1132 ULONG c;
1133 OLEVERB verb;
1134 while (enumVerbs->Next(1, &verb, &c) == S_OK) {
1135 if (!verb.lpszVerbName)
1136 continue;
1137 QString verbName = QString::fromWCharArray(verb.lpszVerbName);
1138 if (!verbName.isEmpty())
1139 d->verbs.insert(verbName, verb.lVerb);
1140 }
1141 enumVerbs->Release();
1142 }
1143 ole->Release();
1144 }
1145 }
1146
1147 return d->verbs.keys();
1148}
1149
1150/*!
1151 \internal
1152*/
1153
1154long QAxBase::indexOfVerb(const QString &verb) const
1155{
1156 return d->verbs.value(verb);
1157}
1158
1159/*!
1160 This virtual function is called by setControl() and creates the
1161 requested COM object. \a ptr is set to the object's IUnknown
1162 implementation. The function returns true if the object
1163 initialization succeeded; otherwise the function returns false.
1164
1165 The default implementation interprets the string returned by
1166 control(), and calls initializeRemote(), initializeLicensed()
1167 or initializeActive() if the string matches the respective
1168 patterns. If control() is the name of an existing file,
1169 initializeFromFile() is called. If no pattern is matched, or
1170 if remote or licensed initialization fails, CoCreateInstance
1171 is used directly to create the object.
1172
1173 See the \l control property documentation for details about
1174 supported patterns.
1175
1176 The interface returned in \a ptr must be referenced exactly once
1177 when this function returns. The interface provided by e.g.
1178 CoCreateInstance is already referenced, and there is no need to
1179 reference it again.
1180*/
1181bool QAxBase::initialize(IUnknown **ptr)
1182{
1183 if (*ptr || control().isEmpty())
1184 return false;
1185
1186 *ptr = 0;
1187
1188 bool res = false;
1189
1190 const QString ctrl(d->ctrl);
1191 if (ctrl.contains(QLatin1String("/{"))) // DCOM request
1192 res = initializeRemote(ptr);
1193 else if (ctrl.contains(QLatin1String("}:"))) // licensed control
1194 res = initializeLicensed(ptr);
1195 else if (ctrl.contains(QLatin1String("}&"))) // running object
1196 res = initializeActive(ptr);
1197 else if (QFile::exists(ctrl)) // existing file
1198 res = initializeFromFile(ptr);
1199
1200 if (!res) { // standard
1201 HRESULT hres = CoCreateInstance(QUuid(ctrl), 0, CLSCTX_SERVER, IID_IUnknown, (void**)ptr);
1202 res = S_OK == hres;
1203#ifndef QT_NO_DEBUG
1204 if (!res)
1205 qErrnoWarning(hres, "CoCreateInstance failure");
1206#endif
1207 }
1208
1209 return *ptr != 0;
1210}
1211
1212/*!
1213 Creates an instance of a licensed control, and returns the IUnknown interface
1214 to the object in \a ptr. This functions returns true if successful, otherwise
1215 returns false.
1216
1217 This function is called by initialize() if the control string contains the
1218 substring "}:". The license key needs to follow this substring.
1219
1220 \sa initialize()
1221*/
1222bool QAxBase::initializeLicensed(IUnknown** ptr)
1223{
1224 int at = control().lastIndexOf(QLatin1String("}:"));
1225
1226 QString clsid(control().left(at));
1227 QString key(control().mid(at+2));
1228
1229 IClassFactory *factory = 0;
1230 CoGetClassObject(QUuid(clsid), CLSCTX_SERVER, 0, IID_IClassFactory, (void**)&factory);
1231 if (!factory)
1232 return false;
1233 initializeLicensedHelper(factory, key, ptr);
1234 factory->Release();
1235
1236 return *ptr != 0;
1237}
1238
1239/* \internal
1240 Called by initializeLicensed and initializedRemote to create an object
1241 via IClassFactory2.
1242*/
1243bool QAxBase::initializeLicensedHelper(void *f, const QString &key, IUnknown **ptr)
1244{
1245 IClassFactory *factory = (IClassFactory*)f;
1246 IClassFactory2 *factory2 = 0;
1247 factory->QueryInterface(IID_IClassFactory2, (void**)&factory2);
1248 if (factory2) {
1249 BSTR bkey = QStringToBSTR(key);
1250 HRESULT hres = factory2->CreateInstanceLic(0, 0, IID_IUnknown, bkey, (void**)ptr);
1251 SysFreeString(bkey);
1252#ifdef QT_DEBUG
1253 LICINFO licinfo;
1254 licinfo.cbLicInfo = sizeof(LICINFO);
1255 factory2->GetLicInfo(&licinfo);
1256
1257 if (hres != S_OK) {
1258 SetLastError(hres);
1259 qErrnoWarning("CreateInstanceLic failed");
1260 if (!licinfo.fLicVerified) {
1261 qWarning("Wrong license key specified, and machine is not fully licensed.");
1262 } else if (licinfo.fRuntimeKeyAvail) {
1263 BSTR licenseKey;
1264 factory2->RequestLicKey(0, &licenseKey);
1265 QString qlicenseKey = QString::fromWCharArray(licenseKey);
1266 SysFreeString(licenseKey);
1267 qWarning("Use license key is '%s' to create object on unlicensed machine.",
1268 qlicenseKey.toLatin1().constData());
1269 }
1270 } else if (licinfo.fLicVerified) {
1271 qWarning("Machine is fully licensed for '%s'", control().toLatin1().constData());
1272 if (licinfo.fRuntimeKeyAvail) {
1273 BSTR licenseKey;
1274 factory2->RequestLicKey(0, &licenseKey);
1275 QString qlicenseKey = QString::fromWCharArray(licenseKey);
1276 SysFreeString(licenseKey);
1277
1278 if (qlicenseKey != key)
1279 qWarning("Runtime license key is '%s'", qlicenseKey.toLatin1().constData());
1280 }
1281 }
1282#else
1283 Q_UNUSED(hres);
1284#endif
1285 factory2->Release();
1286 } else { // give it a shot without license
1287 factory->CreateInstance(0, IID_IUnknown, (void**)ptr);
1288 }
1289 return *ptr != 0;
1290}
1291
1292
1293/*!
1294 Connects to an active instance running on the current machine, and returns the
1295 IUnknown interface to the running object in \a ptr. This function returns true
1296 if successful, otherwise returns false.
1297
1298 This function is called by initialize() if the control string contains the
1299 substring "}&".
1300
1301 \sa initialize()
1302*/
1303bool QAxBase::initializeActive(IUnknown** ptr)
1304{
1305#if defined(Q_OS_WINCE)
1306 Q_UNUSED(ptr);
1307 return false;
1308#else
1309 int at = control().lastIndexOf(QLatin1String("}&"));
1310 QString clsid(control().left(at));
1311
1312 GetActiveObject(QUuid(clsid), 0, ptr);
1313
1314 return *ptr != 0;
1315#endif
1316}
1317
1318#ifdef Q_CC_GNU
1319# ifndef OLEPENDER_NONE
1320# define OLERENDER_NONE 0
1321# endif
1322#endif
1323
1324/*!
1325 Creates the COM object handling the filename in the control property, and
1326 returns the IUnknown interface to the object in \a ptr. This function returns
1327 true if successful, otherwise returns false.
1328
1329 This function is called by initialize() if the control string is the name of
1330 an existing file.
1331
1332 \sa initialize()
1333*/
1334bool QAxBase::initializeFromFile(IUnknown** ptr)
1335{
1336#if defined(Q_OS_WINCE)
1337 Q_UNUSED(ptr);
1338 return false;
1339#else
1340 IStorage *storage = 0;
1341 ILockBytes * bytes = 0;
1342 HRESULT hres = ::CreateILockBytesOnHGlobal(0, TRUE, &bytes);
1343 hres = ::StgCreateDocfileOnILockBytes(bytes, STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_READWRITE, 0, &storage);
1344
1345 hres = OleCreateFromFile(CLSID_NULL, reinterpret_cast<const wchar_t*>(control().utf16()), IID_IUnknown, OLERENDER_NONE, 0, 0, storage, (void**)ptr);
1346
1347 storage->Release();
1348 bytes->Release();
1349
1350 return hres == S_OK;
1351#endif
1352}
1353
1354
1355// There seams to be a naming problem in mingw headers
1356#ifdef Q_CC_GNU
1357#ifndef COAUTHIDENTITY
1358#define COAUTHIDENTITY AUTH_IDENTITY
1359#endif
1360#endif
1361
1362
1363/*!
1364 Creates the instance on a remote server, and returns the IUnknown interface
1365 to the object in \a ptr. This function returns true if successful, otherwise
1366 returns false.
1367
1368 This function is called by initialize() if the control string contains the
1369 substring "/{". The information about the remote machine needs to be provided
1370 in front of the substring.
1371
1372 \sa initialize()
1373*/
1374bool QAxBase::initializeRemote(IUnknown** ptr)
1375{
1376 int at = control().lastIndexOf(QLatin1String("/{"));
1377
1378 QString server(control().left(at));
1379 QString clsid(control().mid(at+1));
1380
1381 QString user;
1382 QString domain;
1383 QString passwd;
1384 QString key;
1385
1386 at = server.indexOf(QChar::fromLatin1('@'));
1387 if (at != -1) {
1388 user = server.left(at);
1389 server = server.mid(at+1);
1390
1391 at = user.indexOf(QChar::fromLatin1(':'));
1392 if (at != -1) {
1393 passwd = user.mid(at+1);
1394 user = user.left(at);
1395 }
1396 at = user.indexOf(QChar::fromLatin1('/'));
1397 if (at != -1) {
1398 domain = user.left(at);
1399 user = user.mid(at+1);
1400 }
1401 }
1402
1403 at = clsid.lastIndexOf(QLatin1String("}:"));
1404 if (at != -1) {
1405 key = clsid.mid(at+2);
1406 clsid = clsid.left(at);
1407 }
1408
1409 d->ctrl = server + QChar::fromLatin1('/') + clsid;
1410 if (!key.isEmpty())
1411 d->ctrl = d->ctrl + QChar::fromLatin1(':') + key;
1412
1413 COAUTHIDENTITY authIdentity;
1414 authIdentity.UserLength = user.length();
1415 authIdentity.User = authIdentity.UserLength ? (ushort*)user.utf16() : 0;
1416 authIdentity.DomainLength = domain.length();
1417 authIdentity.Domain = authIdentity.DomainLength ? (ushort*)domain.utf16() : 0;
1418 authIdentity.PasswordLength = passwd.length();
1419 authIdentity.Password = authIdentity.PasswordLength ? (ushort*)passwd.utf16() : 0;
1420 authIdentity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
1421
1422 COAUTHINFO authInfo;
1423 authInfo.dwAuthnSvc = RPC_C_AUTHN_WINNT;
1424 authInfo.dwAuthzSvc = RPC_C_AUTHZ_NONE;
1425 authInfo.pwszServerPrincName = 0;
1426 authInfo.dwAuthnLevel = RPC_C_AUTHN_LEVEL_DEFAULT;
1427 authInfo.dwImpersonationLevel = RPC_C_IMP_LEVEL_IMPERSONATE;
1428 authInfo.pAuthIdentityData = &authIdentity;
1429 authInfo.dwCapabilities = 0;
1430
1431 COSERVERINFO serverInfo;
1432 serverInfo.dwReserved1 = 0;
1433 serverInfo.dwReserved2 = 0;
1434 serverInfo.pAuthInfo = &authInfo;
1435 serverInfo.pwszName = (wchar_t*)server.utf16();
1436
1437 IClassFactory *factory = 0;
1438 HRESULT res = CoGetClassObject(QUuid(clsid), CLSCTX_REMOTE_SERVER, &serverInfo, IID_IClassFactory, (void**)&factory);
1439 if (factory) {
1440 if (!key.isEmpty())
1441 initializeLicensedHelper(factory, key, ptr);
1442 else
1443 res = factory->CreateInstance(0, IID_IUnknown, (void**)ptr);
1444 factory->Release();
1445 }
1446#ifndef QT_NO_DEBUG
1447 if (res != S_OK)
1448 qErrnoWarning(res, "initializeRemote Failed");
1449#endif
1450
1451 return res == S_OK;
1452}
1453
1454/*!
1455 Requests the interface \a uuid from the COM object and sets the
1456 value of \a iface to the provided interface, or to 0 if the
1457 requested interface could not be provided.
1458
1459 Returns the result of the QueryInterface implementation of the COM object.
1460
1461 \sa control
1462*/
1463long QAxBase::queryInterface(const QUuid &uuid, void **iface) const
1464{
1465 *iface = 0;
1466 if (!d->ptr) {
1467 ((QAxBase*)this)->initialize(&d->ptr);
1468 d->initialized = true;
1469 }
1470
1471 if (d->ptr && !uuid.isNull())
1472 return d->ptr->QueryInterface(uuid, iface);
1473
1474 return E_NOTIMPL;
1475}
1476
1477class MetaObjectGenerator
1478{
1479public:
1480 MetaObjectGenerator(QAxBase *ax, QAxBasePrivate *dptr);
1481 MetaObjectGenerator(ITypeLib *typelib, ITypeInfo *typeinfo);
1482 ~MetaObjectGenerator();
1483
1484 QMetaObject *metaObject(const QMetaObject *parentObject, const QByteArray &className = QByteArray());
1485
1486 void readClassInfo();
1487 void readEnumInfo();
1488 void readInterfaceInfo();
1489 void readFuncsInfo(ITypeInfo *typeinfo, ushort nFuncs);
1490 void readVarsInfo(ITypeInfo *typeinfo, ushort nVars);
1491 void readEventInfo();
1492 void readEventInterface(ITypeInfo *eventinfo, IConnectionPoint *cpoint);
1493
1494 inline void addClassInfo(const char *key, const char *value)
1495 {
1496 classinfo_list.insert(key, value);
1497 }
1498
1499private:
1500 void init();
1501
1502
1503 QMetaObject *tryCache();
1504
1505 QByteArray createPrototype(FUNCDESC *funcdesc, ITypeInfo *typeinfo, const QList<QByteArray> &names,
1506 QByteArray &type, QList<QByteArray> &parameters);
1507
1508 QByteArray usertypeToString(const TYPEDESC &tdesc, ITypeInfo *info, const QByteArray &function);
1509 QByteArray guessTypes(const TYPEDESC &tdesc, ITypeInfo *info, const QByteArray &function);
1510
1511 // ### from qmetaobject.cpp
1512 enum ProperyFlags {
1513 Invalid = 0x00000000,
1514 Readable = 0x00000001,
1515 Writable = 0x00000002,
1516 Resettable = 0x00000004,
1517 EnumOrFlag = 0x00000008,
1518 StdCppSet = 0x00000100,
1519// Override = 0x00000200,
1520 Designable = 0x00001000,
1521 ResolveDesignable = 0x00002000,
1522 Scriptable = 0x00004000,
1523 ResolveScriptable = 0x00008000,
1524 Stored = 0x00010000,
1525 ResolveStored = 0x00020000,
1526 Editable = 0x00040000,
1527 ResolveEditable = 0x00080000,
1528 User = 0x00100000,
1529 ResolveUser = 0x00200000,
1530 // And our own - don't use the upper byte, as it's used for the property type
1531 RequestingEdit = 0x00400000,
1532 Bindable = 0x00800000
1533 };
1534 enum MemberFlags {
1535 AccessPrivate = 0x00,
1536 AccessProtected = 0x01,
1537 AccessPublic = 0x02,
1538 MemberMethod = 0x00,
1539 MemberSignal = 0x04,
1540 MemberSlot = 0x08,
1541 MemberCompatibility = 0x10,
1542 MemberCloned = 0x20,
1543 MemberScriptable = 0x40,
1544 };
1545
1546 inline QList<QByteArray> paramList(const QByteArray &proto)
1547 {
1548 QByteArray prototype(proto);
1549 QByteArray parameters = prototype.mid(prototype.indexOf('(') + 1);
1550 parameters.truncate(parameters.length() - 1);
1551
1552 QList<QByteArray> plist = parameters.split(',');
1553 return plist;
1554 }
1555
1556 inline QByteArray replaceType(const QByteArray &type)
1557 {
1558 int i = 0;
1559 while (type_conversion[i][0]) {
1560 int len = int(strlen(type_conversion[i][0]));
1561 int ti;
1562 if ((ti = type.indexOf(type_conversion[i][0])) != -1) {
1563 QByteArray rtype(type);
1564 rtype.replace(ti, len, type_conversion[i][1]);
1565 return rtype;
1566 }
1567 ++i;
1568 }
1569 return type;
1570 }
1571
1572 QByteArray replacePrototype(const QByteArray &prototype)
1573 {
1574 QByteArray proto(prototype);
1575
1576 QList<QByteArray> plist = paramList(prototype);
1577 for (int p = 0; p < plist.count(); ++p) {
1578 QByteArray param(plist.at(p));
1579 if (param != replaceType(param)) {
1580 int type = 0;
1581 while (type_conversion[type][0]) {
1582 int paren = proto.indexOf('(');
1583 while ((paren = proto.indexOf(type_conversion[type][0])) != -1) {
1584 proto.replace(paren, qstrlen(type_conversion[type][0]), type_conversion[type][1]);
1585 }
1586 ++type;
1587 }
1588 break;
1589 }
1590 }
1591
1592 return proto;
1593 }
1594
1595 QMap<QByteArray, QByteArray> classinfo_list;
1596
1597 inline bool hasClassInfo(const char *key)
1598 {
1599 return classinfo_list.contains(key);
1600 }
1601
1602 struct Method {
1603 Method() : flags(0)
1604 {}
1605 QByteArray type;
1606 QByteArray parameters;
1607 int flags;
1608 QByteArray realPrototype;
1609 };
1610 QMap<QByteArray, Method> signal_list;
1611 inline void addSignal(const QByteArray &prototype, const QByteArray &parameters)
1612 {
1613 QByteArray proto(replacePrototype(prototype));
1614
1615 Method &signal = signal_list[proto];
1616 signal.type = 0;
1617 signal.parameters = parameters;
1618 signal.flags = QMetaMethod::Public | MemberSignal;
1619 if (proto != prototype)
1620 signal.realPrototype = prototype;
1621 }
1622
1623 void addChangedSignal(const QByteArray &function, const QByteArray &type, int memid);
1624
1625 inline bool hasSignal(const QByteArray &prototype)
1626 {
1627 return signal_list.contains(prototype);
1628 }
1629
1630 QMap<QByteArray, Method> slot_list;
1631 inline void addSlot(const QByteArray &type, const QByteArray &prototype, const QByteArray &parameters, int flags = QMetaMethod::Public)
1632 {
1633 QByteArray proto = replacePrototype(prototype);
1634
1635 Method &slot = slot_list[proto];
1636 slot.type = replaceType(type);
1637 slot.parameters = parameters;
1638 slot.flags = flags | MemberSlot;
1639 if (proto != prototype)
1640 slot.realPrototype = prototype;
1641 }
1642
1643 void addSetterSlot(const QByteArray &property);
1644
1645 inline bool hasSlot(const QByteArray &prototype)
1646 {
1647 return slot_list.contains(prototype);
1648 }
1649
1650 struct Property {
1651 Property() : typeId(0)
1652 {}
1653 QByteArray type;
1654 uint typeId;
1655 QByteArray realType;
1656 };
1657 QMap<QByteArray, Property> property_list;
1658 void addProperty(const QByteArray &type, const QByteArray &name, uint flags)
1659 {
1660 QByteArray propertyType(type);
1661 if (propertyType.endsWith('&'))
1662 propertyType.chop(1);
1663
1664 Property &prop = property_list[name];
1665 if (!propertyType.isEmpty() && propertyType != "HRESULT") {
1666 prop.type = replaceType(propertyType);
1667 if (prop.type != propertyType)
1668 prop.realType = propertyType;
1669 }
1670 if (flags & Writable)
1671 flags |= Stored;
1672 prop.typeId |= flags;
1673 QVariant::Type vartype = QVariant::nameToType(prop.type);
1674 switch(vartype) {
1675 case QVariant::Invalid:
1676 if (prop.type == "QVariant") {
1677 prop.typeId |= 0xff << 24;
1678 break;
1679 }
1680 // fall through
1681 case QVariant::UserType:
1682 if (QMetaType::type(prop.type) == -1)
1683 qWarning("QAxBase: Unsupported property type: %s", prop.type.data());
1684 break;
1685 default:
1686 prop.typeId |= vartype << 24;
1687 break;
1688 }
1689 }
1690
1691 inline bool hasProperty(const QByteArray &name)
1692 {
1693 return property_list.contains(name);
1694 }
1695
1696 inline QByteArray propertyType(const QByteArray &name)
1697 {
1698 return property_list.value(name).type;
1699 }
1700
1701 QMap<QByteArray, QList<QPair<QByteArray, int> > > enum_list;
1702 inline void addEnumValue(const QByteArray &enumname, const QByteArray &key, int value)
1703 {
1704 enum_list[enumname].append(QPair<QByteArray, int>(key, value));
1705 }
1706
1707 inline bool hasEnum(const QByteArray &enumname)
1708 {
1709 return enum_list.contains(enumname);
1710 }
1711
1712 QAxBase *that;
1713 QAxBasePrivate *d;
1714
1715 IDispatch *disp;
1716 ITypeInfo *dispInfo;
1717 ITypeInfo *classInfo;
1718 ITypeLib *typelib;
1719 QByteArray current_typelib;
1720
1721 QSettings iidnames;
1722 QString cacheKey;
1723 QByteArray debugInfo;
1724
1725 QUuid iid_propNotifySink;
1726
1727 friend QMetaObject *qax_readClassInfo(ITypeLib *typeLib, ITypeInfo *classInfo, const QMetaObject *parentObject);
1728};
1729
1730QMetaObject *qax_readEnumInfo(ITypeLib *typeLib, const QMetaObject *parentObject)
1731{
1732 MetaObjectGenerator generator(typeLib, 0);
1733
1734 generator.readEnumInfo();
1735 return generator.metaObject(parentObject, "EnumInfo");
1736}
1737
1738QMetaObject *qax_readInterfaceInfo(ITypeLib *typeLib, ITypeInfo *typeInfo, const QMetaObject *parentObject)
1739{
1740 MetaObjectGenerator generator(typeLib, typeInfo);
1741
1742 QString className;
1743 BSTR bstr;
1744 if (S_OK != typeInfo->GetDocumentation(-1, &bstr, 0, 0, 0))
1745 return 0;
1746
1747 className = QString::fromWCharArray(bstr);
1748 SysFreeString(bstr);
1749
1750 generator.readEnumInfo();
1751 generator.readFuncsInfo(typeInfo, 0);
1752 generator.readVarsInfo(typeInfo, 0);
1753
1754 return generator.metaObject(parentObject, className.toLatin1());
1755}
1756
1757QMetaObject *qax_readClassInfo(ITypeLib *typeLib, ITypeInfo *classInfo, const QMetaObject *parentObject)
1758{
1759 MetaObjectGenerator generator(typeLib, 0);
1760 generator.addSignal("exception(int,QString,QString,QString)", "code,source,disc,help");
1761 generator.addSignal("propertyChanged(QString)", "name");
1762
1763 QString className;
1764 BSTR bstr;
1765 if (S_OK != classInfo->GetDocumentation(-1, &bstr, 0, 0, 0))
1766 return 0;
1767
1768 className = QString::fromWCharArray(bstr);
1769 SysFreeString(bstr);
1770
1771 generator.readEnumInfo();
1772
1773 TYPEATTR *typeattr;
1774 classInfo->GetTypeAttr(&typeattr);
1775 if (typeattr) {
1776 int nInterfaces = typeattr->cImplTypes;
1777 classInfo->ReleaseTypeAttr(typeattr);
1778
1779 for (int index = 0; index < nInterfaces; ++index) {
1780 HREFTYPE refType;
1781 if (S_OK != classInfo->GetRefTypeOfImplType(index, &refType))
1782 continue;
1783
1784 int flags = 0;
1785 classInfo->GetImplTypeFlags(index, &flags);
1786 if (flags & IMPLTYPEFLAG_FRESTRICTED)
1787 continue;
1788
1789 ITypeInfo *interfaceInfo = 0;
1790 classInfo->GetRefTypeInfo(refType, &interfaceInfo);
1791 if (!interfaceInfo)
1792 continue;
1793
1794 interfaceInfo->GetDocumentation(-1, &bstr, 0, 0, 0);
1795 QString interfaceName = QString::fromWCharArray(bstr);
1796 SysFreeString(bstr);
1797 QByteArray key;
1798
1799 TYPEATTR *typeattr = 0;
1800 interfaceInfo->GetTypeAttr(&typeattr);
1801
1802 if (flags & IMPLTYPEFLAG_FSOURCE) {
1803 if (typeattr && !(typeattr->wTypeFlags & TYPEFLAG_FHIDDEN))
1804 key = "Event Interface " + QByteArray::number(index);
1805 generator.readEventInterface(interfaceInfo, 0);
1806 } else {
1807 if (typeattr && !(typeattr->wTypeFlags & TYPEFLAG_FHIDDEN))
1808 key = "Interface " + QByteArray::number(index);
1809 generator.readFuncsInfo(interfaceInfo, 0);
1810 generator.readVarsInfo(interfaceInfo, 0);
1811 }
1812 if (!key.isEmpty())
1813 generator.addClassInfo(key.data(), interfaceName.toLatin1());
1814
1815 if (typeattr)
1816 interfaceInfo->ReleaseTypeAttr(typeattr);
1817 interfaceInfo->Release();
1818 }
1819 }
1820
1821 return generator.metaObject(parentObject, className.toLatin1());
1822}
1823
1824MetaObjectGenerator::MetaObjectGenerator(QAxBase *ax, QAxBasePrivate *dptr)
1825: that(ax), d(dptr), disp(0), dispInfo(0), classInfo(0), typelib(0),
1826 iidnames(QLatin1String("HKEY_LOCAL_MACHINE\\Software\\Classes"), QSettings::NativeFormat)
1827{
1828 init();
1829}
1830
1831MetaObjectGenerator::MetaObjectGenerator(ITypeLib *tlib, ITypeInfo *tinfo)
1832: that(0), d(0), disp(0), dispInfo(tinfo), classInfo(0), typelib(tlib),
1833 iidnames(QLatin1String("HKEY_LOCAL_MACHINE\\Software\\Classes"), QSettings::NativeFormat)
1834{
1835 init();
1836
1837 if (dispInfo)
1838 dispInfo->AddRef();
1839 if (typelib) {
1840 typelib->AddRef();
1841 BSTR bstr;
1842 typelib->GetDocumentation(-1, &bstr, 0, 0, 0);
1843 current_typelib = QString::fromWCharArray(bstr).toLatin1();
1844 SysFreeString(bstr);
1845 }
1846 readClassInfo();
1847}
1848
1849void MetaObjectGenerator::init()
1850{
1851 if (d)
1852 disp = d->dispatch();
1853
1854 iid_propNotifySink = IID_IPropertyNotifySink;
1855
1856 addSignal("signal(QString,int,void*)", "name,argc,argv");
1857 addSignal("exception(int,QString,QString,QString)", "code,source,disc,help");
1858 addSignal("propertyChanged(QString)", "name");
1859 if (d || dispInfo) {
1860 addProperty("QString", "control", Readable|Writable|Designable|Scriptable|Stored|Editable|StdCppSet);
1861 }
1862}
1863
1864MetaObjectGenerator::~MetaObjectGenerator()
1865{
1866 if (dispInfo) dispInfo->Release();
1867 if (classInfo) classInfo->Release();
1868 if (typelib) typelib->Release();
1869}
1870
1871bool qax_dispatchEqualsIDispatch = true;
1872QList<QByteArray> qax_qualified_usertypes;
1873
1874QByteArray MetaObjectGenerator::usertypeToString(const TYPEDESC &tdesc, ITypeInfo *info, const QByteArray &function)
1875{
1876 HREFTYPE usertype = tdesc.hreftype;
1877 if (tdesc.vt != VT_USERDEFINED)
1878 return 0;
1879
1880 QByteArray typeName;
1881 ITypeInfo *usertypeinfo = 0;
1882 info->GetRefTypeInfo(usertype, &usertypeinfo);
1883 if (usertypeinfo) {
1884 ITypeLib *usertypelib = 0;
1885 UINT index;
1886 usertypeinfo->GetContainingTypeLib(&usertypelib, &index);
1887 if (usertypelib) {
1888 // get type library name
1889 BSTR typelibname = 0;
1890 usertypelib->GetDocumentation(-1, &typelibname, 0, 0, 0);
1891 QByteArray typeLibName = QString::fromWCharArray(typelibname).toLatin1();
1892 SysFreeString(typelibname);
1893
1894 // get type name
1895 BSTR usertypename = 0;
1896 usertypelib->GetDocumentation(index, &usertypename, 0, 0, 0);
1897 QByteArray userTypeName = QString::fromWCharArray(usertypename).toLatin1();
1898 SysFreeString(usertypename);
1899
1900 if (hasEnum(userTypeName)) // known enum?
1901 typeName = userTypeName;
1902 else if (userTypeName == "OLE_COLOR" || userTypeName == "VB_OLE_COLOR")
1903 typeName = "QColor";
1904 else if (userTypeName == "IFontDisp" || userTypeName == "IFontDisp*" || userTypeName == "IFont" || userTypeName == "IFont*")
1905 typeName = "QFont";
1906 else if (userTypeName == "Picture" || userTypeName == "Picture*")
1907 typeName = "QPixmap";
1908
1909 if (typeName.isEmpty()) {
1910 TYPEATTR *typeattr = 0;
1911 usertypeinfo->GetTypeAttr(&typeattr);
1912 if (typeattr) {
1913 switch(typeattr->typekind) {
1914 case TKIND_ALIAS:
1915 userTypeName = guessTypes(typeattr->tdescAlias, usertypeinfo, function);
1916 break;
1917 case TKIND_DISPATCH:
1918 case TKIND_COCLASS:
1919 if (qax_dispatchEqualsIDispatch) {
1920 userTypeName = "IDispatch";
1921 } else {
1922 if (typeLibName != current_typelib)
1923 userTypeName = typeLibName + "::" + userTypeName;
1924 if (!qax_qualified_usertypes.contains(userTypeName))
1925 qax_qualified_usertypes << userTypeName;
1926 }
1927 break;
1928 case TKIND_ENUM:
1929 if (typeLibName != current_typelib)
1930 userTypeName = typeLibName + "::" + userTypeName;
1931 if (!qax_qualified_usertypes.contains("enum " + userTypeName))
1932 qax_qualified_usertypes << "enum " + userTypeName;
1933 break;
1934 case TKIND_INTERFACE:
1935 if (typeLibName != current_typelib)
1936 userTypeName = typeLibName + "::" + userTypeName;
1937 if (!qax_qualified_usertypes.contains(userTypeName))
1938 qax_qualified_usertypes << userTypeName;
1939 break;
1940 case TKIND_RECORD:
1941 if (!qax_qualified_usertypes.contains("struct " + userTypeName))
1942 qax_qualified_usertypes << "struct "+ userTypeName;
1943 break;
1944 default:
1945 break;
1946 }
1947 }
1948
1949 usertypeinfo->ReleaseTypeAttr(typeattr);
1950 typeName = userTypeName;
1951 }
1952 usertypelib->Release();
1953 }
1954 usertypeinfo->Release();
1955 }
1956
1957 return typeName;
1958}
1959
1960#define VT_UNHANDLED(x) case VT_##x: qWarning("QAxBase: Unhandled type %s", #x); str = #x; break;
1961
1962QByteArray MetaObjectGenerator::guessTypes(const TYPEDESC &tdesc, ITypeInfo *info, const QByteArray &function)
1963{
1964 QByteArray str;
1965 switch (tdesc.vt) {
1966 case VT_EMPTY:
1967 case VT_VOID:
1968 break;
1969 case VT_LPWSTR:
1970 str = "wchar_t *";
1971 break;
1972 case VT_BSTR:
1973 str = "QString";
1974 break;
1975 case VT_BOOL:
1976 str = "bool";
1977 break;
1978 case VT_I1:
1979 str = "char";
1980 break;
1981 case VT_I2:
1982 str = "short";
1983 break;
1984 case VT_I4:
1985 case VT_INT:
1986 str = "int";
1987 break;
1988 case VT_I8:
1989 str = "qlonglong";
1990 break;
1991 case VT_UI1:
1992 case VT_UI2:
1993 case VT_UI4:
1994 case VT_UINT:
1995 str = "uint";
1996 break;
1997 case VT_UI8:
1998 str = "qulonglong";
1999 break;
2000 case VT_CY:
2001 str = "qlonglong";
2002 break;
2003 case VT_R4:
2004 str = "float";
2005 break;
2006 case VT_R8:
2007 str = "double";
2008 break;
2009 case VT_DATE:
2010 str = "QDateTime";
2011 break;
2012 case VT_DISPATCH:
2013 str = "IDispatch*";
2014 break;
2015 case VT_VARIANT:
2016 str = "QVariant";
2017 break;
2018 case VT_UNKNOWN:
2019 str = "IUnknown*";
2020 break;
2021 case VT_HRESULT:
2022 str = "HRESULT";
2023 break;
2024 case VT_PTR:
2025 str = guessTypes(*tdesc.lptdesc, info, function);
2026 switch(tdesc.lptdesc->vt) {
2027 case VT_VOID:
2028 str = "void*";
2029 break;
2030 case VT_VARIANT:
2031 case VT_BSTR:
2032 case VT_I1:
2033 case VT_I2:
2034 case VT_I4:
2035 case VT_I8:
2036 case VT_UI1:
2037 case VT_UI2:
2038 case VT_UI4:
2039 case VT_UI8:
2040 case VT_BOOL:
2041 case VT_R4:
2042 case VT_R8:
2043 case VT_INT:
2044 case VT_UINT:
2045 case VT_CY:
2046 str += '&';
2047 break;
2048 case VT_PTR:
2049 if (str == "QFont" || str == "QPixmap") {
2050 str += '&';
2051 break;
2052 } else if (str == "void*") {
2053 str = "void **";
2054 break;
2055 }
2056 // FALLTHROUGH
2057 default:
2058 if (str == "QColor")
2059 str += '&';
2060 else if (str == "QDateTime")
2061 str += '&';
2062 else if (str == "QVariantList")
2063 str += '&';
2064 else if (str == "QByteArray")
2065 str += '&';
2066 else if (str == "QStringList")
2067 str += '&';
2068 else if (!str.isEmpty() && hasEnum(str))
2069 str += '&';
2070 else if (!str.isEmpty() && str != "QFont" && str != "QPixmap" && str != "QVariant")
2071 str += '*';
2072 }
2073 break;
2074 case VT_SAFEARRAY:
2075 switch(tdesc.lpadesc->tdescElem.vt) {
2076 // some shortcuts, and generic support for lists of QVariant-supported types
2077 case VT_UI1:
2078 str = "QByteArray";
2079 break;
2080 case VT_BSTR:
2081 str = "QStringList";
2082 break;
2083 case VT_VARIANT:
2084 str = "QVariantList";
2085 break;
2086 default:
2087 str = guessTypes(tdesc.lpadesc->tdescElem, info, function);
2088 if (!str.isEmpty())
2089 str = "QList<" + str + '>';
2090 break;
2091 }
2092 break;
2093 case VT_CARRAY:
2094 str = guessTypes(tdesc.lpadesc->tdescElem, info, function);
2095 if (!str.isEmpty()) {
2096 for (int index = 0; index < tdesc.lpadesc->cDims; ++index)
2097 str += '[' + QByteArray::number((int)tdesc.lpadesc->rgbounds[index].cElements) + ']';
2098 }
2099 break;
2100 case VT_USERDEFINED:
2101 str = usertypeToString(tdesc, info, function);
2102 break;
2103
2104 VT_UNHANDLED(FILETIME);
2105 VT_UNHANDLED(BLOB);
2106 VT_UNHANDLED(ERROR);
2107 VT_UNHANDLED(DECIMAL);
2108 VT_UNHANDLED(LPSTR);
2109 default:
2110 break;
2111 }
2112
2113 if (tdesc.vt & VT_BYREF)
2114 str += '&';
2115
2116 str.replace("&*", "**");
2117 return str;
2118}
2119
2120void MetaObjectGenerator::readClassInfo()
2121{
2122 // Read class information
2123 IProvideClassInfo *provideClassInfo = 0;
2124 if (d)
2125 d->ptr->QueryInterface(IID_IProvideClassInfo, (void**)&provideClassInfo);
2126 if (provideClassInfo) {
2127 provideClassInfo->GetClassInfo(&classInfo);
2128 TYPEATTR *typeattr = 0;
2129 if (classInfo)
2130 classInfo->GetTypeAttr(&typeattr);
2131
2132 QString coClassID;
2133 if (typeattr) {
2134 QUuid clsid(typeattr->guid);
2135 coClassID = clsid.toString().toUpper();
2136#ifndef QAX_NO_CLASSINFO
2137 // UUID
2138 if (d->useClassInfo && !hasClassInfo("CoClass")) {
2139 QString coClassIDstr = iidnames.value(QLatin1String("/CLSID/") + coClassID + QLatin1String("/Default"), coClassID).toString();
2140 addClassInfo("CoClass", coClassIDstr.isEmpty() ? coClassID.toLatin1() : coClassIDstr.toLatin1());
2141 QByteArray version = QByteArray::number(typeattr->wMajorVerNum) + '.' + QByteArray::number(typeattr->wMinorVerNum);
2142 if (version != "0.0")
2143 addClassInfo("Version", version);
2144 }
2145#endif
2146 classInfo->ReleaseTypeAttr(typeattr);
2147 }
2148 provideClassInfo->Release();
2149 provideClassInfo = 0;
2150
2151 if (d->tryCache && !coClassID.isEmpty())
2152 cacheKey = QString::fromLatin1("%1$%2$%3$%4").arg(coClassID)
2153 .arg((int)d->useEventSink).arg((int)d->useClassInfo).arg((int)qax_dispatchEqualsIDispatch);
2154 }
2155
2156 UINT index = 0;
2157 if (disp && !dispInfo)
2158 disp->GetTypeInfo(index, LOCALE_USER_DEFAULT, &dispInfo);
2159
2160 if (dispInfo && !typelib)
2161 dispInfo->GetContainingTypeLib(&typelib, &index);
2162
2163 if (!typelib) {
2164 QSettings controls(QLatin1String("HKEY_LOCAL_MACHINE\\Software"), QSettings::NativeFormat);
2165 QString tlid = controls.value(QLatin1String("/Classes/CLSID/") + that->control() + QLatin1String("/TypeLib/.")).toString();
2166 QString tlfile;
2167 if (!tlid.isEmpty()) {
2168 controls.beginGroup(QLatin1String("/Classes/TypeLib/") + tlid);
2169 QStringList versions = controls.childGroups();
2170 QStringList::Iterator vit = versions.begin();
2171 while (tlfile.isEmpty() && vit != versions.end()) {
2172 QString version = *vit;
2173 ++vit;
2174 tlfile = controls.value(QLatin1Char('/') + version + QLatin1String("/0/win32/.")).toString();
2175 }
2176 controls.endGroup();
2177 } else {
2178 tlfile = controls.value(QLatin1String("/Classes/CLSID/") + that->control() + QLatin1String("/InprocServer32/.")).toString();
2179 if (tlfile.isEmpty())
2180 tlfile = controls.value(QLatin1String("/Classes/CLSID/") + that->control() + QLatin1String("/LocalServer32/.")).toString();
2181 }
2182 if (!tlfile.isEmpty()) {
2183 LoadTypeLib((OLECHAR*)tlfile.utf16(), &typelib);
2184 if (!typelib) {
2185 tlfile = tlfile.left(tlfile.lastIndexOf(QLatin1Char('.'))) + QLatin1String(".tlb");
2186 LoadTypeLib((OLECHAR*)tlfile.utf16(), &typelib);
2187 }
2188 if (!typelib) {
2189 tlfile = tlfile.left(tlfile.lastIndexOf(QLatin1Char('.'))) + QLatin1String(".olb");
2190 LoadTypeLib((OLECHAR*)tlfile.utf16(), &typelib);
2191 }
2192 }
2193 }
2194
2195 if (!classInfo && typelib && that)
2196 typelib->GetTypeInfoOfGuid(QUuid(that->control()), &classInfo);
2197
2198 if (classInfo && !dispInfo) {
2199 TYPEATTR *classAttr;
2200 classInfo->GetTypeAttr(&classAttr);
2201 if (classAttr) {
2202 for (int i = 0; i < classAttr->cImplTypes; ++i) {
2203 int typeFlags = 0;
2204 classInfo->GetImplTypeFlags(i, &typeFlags);
2205 if (typeFlags & IMPLTYPEFLAG_FSOURCE)
2206 continue;
2207
2208 HREFTYPE hrefType;
2209 if (S_OK == classInfo->GetRefTypeOfImplType(i, &hrefType))
2210 classInfo->GetRefTypeInfo(hrefType, &dispInfo);
2211 if (dispInfo) {
2212 TYPEATTR *ifaceAttr;
2213 dispInfo->GetTypeAttr(&ifaceAttr);
2214 WORD typekind = ifaceAttr->typekind;
2215 dispInfo->ReleaseTypeAttr(ifaceAttr);
2216
2217 if (typekind & TKIND_DISPATCH) {
2218 break;
2219 } else {
2220 dispInfo->Release();
2221 dispInfo = 0;
2222 }
2223 }
2224 }
2225 classInfo->ReleaseTypeAttr(classAttr);
2226 }
2227 }
2228
2229 if (!d || !dispInfo || !cacheKey.isEmpty() || !d->tryCache)
2230 return;
2231
2232 TYPEATTR *typeattr = 0;
2233 dispInfo->GetTypeAttr(&typeattr);
2234
2235 QString interfaceID;
2236 if (typeattr) {
2237 QUuid iid(typeattr->guid);
2238 interfaceID = iid.toString().toUpper();
2239
2240 dispInfo->ReleaseTypeAttr(typeattr);
2241 // ### event interfaces!!
2242 if (!interfaceID.isEmpty())
2243 cacheKey = QString::fromLatin1("%1$%2$%3$%4").arg(interfaceID)
2244 .arg((int)d->useEventSink).arg((int)d->useClassInfo).arg((int)qax_dispatchEqualsIDispatch);
2245 }
2246}
2247
2248void MetaObjectGenerator::readEnumInfo()
2249{
2250 if (!typelib)
2251 return;
2252
2253 QUuid libUuid;
2254
2255 if (d && d->tryCache) {
2256 TLIBATTR *libAttr = 0;
2257 typelib->GetLibAttr(&libAttr);
2258 if (libAttr) {
2259 libUuid = QUuid(libAttr->guid);
2260 typelib->ReleaseTLibAttr(libAttr);
2261 enum_list = enum_cache.value(libUuid);
2262 if (!enum_list.isEmpty())
2263 return;
2264 }
2265 }
2266
2267 int valueindex = 0;
2268 QSet<QString> clashCheck;
2269 int clashIndex = 0;
2270
2271 int enum_serial = 0;
2272 UINT index = typelib->GetTypeInfoCount();
2273 for (UINT i = 0; i < index; ++i) {
2274 TYPEKIND typekind;
2275 typelib->GetTypeInfoType(i, &typekind);
2276 if (typekind == TKIND_ENUM) {
2277 // Get the type information for the enum
2278 ITypeInfo *enuminfo = 0;
2279 typelib->GetTypeInfo(i, &enuminfo);
2280 if (!enuminfo)
2281 continue;
2282
2283 // Get the name of the enumeration
2284 BSTR enumname;
2285 QByteArray enumName;
2286 if (typelib->GetDocumentation(i, &enumname, 0, 0, 0) == S_OK) {
2287 enumName = QString::fromWCharArray(enumname).toLatin1();
2288 SysFreeString(enumname);
2289 } else {
2290 enumName = "enum" + QByteArray::number(++enum_serial);
2291 }
2292
2293 // Get the attributes of the enum type
2294 TYPEATTR *typeattr = 0;
2295 enuminfo->GetTypeAttr(&typeattr);
2296 if (typeattr) {
2297 // Get all values of the enumeration
2298 for (UINT vd = 0; vd < (UINT)typeattr->cVars; ++vd) {
2299 VARDESC *vardesc = 0;
2300 enuminfo->GetVarDesc(vd, &vardesc);
2301 if (vardesc && vardesc->varkind == VAR_CONST) {
2302 int value = vardesc->lpvarValue->lVal;
2303 int memid = vardesc->memid;
2304 // Get the name of the value
2305 BSTR valuename;
2306 QByteArray valueName;
2307 UINT maxNamesOut;
2308 enuminfo->GetNames(memid, &valuename, 1, &maxNamesOut);
2309 if (maxNamesOut) {
2310 valueName = QString::fromWCharArray(valuename).toLatin1();
2311 SysFreeString(valuename);
2312 } else {
2313 valueName = "value" + QByteArray::number(valueindex++);
2314 }
2315
2316 if (clashCheck.contains(QString::fromLatin1(valueName)))
2317 valueName += QByteArray::number(++clashIndex);
2318
2319 clashCheck.insert(QString::fromLatin1(valueName));
2320 addEnumValue(enumName, valueName, value);
2321 }
2322 enuminfo->ReleaseVarDesc(vardesc);
2323 }
2324 }
2325 enuminfo->ReleaseTypeAttr(typeattr);
2326 enuminfo->Release();
2327 }
2328 }
2329
2330 if (!libUuid.isNull())
2331 enum_cache.insert(libUuid, enum_list);
2332}
2333
2334void MetaObjectGenerator::addChangedSignal(const QByteArray &function, const QByteArray &type, int memid)
2335{
2336 QAxEventSink *eventSink = 0;
2337 if (d) {
2338 eventSink = d->eventSink.value(iid_propNotifySink);
2339 if (!eventSink && d->useEventSink) {
2340 eventSink = new QAxEventSink(that);
2341 d->eventSink.insert(iid_propNotifySink, eventSink);
2342 }
2343 }
2344 // generate changed signal
2345 QByteArray signalName(function);
2346 signalName += "Changed";
2347 QByteArray signalProto = signalName + '(' + replaceType(type) + ')';
2348 if (!hasSignal(signalProto))
2349 addSignal(signalProto, function);
2350 if (eventSink)
2351 eventSink->addProperty(memid, function, signalProto);
2352}
2353
2354void MetaObjectGenerator::addSetterSlot(const QByteArray &property)
2355{
2356 QByteArray set;
2357 QByteArray prototype(property);
2358 if (isupper(prototype.at(0))) {
2359 set = "Set";
2360 } else {
2361 set = "set";
2362 prototype[0] = toupper(prototype[0]);
2363 }
2364 prototype = set + prototype + '(' + propertyType(property) + ')';
2365 if (!hasSlot(prototype))
2366 addSlot(0, prototype, property);
2367}
2368
2369QByteArray MetaObjectGenerator::createPrototype(FUNCDESC *funcdesc, ITypeInfo *typeinfo, const QList<QByteArray> &names,
2370 QByteArray &type, QList<QByteArray> &parameters)
2371{
2372 QByteArray prototype;
2373 QByteArray function(names.at(0));
2374 const QByteArray hresult("HRESULT");
2375 // get function prototype
2376 type = guessTypes(funcdesc->elemdescFunc.tdesc, typeinfo, function);
2377 if ((type.isEmpty() || type == hresult) && funcdesc->invkind == INVOKE_PROPERTYPUT && funcdesc->lprgelemdescParam) {
2378 type = guessTypes(funcdesc->lprgelemdescParam->tdesc, typeinfo, function);
2379 }
2380
2381 prototype = function + '(';
2382 if (funcdesc->invkind == INVOKE_FUNC && type == hresult)
2383 type = 0;
2384
2385 int p;
2386 for (p = 1; p < names.count(); ++p) {
2387 // parameter
2388 QByteArray paramName = names.at(p);
2389 bool optional = p > (funcdesc->cParams - funcdesc->cParamsOpt);
2390 TYPEDESC tdesc = funcdesc->lprgelemdescParam[p-1].tdesc;
2391 PARAMDESC pdesc = funcdesc->lprgelemdescParam[p-1].paramdesc;
2392
2393 QByteArray ptype = guessTypes(tdesc, typeinfo, function);
2394 if (pdesc.wParamFlags & PARAMFLAG_FRETVAL) {
2395 if (ptype.endsWith('&')) {
2396 ptype.truncate(ptype.length() - 1);
2397 } else if (ptype.endsWith("**")) {
2398 ptype.truncate(ptype.length() - 1);
2399 }
2400 type = ptype;
2401 } else {
2402 prototype += ptype;
2403 if (pdesc.wParamFlags & PARAMFLAG_FOUT && !ptype.endsWith('&') && !ptype.endsWith("**"))
2404 prototype += '&';
2405 if (optional || pdesc.wParamFlags & PARAMFLAG_FOPT)
2406 paramName += "=0";
2407 else if (pdesc.wParamFlags & PARAMFLAG_FHASDEFAULT) {
2408 // ### get the value from pdesc.pparamdescex
2409 paramName += "=0";
2410 }
2411 parameters << paramName;
2412 }
2413 if (p < funcdesc->cParams && !(pdesc.wParamFlags & PARAMFLAG_FRETVAL))
2414 prototype += ',';
2415 }
2416
2417 if (!prototype.isEmpty()) {
2418 if (prototype.endsWith(',')) {
2419 if (funcdesc->invkind == INVOKE_PROPERTYPUT && p == funcdesc->cParams) {
2420 TYPEDESC tdesc = funcdesc->lprgelemdescParam[p-1].tdesc;
2421 QByteArray ptype = guessTypes(tdesc, typeinfo, function);
2422 prototype += ptype;
2423 prototype += ')';
2424 parameters << "rhs";
2425 } else {
2426 prototype[prototype.length()-1] = ')';
2427 }
2428 } else {
2429 prototype += ')';
2430 }
2431 }
2432
2433 return prototype;
2434}
2435
2436void MetaObjectGenerator::readFuncsInfo(ITypeInfo *typeinfo, ushort nFuncs)
2437{
2438 if (!nFuncs) {
2439 TYPEATTR *typeattr = 0;
2440 typeinfo->GetTypeAttr(&typeattr);
2441 if (typeattr) {
2442 nFuncs = typeattr->cFuncs;
2443 typeinfo->ReleaseTypeAttr(typeattr);
2444 }
2445 }
2446
2447 // get information about all functions
2448 for (ushort fd = 0; fd < nFuncs ; ++fd) {
2449 FUNCDESC *funcdesc = 0;
2450 typeinfo->GetFuncDesc(fd, &funcdesc);
2451 if (!funcdesc)
2452 break;
2453
2454 QByteArray function;
2455 QByteArray type;
2456 QByteArray prototype;
2457 QList<QByteArray> parameters;
2458
2459 // parse function description
2460 BSTR bstrNames[256];
2461 UINT maxNames = 255;
2462 UINT maxNamesOut;
2463 typeinfo->GetNames(funcdesc->memid, (BSTR*)&bstrNames, maxNames, &maxNamesOut);
2464 QList<QByteArray> names;
2465 int p;
2466 for (p = 0; p < (int)maxNamesOut; ++p) {
2467 names << QString::fromWCharArray(bstrNames[p]).toLatin1();
2468 SysFreeString(bstrNames[p]);
2469 }
2470
2471 // function name
2472 function = names.at(0);
2473 if ((maxNamesOut == 3 && function == "QueryInterface") ||
2474 (maxNamesOut == 1 && function == "AddRef") ||
2475 (maxNamesOut == 1 && function == "Release") ||
2476 (maxNamesOut == 9 && function == "Invoke") ||
2477 (maxNamesOut == 6 && function == "GetIDsOfNames") ||
2478 (maxNamesOut == 2 && function == "GetTypeInfoCount") ||
2479 (maxNamesOut == 4 && function == "GetTypeInfo")) {
2480 typeinfo->ReleaseFuncDesc(funcdesc);
2481 continue;
2482 }
2483
2484 prototype = createPrototype(/*in*/ funcdesc, typeinfo, names, /*out*/type, parameters);
2485
2486 // get type of function
2487 switch(funcdesc->invkind) {
2488 case INVOKE_PROPERTYGET: // property
2489 case INVOKE_PROPERTYPUT:
2490 if (funcdesc->cParams - funcdesc->cParamsOpt <= 1) {
2491 bool dontBreak = false;
2492 // getter with non-default-parameters -> fall through to function handling
2493 if (funcdesc->invkind == INVOKE_PROPERTYGET && parameters.count() && funcdesc->cParams - funcdesc->cParamsOpt) {
2494 dontBreak = true;
2495 } else {
2496 uint flags = Readable;
2497 if (funcdesc->invkind != INVOKE_PROPERTYGET)
2498 flags |= Writable;
2499 if (!(funcdesc->wFuncFlags & (FUNCFLAG_FNONBROWSABLE | FUNCFLAG_FHIDDEN)))
2500 flags |= Designable;
2501 if (!(funcdesc->wFuncFlags & FUNCFLAG_FRESTRICTED))
2502 flags |= Scriptable;
2503 if (funcdesc->wFuncFlags & FUNCFLAG_FREQUESTEDIT)
2504 flags |= RequestingEdit;
2505 if (hasEnum(type))
2506 flags |= EnumOrFlag;
2507
2508 if (funcdesc->wFuncFlags & FUNCFLAG_FBINDABLE && funcdesc->invkind == INVOKE_PROPERTYGET) {
2509 addChangedSignal(function, type, funcdesc->memid);
2510 flags |= Bindable;
2511 }
2512 // Don't generate code for properties without type
2513 if (type.isEmpty())
2514 break;
2515 addProperty(type, function, flags);
2516
2517 // more parameters -> function handling
2518 if (funcdesc->invkind == INVOKE_PROPERTYGET && funcdesc->cParams)
2519 dontBreak = true;
2520 }
2521
2522 if (!funcdesc->cParams) {
2523 // don't generate slots for incomplete properties
2524 if (type.isEmpty())
2525 break;
2526
2527 // Done for getters
2528 if (funcdesc->invkind == INVOKE_PROPERTYGET)
2529 break;
2530
2531 // generate setter slot
2532 if (funcdesc->invkind == INVOKE_PROPERTYPUT && hasProperty(function)) {
2533 addSetterSlot(function);
2534 break;
2535 }
2536 } else if (funcdesc->invkind == INVOKE_PROPERTYPUT && hasProperty(function)) {
2537 addSetterSlot(function);
2538 // more parameters -> function handling
2539 if (funcdesc->cParams > 1)
2540 dontBreak = true;
2541 }
2542 if (!dontBreak)
2543 break;
2544 }
2545 if (funcdesc->invkind == INVOKE_PROPERTYPUT) {
2546 QByteArray set;
2547 if (isupper(prototype.at(0))) {
2548 set = "Set";
2549 } else {
2550 set = "set";
2551 prototype[0] = toupper(prototype[0]);
2552 }
2553
2554 prototype = set + prototype;
2555 }
2556 // FALL THROUGH to support multi-variat properties
2557 case INVOKE_FUNC: // method
2558 {
2559 bool cloned = false;
2560 bool defargs;
2561 do {
2562 QByteArray pnames;
2563 for (p = 0; p < parameters.count(); ++p) {
2564 pnames += parameters.at(p);
2565 if (p < parameters.count() - 1)
2566 pnames += ',';
2567 }
2568 defargs = pnames.contains("=0");
2569 int flags = QMetaMethod::Public;
2570 if (cloned)
2571 flags |= QMetaMethod::Cloned << 4;
2572 cloned |= defargs;
2573 addSlot(type, prototype, pnames.replace("=0", ""), flags);
2574
2575 if (defargs) {
2576 parameters.takeLast();
2577 int lastParam = prototype.lastIndexOf(',');
2578 if (lastParam == -1)
2579 lastParam = prototype.indexOf('(') + 1;
2580 prototype.truncate(lastParam);
2581 prototype += ')';
2582 }
2583 } while (defargs);
2584 }
2585 break;
2586
2587 default:
2588 break;
2589 }
2590#if 0 // documentation in metaobject would be cool?
2591 // get function documentation
2592 BSTR bstrDocu;
2593 info->GetDocumentation(funcdesc->memid, 0, &bstrDocu, 0, 0);
2594 QString strDocu = QString::fromWCharArray(bstrDocu);
2595 SysFreeString(bstrDocu);
2596 if (!!strDocu)
2597 desc += '[' + strDocu + ']';
2598 desc += '\n';
2599#endif
2600 typeinfo->ReleaseFuncDesc(funcdesc);
2601 }
2602}
2603
2604void MetaObjectGenerator::readVarsInfo(ITypeInfo *typeinfo, ushort nVars)
2605{
2606 if (!nVars) {
2607 TYPEATTR *typeattr = 0;
2608 typeinfo->GetTypeAttr(&typeattr);
2609 if (typeattr) {
2610 nVars = typeattr->cVars;
2611 typeinfo->ReleaseTypeAttr(typeattr);
2612 }
2613 }
2614
2615 // get information about all variables
2616 for (ushort vd = 0; vd < nVars; ++vd) {
2617 VARDESC *vardesc;
2618 typeinfo->GetVarDesc(vd, &vardesc);
2619 if (!vardesc)
2620 break;
2621
2622 // no use if it's not a dispatched variable
2623 if (vardesc->varkind != VAR_DISPATCH) {
2624 typeinfo->ReleaseVarDesc(vardesc);
2625 continue;
2626 }
2627
2628 // get variable name
2629 BSTR bstrName;
2630 UINT maxNames = 1;
2631 UINT maxNamesOut;
2632 typeinfo->GetNames(vardesc->memid, &bstrName, maxNames, &maxNamesOut);
2633 if (maxNamesOut != 1 || !bstrName) {
2634 typeinfo->ReleaseVarDesc(vardesc);
2635 continue;
2636 }
2637 QByteArray variableType;
2638 QByteArray variableName;
2639 uint flags = 0;
2640
2641 variableName = QString::fromWCharArray(bstrName).toLatin1();
2642 SysFreeString(bstrName);
2643
2644 // get variable type
2645 TYPEDESC typedesc = vardesc->elemdescVar.tdesc;
2646 variableType = guessTypes(typedesc, typeinfo, variableName);
2647
2648 // generate meta property
2649 if (!hasProperty(variableName)) {
2650 flags = Readable;
2651 if (!(vardesc->wVarFlags & VARFLAG_FREADONLY))
2652 flags |= Writable;
2653 if (!(vardesc->wVarFlags & (VARFLAG_FNONBROWSABLE | VARFLAG_FHIDDEN)))
2654 flags |= Designable;
2655 if (!(vardesc->wVarFlags & VARFLAG_FRESTRICTED))
2656 flags |= Scriptable;
2657 if (vardesc->wVarFlags & VARFLAG_FREQUESTEDIT)
2658 flags |= RequestingEdit;
2659 if (hasEnum(variableType))
2660 flags |= EnumOrFlag;
2661
2662 if (vardesc->wVarFlags & VARFLAG_FBINDABLE) {
2663 addChangedSignal(variableName, variableType, vardesc->memid);
2664 flags |= Bindable;
2665 }
2666 addProperty(variableType, variableName, flags);
2667 }
2668
2669 // generate a set slot
2670 if (!(vardesc->wVarFlags & VARFLAG_FREADONLY))
2671 addSetterSlot(variableName);
2672
2673#if 0 // documentation in metaobject would be cool?
2674 // get function documentation
2675 BSTR bstrDocu;
2676 info->GetDocumentation(vardesc->memid, 0, &bstrDocu, 0, 0);
2677 QString strDocu = QString::fromWCharArray(bstrDocu);
2678 SysFreeString(bstrDocu);
2679 if (!!strDocu)
2680 desc += '[' + strDocu + ']';
2681 desc += '\n';
2682#endif
2683 typeinfo->ReleaseVarDesc(vardesc);
2684 }
2685}
2686
2687void MetaObjectGenerator::readInterfaceInfo()
2688{
2689 ITypeInfo *typeinfo = dispInfo;
2690 if (!typeinfo)
2691 return;
2692 typeinfo->AddRef();
2693 int interface_serial = 0;
2694 while (typeinfo) {
2695 ushort nFuncs = 0;
2696 ushort nVars = 0;
2697 ushort nImpl = 0;
2698 // get information about type
2699 TYPEATTR *typeattr;
2700 typeinfo->GetTypeAttr(&typeattr);
2701 bool interesting = true;
2702 if (typeattr) {
2703 // get number of functions, variables, and implemented interfaces
2704 nFuncs = typeattr->cFuncs;
2705 nVars = typeattr->cVars;
2706 nImpl = typeattr->cImplTypes;
2707
2708 if ((typeattr->typekind == TKIND_DISPATCH || typeattr->typekind == TKIND_INTERFACE) &&
2709 (typeattr->guid != IID_IDispatch && typeattr->guid != IID_IUnknown)) {
2710#ifndef QAX_NO_CLASSINFO
2711 if (d && d->useClassInfo) {
2712 // UUID
2713 QUuid uuid(typeattr->guid);
2714 QString uuidstr = uuid.toString().toUpper();
2715 uuidstr = iidnames.value(QLatin1String("/Interface/") + uuidstr + QLatin1String("/Default"), uuidstr).toString();
2716 addClassInfo("Interface " + QByteArray::number(++interface_serial), uuidstr.toLatin1());
2717 }
2718#endif
2719 typeinfo->ReleaseTypeAttr(typeattr);
2720 } else {
2721 interesting = false;
2722 typeinfo->ReleaseTypeAttr(typeattr);
2723 }
2724 }
2725
2726 if (interesting) {
2727 readFuncsInfo(typeinfo, nFuncs);
2728 readVarsInfo(typeinfo, nVars);
2729 }
2730
2731 if (!nImpl) {
2732 typeinfo->Release();
2733 typeinfo = 0;
2734 break;
2735 }
2736
2737 // go up one base class
2738 HREFTYPE pRefType;
2739 typeinfo->GetRefTypeOfImplType(0, &pRefType);
2740 ITypeInfo *baseInfo = 0;
2741 typeinfo->GetRefTypeInfo(pRefType, &baseInfo);
2742 typeinfo->Release();
2743 if (typeinfo == baseInfo) { // IUnknown inherits IUnknown ???
2744 baseInfo->Release();
2745 typeinfo = 0;
2746 break;
2747 }
2748 typeinfo = baseInfo;
2749 }
2750}
2751
2752void MetaObjectGenerator::readEventInterface(ITypeInfo *eventinfo, IConnectionPoint *cpoint)
2753{
2754 TYPEATTR *eventattr;
2755 eventinfo->GetTypeAttr(&eventattr);
2756 if (!eventattr)
2757 return;
2758 if (eventattr->typekind != TKIND_DISPATCH) {
2759 eventinfo->ReleaseTypeAttr(eventattr);
2760 return;
2761 }
2762
2763 QAxEventSink *eventSink = 0;
2764 if (d) {
2765 IID conniid;
2766 cpoint->GetConnectionInterface(&conniid);
2767 eventSink = d->eventSink.value(QUuid(conniid));
2768 if (!eventSink) {
2769 eventSink = new QAxEventSink(that);
2770 d->eventSink.insert(QUuid(conniid), eventSink);
2771 eventSink->advise(cpoint, conniid);
2772 }
2773 }
2774
2775 // get information about all event functions
2776 for (UINT fd = 0; fd < (UINT)eventattr->cFuncs; ++fd) {
2777 FUNCDESC *funcdesc;
2778 eventinfo->GetFuncDesc(fd, &funcdesc);
2779 if (!funcdesc)
2780 break;
2781 if (funcdesc->invkind != INVOKE_FUNC ||
2782 funcdesc->funckind != FUNC_DISPATCH) {
2783 eventinfo->ReleaseTypeAttr(eventattr);
2784 eventinfo->ReleaseFuncDesc(funcdesc);
2785 continue;
2786 }
2787
2788 QByteArray function;
2789 QByteArray prototype;
2790 QList<QByteArray> parameters;
2791
2792 // parse event function description
2793 BSTR bstrNames[256];
2794 UINT maxNames = 255;
2795 UINT maxNamesOut;
2796 eventinfo->GetNames(funcdesc->memid, (BSTR*)&bstrNames, maxNames, &maxNamesOut);
2797 QList<QByteArray> names;
2798 int p;
2799 for (p = 0; p < (int)maxNamesOut; ++p) {
2800 names << QString::fromWCharArray(bstrNames[p]).toLatin1();
2801 SysFreeString(bstrNames[p]);
2802 }
2803
2804 // get event function prototype
2805 function = names.at(0);
2806 QByteArray type; // dummy - we don't care about return values for signals
2807 prototype = createPrototype(/*in*/ funcdesc, eventinfo, names, /*out*/type, parameters);
2808 if (!hasSignal(prototype)) {
2809 QByteArray pnames;
2810 for (p = 0; p < parameters.count(); ++p) {
2811 pnames += parameters.at(p);
2812 if (p < parameters.count() - 1)
2813 pnames += ',';
2814 }
2815 addSignal(prototype, pnames);
2816 }
2817 if (eventSink)
2818 eventSink->addSignal(funcdesc->memid, prototype);
2819
2820#if 0 // documentation in metaobject would be cool?
2821 // get function documentation
2822 BSTR bstrDocu;
2823 eventinfo->GetDocumentation(funcdesc->memid, 0, &bstrDocu, 0, 0);
2824 QString strDocu = QString::fromWCharArray(bstrDocu);
2825 SysFreeString(bstrDocu);
2826 if (!!strDocu)
2827 desc += '[' + strDocu + ']';
2828 desc += '\n';
2829#endif
2830 eventinfo->ReleaseFuncDesc(funcdesc);
2831 }
2832 eventinfo->ReleaseTypeAttr(eventattr);
2833}
2834
2835void MetaObjectGenerator::readEventInfo()
2836{
2837 int event_serial = 0;
2838 IConnectionPointContainer *cpoints = 0;
2839 if (d && d->useEventSink)
2840 d->ptr->QueryInterface(IID_IConnectionPointContainer, (void**)&cpoints);
2841 if (cpoints) {
2842 // Get connection point enumerator
2843 IEnumConnectionPoints *epoints = 0;
2844 cpoints->EnumConnectionPoints(&epoints);
2845 if (epoints) {
2846 ULONG c = 1;
2847 IConnectionPoint *cpoint = 0;
2848 epoints->Reset();
2849 QList<QUuid> cpointlist;
2850 do {
2851 if (cpoint) cpoint->Release();
2852 cpoint = 0;
2853 HRESULT hr = epoints->Next(c, &cpoint, &c);
2854 if (!c || hr != S_OK)
2855 break;
2856
2857 IID conniid;
2858 cpoint->GetConnectionInterface(&conniid);
2859 // workaround for typelibrary bug of Word.Application
2860 QUuid connuuid(conniid);
2861 if (cpointlist.contains(connuuid))
2862 break;
2863
2864#ifndef QAX_NO_CLASSINFO
2865 if (d->useClassInfo) {
2866 QString uuidstr = connuuid.toString().toUpper();
2867 uuidstr = iidnames.value(QLatin1String("/Interface/") + uuidstr + QLatin1String("/Default"), uuidstr).toString();
2868 addClassInfo("Event Interface " + QByteArray::number(++event_serial), uuidstr.toLatin1());
2869 }
2870#endif
2871
2872 // get information about type
2873 if (conniid == IID_IPropertyNotifySink) {
2874 // test whether property notify sink has been created already, and advise on it
2875 QAxEventSink *eventSink = d->eventSink.value(iid_propNotifySink);
2876 if (eventSink)
2877 eventSink->advise(cpoint, conniid);
2878 continue;
2879 }
2880
2881 ITypeInfo *eventinfo = 0;
2882 if (typelib)
2883 typelib->GetTypeInfoOfGuid(conniid, &eventinfo);
2884
2885 if (eventinfo) {
2886 // avoid recursion (see workaround above)
2887 cpointlist.append(connuuid);
2888
2889 readEventInterface(eventinfo, cpoint);
2890 eventinfo->Release();
2891 }
2892 } while (c);
2893 if (cpoint) cpoint->Release();
2894 epoints->Release();
2895 } else if (classInfo) { // no enumeration - search source interfaces and ask for those
2896 TYPEATTR *typeattr = 0;
2897 classInfo->GetTypeAttr(&typeattr);
2898 if (typeattr) {
2899 for (int i = 0; i < typeattr->cImplTypes; ++i) {
2900 int flags = 0;
2901 classInfo->GetImplTypeFlags(i, &flags);
2902 if (!(flags & IMPLTYPEFLAG_FSOURCE))
2903 continue;
2904 HREFTYPE reference;
2905 if (S_OK != classInfo->GetRefTypeOfImplType(i, &reference))
2906 continue;
2907 ITypeInfo *eventInfo = 0;
2908 classInfo->GetRefTypeInfo(reference, &eventInfo);
2909 if (!eventInfo)
2910 continue;
2911 TYPEATTR *eventattr = 0;
2912 eventInfo->GetTypeAttr(&eventattr);
2913 if (eventattr) {
2914 IConnectionPoint *cpoint = 0;
2915 cpoints->FindConnectionPoint(eventattr->guid, &cpoint);
2916 if (cpoint) {
2917 if (eventattr->guid == IID_IPropertyNotifySink) {
2918 // test whether property notify sink has been created already, and advise on it
2919 QAxEventSink *eventSink = d->eventSink.value(iid_propNotifySink);
2920 if (eventSink)
2921 eventSink->advise(cpoint, eventattr->guid);
2922 continue;
2923 }
2924
2925 readEventInterface(eventInfo, cpoint);
2926 cpoint->Release();
2927 }
2928 eventInfo->ReleaseTypeAttr(eventattr);
2929 }
2930 eventInfo->Release();
2931 }
2932 classInfo->ReleaseTypeAttr(typeattr);
2933 }
2934 }
2935 cpoints->Release();
2936 }
2937}
2938
2939QMetaObject *MetaObjectGenerator::tryCache()
2940{
2941 if (!cacheKey.isEmpty()) {
2942 d->metaobj = mo_cache.value(cacheKey);
2943 if (d->metaobj) {
2944 d->cachedMetaObject = true;
2945
2946 IConnectionPointContainer *cpoints = 0;
2947 d->ptr->QueryInterface(IID_IConnectionPointContainer, (void**)&cpoints);
2948 if (cpoints) {
2949 QList<QUuid>::ConstIterator it = d->metaobj->connectionInterfaces.begin();
2950 while (it != d->metaobj->connectionInterfaces.end()) {
2951 QUuid iid = *it;
2952 ++it;
2953
2954 IConnectionPoint *cpoint = 0;
2955 cpoints->FindConnectionPoint(iid, &cpoint);
2956 if (cpoint) {
2957 QAxEventSink *sink = new QAxEventSink(that);
2958 sink->advise(cpoint, iid);
2959 d->eventSink.insert(iid, sink);
2960 sink->sigs = d->metaobj->sigs.value(iid);
2961 sink->props = d->metaobj->props.value(iid);
2962 sink->propsigs = d->metaobj->propsigs.value(iid);
2963 cpoint->Release();
2964 }
2965 }
2966 cpoints->Release();
2967 }
2968
2969 return d->metaobj;
2970 }
2971 }
2972 return 0;
2973}
2974
2975QMetaObject *MetaObjectGenerator::metaObject(const QMetaObject *parentObject, const QByteArray &className)
2976{
2977 if (that) {
2978 readClassInfo();
2979 if (typelib) {
2980 BSTR bstr;
2981 typelib->GetDocumentation(-1, &bstr, 0, 0, 0);
2982 current_typelib = QString::fromWCharArray(bstr).toLatin1();
2983 SysFreeString(bstr);
2984 }
2985 if (d->tryCache && tryCache())
2986 return d->metaobj;
2987 readEnumInfo();
2988 readInterfaceInfo();
2989 readEventInfo();
2990 }
2991
2992 current_typelib = QByteArray();
2993
2994#ifndef QAX_NO_CLASSINFO
2995 if (!debugInfo.isEmpty() && d->useClassInfo)
2996 addClassInfo("debugInfo", debugInfo);
2997#endif
2998
2999 QAxMetaObject *metaobj = new QAxMetaObject;
3000
3001 // revision + classname + table + zero terminator
3002 uint int_data_size = 1+1+2+2+2+2+1;
3003
3004 int_data_size += classinfo_list.count() * 2;
3005 int_data_size += signal_list.count() * 5;
3006 int_data_size += slot_list.count() * 5;
3007 int_data_size += property_list.count() * 3;
3008 int_data_size += enum_list.count() * 4;
3009 for (QMap<QByteArray, QList<QPair<QByteArray, int> > >::ConstIterator it = enum_list.begin();
3010 it != enum_list.end(); ++it) {
3011 int_data_size += (*it).count() * 2;
3012 }
3013
3014 uint *int_data = new uint[int_data_size];
3015 int_data[0] = 1; // revision number
3016 int_data[1] = 0; // classname index
3017 int_data[2] = classinfo_list.count(); // num_classinfo
3018 int_data[3] = 10; // idx_classinfo
3019 int_data[4] = signal_list.count() + slot_list.count(); // num_methods
3020 int_data[5] = int_data[3] + int_data[2] * 2; // idx_signals
3021 int_data[6] = property_list.count(); // num_properties
3022 int_data[7] = int_data[5] + int_data[4] * 5; // idx_properties
3023 int_data[8] = enum_list.count(); // num_enums
3024 int_data[9] = int_data[7] + int_data[6] * 3; // idx_enums
3025 int_data[int_data_size - 1] = 0; // eod;
3026
3027 char null('\0');
3028 // data + zero-terminator
3029 QByteArray stringdata = that ? QByteArray(that->className()) : className;
3030 stringdata += null;
3031 stringdata.reserve(8192);
3032
3033 uint offset = int_data[3]; //idx_classinfo
3034
3035 // each class info in form key\0value\0
3036 for (QMap<QByteArray, QByteArray>::ConstIterator it = classinfo_list.begin(); it != classinfo_list.end(); ++it) {
3037 QByteArray key(it.key());
3038 QByteArray value(it.value());
3039 int_data[offset++] = stringdata.length();
3040 stringdata += key;
3041 stringdata += null;
3042 int_data[offset++] = stringdata.length();
3043 stringdata += value;
3044 stringdata += null;
3045 }
3046 Q_ASSERT(offset == int_data[5]);
3047
3048 // each signal in form prototype\0parameters\0type\0tag\0
3049 for (QMap<QByteArray, Method>::ConstIterator it = signal_list.begin(); it != signal_list.end(); ++it) {
3050 QByteArray prototype(QMetaObject::normalizedSignature(it.key()));
3051 QByteArray type(it.value().type);
3052 QByteArray parameters(it.value().parameters);
3053 if (!it.value().realPrototype.isEmpty())
3054 metaobj->realPrototype.insert(prototype, it.value().realPrototype);
3055 QByteArray tag;
3056 int flags = it.value().flags;
3057
3058 int_data[offset++] = stringdata.length();
3059 stringdata += prototype;
3060 stringdata += null;
3061 int_data[offset++] = stringdata.length();
3062 stringdata += parameters;
3063 stringdata += null;
3064 int_data[offset++] = stringdata.length();
3065 stringdata += type;
3066 stringdata += null;
3067 int_data[offset++] = stringdata.length();
3068 stringdata += tag;
3069 stringdata += null;
3070 int_data[offset++] = flags;
3071 }
3072
3073 // each slot in form prototype\0parameters\0type\0tag\0
3074 for (QMap<QByteArray, Method>::ConstIterator it = slot_list.begin(); it != slot_list.end(); ++it) {
3075 QByteArray prototype(QMetaObject::normalizedSignature(it.key()));
3076 QByteArray type(it.value().type);
3077 QByteArray parameters(it.value().parameters);
3078 if (!it.value().realPrototype.isEmpty())
3079 metaobj->realPrototype.insert(prototype, it.value().realPrototype);
3080 QByteArray tag;
3081 int flags = it.value().flags;
3082
3083 int_data[offset++] = stringdata.length();
3084 stringdata += prototype;
3085 stringdata += null;
3086 int_data[offset++] = stringdata.length();
3087 stringdata += parameters;
3088 stringdata += null;
3089 int_data[offset++] = stringdata.length();
3090 stringdata += type;
3091 stringdata += null;
3092 int_data[offset++] = stringdata.length();
3093 stringdata += tag;
3094 stringdata += null;
3095 int_data[offset++] = flags;
3096 }
3097 Q_ASSERT(offset == int_data[7]);
3098
3099 // each property in form name\0type\0
3100 for (QMap<QByteArray, Property>::ConstIterator it = property_list.begin(); it != property_list.end(); ++it) {
3101 QByteArray name(it.key());
3102 QByteArray type(it.value().type);
3103 QByteArray realType(it.value().realType);
3104 if (!realType.isEmpty() && realType != type)
3105 metaobj->realPrototype.insert(name, realType);
3106 uint flags = it.value().typeId;
3107
3108 int_data[offset++] = stringdata.length();
3109 stringdata += name;
3110 stringdata += null;
3111 int_data[offset++] = stringdata.length();
3112 stringdata += type;
3113 stringdata += null;
3114 int_data[offset++] = flags;
3115 }
3116 Q_ASSERT(offset == int_data[9]);
3117
3118 int value_offset = offset + enum_list.count() * 4;
3119 // each enum in form name\0
3120 for (QMap<QByteArray, QList<QPair<QByteArray, int> > >::ConstIterator it = enum_list.begin(); it != enum_list.end(); ++it) {
3121 QByteArray name(it.key());
3122 int flags = 0x0; // 0x1 for flag?
3123 int count = it.value().count();
3124
3125 int_data[offset++] = stringdata.length();
3126 stringdata += name;
3127 stringdata += null;
3128 int_data[offset++] = flags;
3129 int_data[offset++] = count;
3130 int_data[offset++] = value_offset;
3131 value_offset += count * 2;
3132 }
3133 Q_ASSERT(offset == int_data[9] + enum_list.count() * 4);
3134
3135 // each enum value in form key\0
3136 for (QMap<QByteArray, QList<QPair<QByteArray, int> > >::ConstIterator it = enum_list.begin(); it != enum_list.end(); ++it) {
3137 for (QList<QPair<QByteArray,int> >::ConstIterator it2 = it.value().begin(); it2 != it.value().end(); ++it2) {
3138 QByteArray key((*it2).first);
3139 int value = (*it2).second;
3140 int_data[offset++] = stringdata.length();
3141 stringdata += key;
3142 stringdata += null;
3143 int_data[offset++] = value;
3144 }
3145 }
3146 Q_ASSERT(offset == int_data_size-1);
3147
3148 char *string_data = new char[stringdata.length()];
3149 memset(string_data, 0, sizeof(string_data));
3150 memcpy(string_data, stringdata, stringdata.length());
3151
3152 // put the metaobject together
3153 metaobj->d.data = int_data;
3154 metaobj->d.extradata = 0;
3155 metaobj->d.stringdata = string_data;
3156 metaobj->d.superdata = parentObject;
3157
3158 if (d)
3159 d->metaobj = metaobj;
3160
3161 if (!cacheKey.isEmpty()) {
3162 mo_cache.insert(cacheKey, d->metaobj);
3163 d->cachedMetaObject = true;
3164 for (QHash<QUuid, QAxEventSink*>::Iterator it = d->eventSink.begin(); it != d->eventSink.end(); ++it) {
3165 QAxEventSink *sink = it.value();
3166 if (sink) {
3167 QUuid ciid = sink->connectionInterface();
3168
3169 d->metaobj->connectionInterfaces.append(ciid);
3170 d->metaobj->sigs.insert(ciid, sink->signalMap());
3171 d->metaobj->props.insert(ciid, sink->propertyMap());
3172 d->metaobj->propsigs.insert(ciid, sink->propSignalMap());
3173 }
3174 }
3175 }
3176
3177 return metaobj;
3178}
3179
3180static const uint qt_meta_data_QAxBase[] = {
3181
3182 // content:
3183 1, // revision
3184 0, // classname
3185 0, 0, // classinfo
3186 3, 10, // methods
3187 1, 25, // properties
3188 0, 0, // enums/sets
3189
3190 // signals: signature, parameters, type, tag, flags
3191 24, 9, 8, 8, 0x05,
3192 55, 50, 8, 8, 0x05,
3193 102, 80, 8, 8, 0x05,
3194
3195 // properties: name, type, flags
3196 149, 141, 0x0a095103,
3197
3198 0 // eod
3199};
3200
3201static const char qt_meta_stringdata_QAxBase[] = {
3202 "QAxBase\0\0name,argc,argv\0signal(QString,int,void*)\0name\0"
3203 "propertyChanged(QString)\0code,source,desc,help\0"
3204 "exception(int,QString,QString,QString)\0QString\0control\0"
3205};
3206
3207static QMetaObject qaxobject_staticMetaObject = {
3208 { &QObject::staticMetaObject, qt_meta_stringdata_QAxBase,
3209 qt_meta_data_QAxBase, 0 }
3210};
3211static QMetaObject qaxwidget_staticMetaObject = {
3212 { &QWidget::staticMetaObject, qt_meta_stringdata_QAxBase,
3213 qt_meta_data_QAxBase, 0 }
3214};
3215
3216/*!
3217 \internal
3218
3219 The metaobject is generated on the fly from the information
3220 provided by the IDispatch and ITypeInfo interface implementations
3221 in the COM object.
3222*/
3223const QMetaObject *QAxBase::metaObject() const
3224{
3225 if (d->metaobj)
3226 return d->metaobj;
3227 const QMetaObject* parentObject = parentMetaObject();
3228
3229 if (!d->ptr && !d->initialized) {
3230 ((QAxBase*)this)->initialize(&d->ptr);
3231 d->initialized = true;
3232 }
3233
3234#ifndef QT_NO_THREAD
3235 // only one thread at a time can generate meta objects
3236 QMutexLocker locker(&cache_mutex);
3237#endif
3238
3239 // return the default meta object if not yet initialized
3240 if (!d->ptr || !d->useMetaObject) {
3241 if (qObject()->isWidgetType())
3242 return &qaxwidget_staticMetaObject;
3243 return &qaxobject_staticMetaObject;
3244 }
3245 MetaObjectGenerator generator((QAxBase*)this, d);
3246 return generator.metaObject(parentObject);
3247}
3248
3249/*!
3250 \internal
3251
3252 Connects to all event interfaces of the object.
3253
3254 Called by the subclasses' connectNotify() reimplementations, so
3255 at this point the connection as actually been created already.
3256*/
3257void QAxBase::connectNotify()
3258{
3259 if (d->eventSink.count()) // already listening
3260 return;
3261
3262 IEnumConnectionPoints *epoints = 0;
3263 if (d->ptr && d->useEventSink) {
3264 IConnectionPointContainer *cpoints = 0;
3265 d->ptr->QueryInterface(IID_IConnectionPointContainer, (void**)&cpoints);
3266 if (!cpoints)
3267 return;
3268
3269 cpoints->EnumConnectionPoints(&epoints);
3270 cpoints->Release();
3271 }
3272
3273 if (!epoints)
3274 return;
3275
3276 UINT index;
3277 IDispatch *disp = d->dispatch();
3278 ITypeInfo *typeinfo = 0;
3279 ITypeLib *typelib = 0;
3280 if (disp)
3281 disp->GetTypeInfo(0, LOCALE_USER_DEFAULT, &typeinfo);
3282 if (typeinfo)
3283 typeinfo->GetContainingTypeLib(&typelib, &index);
3284
3285 if (!typelib) {
3286 epoints->Release();
3287 return;
3288 }
3289
3290 MetaObjectGenerator generator(this, d);
3291 bool haveEnumInfo = false;
3292
3293 ULONG c = 1;
3294 IConnectionPoint *cpoint = 0;
3295 epoints->Reset();
3296 do {
3297 if (cpoint) cpoint->Release();
3298 cpoint = 0;
3299 epoints->Next(c, &cpoint, &c);
3300 if (!c || !cpoint)
3301 break;
3302
3303 IID conniid;
3304 cpoint->GetConnectionInterface(&conniid);
3305 // workaround for typelibrary bug of Word.Application
3306 QString connuuid(QUuid(conniid).toString());
3307 if (d->eventSink.contains(connuuid))
3308 break;
3309
3310 // Get ITypeInfo for source-interface, and skip if not supporting IDispatch
3311 ITypeInfo *eventinfo = 0;
3312 typelib->GetTypeInfoOfGuid(conniid, &eventinfo);
3313 if (eventinfo) {
3314 TYPEATTR *eventAttr;
3315 eventinfo->GetTypeAttr(&eventAttr);
3316 if (!eventAttr) {
3317 eventinfo->Release();
3318 break;
3319 }
3320
3321 TYPEKIND eventKind = eventAttr->typekind;
3322 eventinfo->ReleaseTypeAttr(eventAttr);
3323 if (eventKind != TKIND_DISPATCH) {
3324 eventinfo->Release();
3325 break;
3326 }
3327 }
3328
3329 // always into the cache to avoid recoursion
3330 QAxEventSink *eventSink = eventinfo ? new QAxEventSink(this) : 0;
3331 d->eventSink.insert(connuuid, eventSink);
3332
3333 if (!eventinfo)
3334 continue;
3335
3336 // have to get type info to support signals with enum parameters
3337 if (!haveEnumInfo) {
3338 bool wasTryCache = d->tryCache;
3339 d->tryCache = true;
3340 generator.readClassInfo();
3341 generator.readEnumInfo();
3342 d->tryCache = wasTryCache;
3343 haveEnumInfo = true;
3344 }
3345 generator.readEventInterface(eventinfo, cpoint);
3346 eventSink->advise(cpoint, conniid);
3347
3348 eventinfo->Release();
3349 } while (c);
3350 if (cpoint) cpoint->Release();
3351 epoints->Release();
3352
3353 typelib->Release();
3354
3355 // make sure we don't try again
3356 if (!d->eventSink.count())
3357 d->eventSink.insert(QString(), 0);
3358}
3359
3360/*!
3361 \fn QString QAxBase::generateDocumentation()
3362
3363 Returns a rich text string with documentation for the
3364 wrapped COM object. Dump the string to an HTML-file,
3365 or use it in e.g. a QTextBrowser widget.
3366*/
3367
3368static bool checkHRESULT(HRESULT hres, EXCEPINFO *exc, QAxBase *that, const QString &name, uint argerr)
3369{
3370 switch(hres) {
3371 case S_OK:
3372 return true;
3373 case DISP_E_BADPARAMCOUNT:
3374 qWarning("QAxBase: Error calling IDispatch member %s: Bad parameter count", name.toLatin1().data());
3375 return false;
3376 case DISP_E_BADVARTYPE:
3377 qWarning("QAxBase: Error calling IDispatch member %s: Bad variant type", name.toLatin1().data());
3378 return false;
3379 case DISP_E_EXCEPTION:
3380 {
3381 bool printWarning = true;
3382 unsigned short code = -1;
3383 QString source, desc, help;
3384 const QMetaObject *mo = that->metaObject();
3385 int exceptionSignal = mo->indexOfSignal("exception(int,QString,QString,QString)");
3386 if (exceptionSignal >= 0) {
3387 if (exc->pfnDeferredFillIn)
3388 exc->pfnDeferredFillIn(exc);
3389
3390 code = exc->wCode ? exc->wCode : exc->scode;
3391 source = QString::fromWCharArray(exc->bstrSource);
3392 desc = QString::fromWCharArray(exc->bstrDescription);
3393 help = QString::fromWCharArray(exc->bstrHelpFile);
3394 uint helpContext = exc->dwHelpContext;
3395
3396 if (helpContext && !help.isEmpty())
3397 help += QString::fromLatin1(" [%1]").arg(helpContext);
3398
3399 if (QAxEventSink::signalHasReceivers(that->qObject(), "exception(int,QString,QString,QString)")) {
3400 void *argv[] = {0, &code, &source, &desc, &help};
3401 that->qt_metacall(QMetaObject::InvokeMetaMethod, exceptionSignal, argv);
3402 printWarning = false;
3403 }
3404 }
3405 if (printWarning) {
3406 qWarning("QAxBase: Error calling IDispatch member %s: Exception thrown by server", name.toLatin1().data());
3407 qWarning(" Code : %d", code);
3408 qWarning(" Source : %s", source.toLatin1().data());
3409 qWarning(" Description: %s", desc.toLatin1().data());
3410 qWarning(" Help : %s", help.toLatin1().data());
3411 qWarning(" Connect to the exception(int,QString,QString,QString) signal to catch this exception");
3412 }
3413 }
3414 return false;
3415 case DISP_E_MEMBERNOTFOUND:
3416 qWarning("QAxBase: Error calling IDispatch member %s: Member not found", name.toLatin1().data());
3417 return false;
3418 case DISP_E_NONAMEDARGS:
3419 qWarning("QAxBase: Error calling IDispatch member %s: No named arguments", name.toLatin1().data());
3420 return false;
3421 case DISP_E_OVERFLOW:
3422 qWarning("QAxBase: Error calling IDispatch member %s: Overflow", name.toLatin1().data());
3423 return false;
3424 case DISP_E_PARAMNOTFOUND:
3425 qWarning("QAxBase: Error calling IDispatch member %s: Parameter %d not found", name.toLatin1().data(), argerr);
3426 return false;
3427 case DISP_E_TYPEMISMATCH:
3428 qWarning("QAxBase: Error calling IDispatch member %s: Type mismatch in parameter %d", name.toLatin1().data(), argerr);
3429 return false;
3430 case DISP_E_UNKNOWNINTERFACE:
3431 qWarning("QAxBase: Error calling IDispatch member %s: Unknown interface", name.toLatin1().data());
3432 return false;
3433 case DISP_E_UNKNOWNLCID:
3434 qWarning("QAxBase: Error calling IDispatch member %s: Unknown locale ID", name.toLatin1().data());
3435 return false;
3436 case DISP_E_PARAMNOTOPTIONAL:
3437 qWarning("QAxBase: Error calling IDispatch member %s: Non-optional parameter missing", name.toLatin1().data());
3438 return false;
3439 default:
3440 qWarning("QAxBase: Error calling IDispatch member %s: Unknown error", name.toLatin1().data());
3441 return false;
3442 }
3443}
3444
3445/*!
3446 \internal
3447*/
3448int QAxBase::internalProperty(QMetaObject::Call call, int index, void **v)
3449{
3450 const QMetaObject *mo = metaObject();
3451 const QMetaProperty prop = mo->property(index + mo->propertyOffset());
3452 QByteArray propname = prop.name();
3453
3454 // hardcoded control property
3455 if (propname == "control") {
3456 switch(call) {
3457 case QMetaObject::ReadProperty:
3458 *(QString*)*v = control();
3459 break;
3460 case QMetaObject::WriteProperty:
3461 setControl(*(QString*)*v);
3462 break;
3463 case QMetaObject::ResetProperty:
3464 clear();
3465 break;
3466 default:
3467 break;
3468 }
3469 return index - mo->propertyCount();
3470 }
3471
3472 // get the IDispatch
3473 if (!d->ptr || !prop.isValid())
3474 return index;
3475 IDispatch *disp = d->dispatch();
3476 if (!disp)
3477 return index;
3478
3479 DISPID dispid = d->metaObject()->dispIDofName(propname, disp);
3480 if (dispid == DISPID_UNKNOWN)
3481 return index;
3482
3483 Q_ASSERT(d->metaobj);
3484 // property found, so everthing that goes wrong now should not bother the caller
3485 index -= mo->propertyCount();
3486
3487 VARIANTARG arg;
3488 VariantInit(&arg);
3489 DISPPARAMS params;
3490 EXCEPINFO excepinfo;
3491 memset(&excepinfo, 0, sizeof(excepinfo));
3492 UINT argerr = 0;
3493 HRESULT hres = E_FAIL;
3494
3495 QByteArray proptype(prop.typeName());
3496 switch (call) {
3497 case QMetaObject::ReadProperty:
3498 {
3499 params.cArgs = 0;
3500 params.cNamedArgs = 0;
3501 params.rgdispidNamedArgs = 0;
3502 params.rgvarg = 0;
3503
3504 hres = disp->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &params, &arg, &excepinfo, 0);
3505
3506 // map result VARIANTARG to void*
3507 uint type = QVariant::Int;
3508 if (!prop.isEnumType())
3509 type = prop.type();
3510 QVariantToVoidStar(VARIANTToQVariant(arg, proptype, type), *v, proptype, type);
3511 if ((arg.vt != VT_DISPATCH && arg.vt != VT_UNKNOWN) || type == QVariant::Pixmap || type == QVariant::Font)
3512 clearVARIANT(&arg);
3513 }
3514 break;
3515
3516 case QMetaObject::WriteProperty:
3517 {
3518 QVariant::Type t = (QVariant::Type)prop.type();
3519
3520 DISPID dispidNamed = DISPID_PROPERTYPUT;
3521 params.cArgs = 1;
3522 params.cNamedArgs = 1;
3523 params.rgdispidNamedArgs = &dispidNamed;
3524 params.rgvarg = &arg;
3525
3526 arg.vt = VT_ERROR;
3527 arg.scode = DISP_E_TYPEMISMATCH;
3528
3529 // map void* to VARIANTARG via QVariant
3530 QVariant qvar;
3531 if (prop.isEnumType()) {
3532 qvar = *(int*)v[0];
3533 proptype = 0;
3534 } else {
3535 if (t == QVariant::LastType) {
3536 qvar = *(QVariant*)v[0];
3537 proptype = 0;
3538 } else if (t == QVariant::UserType) {
3539 qvar = QVariant(qRegisterMetaType<void*>(prop.typeName()), (void**)v[0]);
3540// qVariantSetValue(qvar, *(void**)v[0], prop.typeName());
3541 } else {
3542 proptype = d->metaObject()->propertyType(propname);
3543 qvar = QVariant(t, v[0]);
3544 }
3545 }
3546
3547 QVariantToVARIANT(qvar, arg, proptype);
3548 if (arg.vt == VT_EMPTY || arg.vt == VT_ERROR) {
3549 qWarning("QAxBase::setProperty: Unhandled property type %s", prop.typeName());
3550 break;
3551 }
3552 }
3553 hres = disp->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT, &params, 0, &excepinfo, &argerr);
3554 clearVARIANT(&arg);
3555 break;
3556
3557 default:
3558 break;
3559 }
3560
3561 checkHRESULT(hres, &excepinfo, this, QLatin1String(propname), argerr);
3562 return index;
3563}
3564
3565int QAxBase::internalInvoke(QMetaObject::Call call, int index, void **v)
3566{
3567 Q_ASSERT(call == QMetaObject::InvokeMetaMethod);
3568 Q_UNUSED(call);
3569
3570 // get the IDispatch
3571 IDispatch *disp = d->dispatch();
3572 if (!disp)
3573 return index;
3574
3575 const QMetaObject *mo = metaObject();
3576 // get the slot information
3577 const QMetaMethod slot = mo->method(index + mo->methodOffset());
3578 Q_ASSERT(slot.methodType() == QMetaMethod::Slot);
3579
3580 QByteArray signature(slot.signature());
3581 QByteArray slotname(signature);
3582 slotname.truncate(slotname.indexOf('('));
3583
3584 // Get the Dispatch ID of the method to be called
3585 bool isProperty = false;
3586 DISPID dispid = d->metaObject()->dispIDofName(slotname, disp);
3587
3588 Q_ASSERT(d->metaobj);
3589
3590 if (dispid == DISPID_UNKNOWN && slotname.toLower().startsWith("set")) {
3591 // see if we are calling a property set function as a slot
3592 slotname = slotname.right(slotname.length() - 3);
3593 dispid = d->metaobj->dispIDofName(slotname, disp);
3594 isProperty = true;
3595 }
3596 if (dispid == DISPID_UNKNOWN)
3597 return index;
3598
3599 // slot found, so everthing that goes wrong now should not bother the caller
3600 index -= mo->methodCount();
3601
3602 // setup the parameters
3603 DISPPARAMS params;
3604 DISPID dispidNamed = DISPID_PROPERTYPUT;
3605 params.cArgs = d->metaobj->numParameter(signature);
3606 params.cNamedArgs = isProperty ? 1 : 0;
3607 params.rgdispidNamedArgs = isProperty ? &dispidNamed : 0;
3608 params.rgvarg = 0;
3609 VARIANTARG static_rgvarg[QAX_NUM_PARAMS];
3610 if (params.cArgs) {
3611 if (params.cArgs <= QAX_NUM_PARAMS)
3612 params.rgvarg = static_rgvarg;
3613 else
3614 params.rgvarg = new VARIANTARG[params.cArgs];
3615 }
3616
3617 int p;
3618 for (p = 0; p < (int)params.cArgs; ++p) {
3619 bool out;
3620 QByteArray type = d->metaobj->paramType(signature, p, &out);
3621 QVariant::Type vt = QVariant::nameToType(type);
3622 QVariant qvar;
3623 if (vt != QVariant::UserType)
3624 qvar = QVariant(vt, v[p + 1]);
3625
3626 if (!qvar.isValid()) {
3627 if (type == "IDispatch*")
3628 qVariantSetValue(qvar, *(IDispatch**)v[p+1]);
3629 else if (type == "IUnknown*")
3630 qVariantSetValue(qvar, *(IUnknown**)v[p+1]);
3631 else if (type == "QVariant")
3632 qvar = *(QVariant*)v[p + 1];
3633 else if (mo->indexOfEnumerator(type) != -1)
3634 qvar = *(int*)v[p + 1];
3635 else
3636 qvar = QVariant(QMetaType::type(type), v[p + 1]);
3637 }
3638
3639 QVariantToVARIANT(qvar, params.rgvarg[params.cArgs - p - 1], type, out);
3640 }
3641
3642 // call the method
3643 VARIANT ret;
3644 VariantInit(&ret);
3645 UINT argerr = 0;
3646 HRESULT hres = E_FAIL;
3647 EXCEPINFO excepinfo;
3648 memset(&excepinfo, 0, sizeof(excepinfo));
3649
3650 WORD wFlags = isProperty ? DISPATCH_PROPERTYPUT : DISPATCH_METHOD | DISPATCH_PROPERTYGET;
3651 hres = disp->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, wFlags, &params, &ret, &excepinfo, &argerr);
3652
3653 // get return value
3654 if (hres == S_OK && ret.vt != VT_EMPTY)
3655 QVariantToVoidStar(VARIANTToQVariant(ret, slot.typeName()), v[0], slot.typeName());
3656
3657 // update out parameters
3658 for (p = 0; p < (int)params.cArgs; ++p) {
3659 bool out;
3660 QByteArray ptype = d->metaobj->paramType(signature, p, &out);
3661 if (out)
3662 QVariantToVoidStar(VARIANTToQVariant(params.rgvarg[params.cArgs - p - 1], ptype), v[p+1], ptype);
3663 }
3664 // clean up
3665 for (p = 0; p < (int)params.cArgs; ++p)
3666 clearVARIANT(params.rgvarg+p);
3667 if (params.rgvarg != static_rgvarg)
3668 delete [] params.rgvarg;
3669
3670 checkHRESULT(hres, &excepinfo, this, QString::fromLatin1(slotname), params.cArgs-argerr-1);
3671 return index;
3672}
3673
3674/*!
3675 \internal
3676*/
3677int QAxBase::qt_metacall(QMetaObject::Call call, int id, void **v)
3678{
3679 const QMetaObject *mo = metaObject();
3680 if (isNull() && mo->property(id + mo->propertyOffset()).name() != QByteArray("control")) {
3681 qWarning("QAxBase::qt_metacall: Object is not initialized, or initialization failed");
3682 return id;
3683 }
3684
3685 switch(call) {
3686 case QMetaObject::InvokeMetaMethod:
3687 switch (mo->method(id + mo->methodOffset()).methodType()) {
3688 case QMetaMethod::Signal:
3689 QMetaObject::activate(qObject(), mo, id, v);
3690 id -= mo->methodCount();
3691 break;
3692 case QMetaMethod::Method:
3693 case QMetaMethod::Slot:
3694 id = internalInvoke(call, id, v);
3695 break;
3696 default:
3697 break;
3698 }
3699 break;
3700 case QMetaObject::ReadProperty:
3701 case QMetaObject::WriteProperty:
3702 case QMetaObject::ResetProperty:
3703 id = internalProperty(call, id, v);
3704 break;
3705 case QMetaObject::QueryPropertyScriptable:
3706 case QMetaObject::QueryPropertyDesignable:
3707 case QMetaObject::QueryPropertyStored:
3708 case QMetaObject::QueryPropertyEditable:
3709 case QMetaObject::QueryPropertyUser:
3710 id -= mo->propertyCount();
3711 break;
3712 default:
3713 break;
3714 }
3715 Q_ASSERT(id < 0);
3716 return id;
3717}
3718
3719#ifdef QT_CHECK_STATE
3720static void qax_noSuchFunction(int disptype, const QByteArray &name, const QByteArray &function, const QAxBase *that)
3721{
3722 const QMetaObject *metaObject = that->metaObject();
3723 const char *coclass = metaObject->classInfo(metaObject->indexOfClassInfo("CoClass")).value();
3724
3725 if (disptype == DISPATCH_METHOD) {
3726 qWarning("QAxBase::dynamicCallHelper: %s: No such method in %s [%s]", name.data(), that->control().toLatin1().data(), coclass ? coclass: "unknown");
3727 qWarning("\tCandidates are:");
3728 for (int i = 0; i < metaObject->methodCount(); ++i) {
3729 const QMetaMethod slot(metaObject->method(i));
3730 if (slot.methodType() != QMetaMethod::Slot)
3731 continue;
3732 QByteArray signature = slot.signature();
3733 if (signature.toLower().startsWith(function.toLower()))
3734 qWarning("\t\t%s", signature.data());
3735 }
3736 } else {
3737 qWarning("QAxBase::dynamicCallHelper: %s: No such property in %s [%s]", name.data(), that->control().toLatin1().data(), coclass ? coclass: "unknown");
3738 if (!function.isEmpty()) {
3739 qWarning("\tCandidates are:");
3740 char f0 = function.toLower().at(0);
3741 for (int i = metaObject->propertyOffset(); i < metaObject->propertyCount(); ++i) {
3742 QByteArray signature(metaObject->property(i).name());
3743 if (!signature.isEmpty() && signature.toLower().at(0) == f0)
3744 qWarning("\t\t%s", signature.data());
3745 }
3746 }
3747 }
3748}
3749#endif
3750
3751/*!
3752 \internal
3753
3754 \a name is already normalized?
3755*/
3756bool QAxBase::dynamicCallHelper(const char *name, void *inout, QList<QVariant> &vars, QByteArray &type)
3757{
3758 if (isNull()) {
3759 qWarning("QAxBase::dynamicCallHelper: Object is not initialized, or initialization failed");
3760 return false;
3761 }
3762
3763 IDispatch *disp = d->dispatch();
3764 if (!disp) {
3765 qWarning("QAxBase::dynamicCallHelper: Object does not support automation");
3766 return false;
3767 }
3768
3769 const QMetaObject *mo = metaObject();
3770 d->metaObject();
3771 Q_ASSERT(d->metaobj);
3772
3773 int varc = vars.count();
3774
3775 QByteArray normFunction = QMetaObject::normalizedSignature(name);
3776 QByteArray function(normFunction);
3777 VARIANT staticarg[QAX_NUM_PARAMS];
3778 VARIANT *arg = 0;
3779 VARIANTARG *res = (VARIANTARG*)inout;
3780
3781 unsigned short disptype;
3782
3783 int id = -1;
3784 bool parse = false;
3785
3786 if (function.contains('(')) {
3787 disptype = DISPATCH_METHOD | DISPATCH_PROPERTYGET;
3788 if (d->useMetaObject)
3789 id = mo->indexOfSlot(function);
3790 if (id >= 0) {
3791 const QMetaMethod slot = mo->method(id);
3792 Q_ASSERT(slot.methodType() == QMetaMethod::Slot);
3793 function = slot.signature();
3794 type = slot.typeName();
3795 }
3796 function.truncate(function.indexOf('('));
3797 parse = !varc && normFunction.length() > function.length() + 2;
3798 if (parse) {
3799 QString args = QLatin1String(normFunction);
3800 args = args.mid(function.length() + 1);
3801 // parse argument string int list of arguments
3802 QString curArg;
3803 const QChar *c = args.unicode();
3804 int index = 0;
3805 bool inString = false;
3806 bool inEscape = false;
3807 while (index < (int)args.length()) {
3808 QChar cc = *c;
3809 ++c;
3810 ++index;
3811 switch(cc.toLatin1()) {
3812 case 'n':
3813 if (inEscape)
3814 cc = QLatin1Char('\n');
3815 break;
3816 case 'r':
3817 if (inEscape)
3818 cc = QLatin1Char('\r');
3819 break;
3820 case 't':
3821 if (inEscape)
3822 cc = QLatin1Char('\t');
3823 break;
3824 case '\\':
3825 if (!inEscape && inString) {
3826 inEscape = true;
3827 continue;
3828 }
3829 break;
3830 case '"':
3831 if (!inEscape) {
3832 inString = !inString;
3833 curArg += cc;
3834 continue;
3835 }
3836 break;
3837 case ' ':
3838 if (!inString && curArg.isEmpty())
3839 continue;
3840 break;
3841 case ',':
3842 case ')':
3843 if (inString)
3844 break;
3845 curArg = curArg.trimmed();
3846 if (curArg.at(0) == QLatin1Char('\"') && curArg.at(curArg.length()-1) == QLatin1Char('\"')) {
3847 vars << curArg.mid(1, curArg.length() - 2);
3848 } else {
3849 bool isNumber = false;
3850 bool isDouble = false;
3851 int number = curArg.toInt(&isNumber);
3852 double dbl = curArg.toDouble(&isDouble);
3853 if (isNumber) {
3854 vars << number;
3855 } else if (isDouble) {
3856 vars << dbl;
3857 } else {
3858 bool isEnum = false;
3859 for (int enumIndex = 0; enumIndex < mo->enumeratorCount(); ++enumIndex) {
3860 QMetaEnum metaEnum =mo->enumerator(enumIndex);
3861 int value = metaEnum.keyToValue(curArg.toLatin1());
3862 if (value != -1 && !QByteArray(metaEnum.valueToKey(value)).isEmpty()) {
3863 vars << value;
3864 isEnum = true;
3865 break;
3866 }
3867 }
3868 if (!isEnum)
3869 vars << curArg;
3870 }
3871 }
3872 curArg.clear();
3873 continue;
3874 default:
3875 break;
3876 }
3877 inEscape = false;
3878 curArg += cc;
3879 }
3880
3881 varc = vars.count();
3882 }
3883 } else {
3884 if (d->useMetaObject)
3885 id = mo->indexOfProperty(normFunction);
3886
3887 if (id >= 0) {
3888 const QMetaProperty prop =mo->property(id);
3889 type = prop.typeName();
3890 }
3891 if (varc == 1) {
3892 res = 0;
3893 disptype = DISPATCH_PROPERTYPUT;
3894 } else {
3895 disptype = DISPATCH_PROPERTYGET;
3896 }
3897 }
3898 if (varc) {
3899 varc = qMin(varc, d->metaobj->numParameter(normFunction));
3900 arg = varc <= QAX_NUM_PARAMS ? staticarg : new VARIANT[varc];
3901 for (int i = 0; i < varc; ++i) {
3902 QVariant var(vars.at(i));
3903 VariantInit(arg + (varc - i - 1));
3904 bool out = false;
3905 QByteArray paramType;
3906 if (disptype == DISPATCH_PROPERTYPUT)
3907 paramType = type;
3908 else if (parse || disptype == DISPATCH_PROPERTYGET)
3909 paramType = 0;
3910 else
3911 paramType = d->metaobj->paramType(normFunction, i, &out);
3912
3913 if ((!parse && d->useMetaObject && var.type() == QVariant::String) || var.type() == QVariant::ByteArray) {
3914 int enumIndex =mo->indexOfEnumerator(paramType);
3915 if (enumIndex != -1) {
3916 QMetaEnum metaEnum =mo->enumerator(enumIndex);
3917 QVariantToVARIANT(metaEnum.keyToValue(var.toByteArray()), arg[varc - i - 1], "int", out);
3918 }
3919 }
3920
3921 if (arg[varc - i - 1].vt == VT_EMPTY)
3922 QVariantToVARIANT(var, arg[varc - i - 1], paramType, out);
3923 }
3924 }
3925
3926 DISPID dispid = d->metaobj->dispIDofName(function, disp);
3927 if (dispid == DISPID_UNKNOWN && function.toLower().startsWith("set")) {
3928 function = function.mid(3);
3929 dispid = d->metaobj->dispIDofName(function, disp);
3930 disptype = DISPATCH_PROPERTYPUT;
3931 }
3932
3933 if (dispid == DISPID_UNKNOWN) {
3934#ifdef QT_CHECK_STATE
3935 qax_noSuchFunction(disptype, normFunction, function, this);
3936#endif
3937 return false;
3938 }
3939
3940 DISPPARAMS params;
3941 DISPID dispidNamed = DISPID_PROPERTYPUT;
3942
3943 params.cArgs = varc;
3944 params.cNamedArgs = (disptype == DISPATCH_PROPERTYPUT) ? 1 : 0;
3945 params.rgdispidNamedArgs = (disptype == DISPATCH_PROPERTYPUT) ? &dispidNamed : 0;
3946 params.rgvarg = arg;
3947 EXCEPINFO excepinfo;
3948 memset(&excepinfo, 0, sizeof(excepinfo));
3949 UINT argerr = 0;
3950
3951 HRESULT hres = disp->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, disptype, &params, res, &excepinfo, &argerr);
3952
3953 if (disptype == (DISPATCH_METHOD|DISPATCH_PROPERTYGET) && hres == S_OK && varc) {
3954 for (int i = 0; i < varc; ++i)
3955 if (arg[varc-i-1].vt & VT_BYREF) // update out-parameters
3956 vars[i] = VARIANTToQVariant(arg[varc-i-1], vars.at(i).typeName());
3957 }
3958
3959 // clean up
3960 for (int i = 0; i < varc; ++i)
3961 clearVARIANT(params.rgvarg+i);
3962 if (arg && arg != staticarg)
3963 delete[] arg;
3964
3965 return checkHRESULT(hres, &excepinfo, this, QLatin1String(function), varc-argerr-1);
3966}
3967
3968
3969/*!
3970 Calls the COM object's method \a function, passing the
3971 parameters \a var1, \a var1, \a var2, \a var3, \a var4, \a var5,
3972 \a var6, \a var7 and \a var8, and returns the value returned by
3973 the method, or an invalid QVariant if the method does not return
3974 a value or when the function call failed.
3975
3976 If \a function is a method of the object the string must be provided
3977 as the full prototype, for example as it would be written in a
3978 QObject::connect() call.
3979
3980 \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 15
3981
3982 Alternatively a function can be called passing the parameters embedded
3983 in the string, e.g. above function can also be invoked using
3984
3985 \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 16
3986
3987 All parameters are passed as strings; it depends on the control whether
3988 they are interpreted correctly, and is slower than using the prototype
3989 with correctly typed parameters.
3990
3991 If \a function is a property the string has to be the name of the
3992 property. The property setter is called when \a var1 is a valid QVariant,
3993 otherwise the getter is called.
3994
3995 \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 17
3996
3997 Note that it is faster to get and set properties using
3998 QObject::property() and QObject::setProperty().
3999
4000 dynamicCall() can also be used to call objects with a
4001 \l{QAxBase::disableMetaObject()}{disabled metaobject} wrapper,
4002 which can improve performance significantely, esp. when calling many
4003 different objects of different types during an automation process.
4004 ActiveQt will then however not validate parameters.
4005
4006 It is only possible to call functions through dynamicCall() that
4007 have parameters or return values of datatypes supported by
4008 QVariant. See the QAxBase class documentation for a list of
4009 supported and unsupported datatypes. If you want to call functions
4010 that have unsupported datatypes in the parameter list, use
4011 queryInterface() to retrieve the appropriate COM interface, and
4012 use the function directly.
4013
4014 \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 18
4015
4016 This is also more efficient.
4017*/
4018QVariant QAxBase::dynamicCall(const char *function,
4019 const QVariant &var1,
4020 const QVariant &var2,
4021 const QVariant &var3,
4022 const QVariant &var4,
4023 const QVariant &var5,
4024 const QVariant &var6,
4025 const QVariant &var7,
4026 const QVariant &var8)
4027{
4028 QList<QVariant> vars;
4029 QVariant var = var1;
4030 int argc = 1;
4031 while(var.isValid()) {
4032 vars << var;
4033 switch(++argc) {
4034 case 2: var = var2; break;
4035 case 3: var = var3; break;
4036 case 4: var = var4; break;
4037 case 5: var = var5; break;
4038 case 6: var = var6; break;
4039 case 7: var = var7; break;
4040 case 8: var = var8; break;
4041 default:var = QVariant(); break;
4042 }
4043 }
4044
4045 return dynamicCall(function, vars);
4046}
4047
4048/*!
4049 \overload
4050
4051 Calls the COM object's method \a function, passing the
4052 parameters in \a vars, and returns the value returned by
4053 the method. If the method does not return a value or when
4054 the function call failed this function returns an invalid
4055 QVariant object.
4056
4057 The QVariant objects in \a vars are updated when the method has
4058 out-parameters.
4059*/
4060QVariant QAxBase::dynamicCall(const char *function, QList<QVariant> &vars)
4061{
4062 VARIANTARG res;
4063 VariantInit(&res);
4064
4065 QByteArray rettype;
4066 if (!dynamicCallHelper(function, &res, vars, rettype))
4067 return QVariant();
4068
4069 QVariant qvar = VARIANTToQVariant(res, rettype);
4070 if ((res.vt != VT_DISPATCH && res.vt != VT_UNKNOWN) || qvar.type() == QVariant::Pixmap || qvar.type() == QVariant::Font)
4071 clearVARIANT(&res);
4072
4073 return qvar;
4074}
4075
4076/*!
4077 Returns a pointer to a QAxObject wrapping the COM object provided
4078 by the method or property \a name, passing passing the parameters
4079 \a var1, \a var1, \a var2, \a var3, \a var4, \a var5, \a var6,
4080 \a var7 and \a var8.
4081
4082 If \a name is provided by a method the string must include the
4083 full function prototype.
4084
4085 If \a name is a property the string must be the name of the property,
4086 and \a var1, ... \a var8 are ignored.
4087
4088 The returned QAxObject is a child of this object (which is either of
4089 type QAxObject or QAxWidget), and is deleted when this object is
4090 deleted. It is however safe to delete the returned object yourself,
4091 and you should do so when you iterate over lists of subobjects.
4092
4093 COM enabled applications usually have an object model publishing
4094 certain elements of the application as dispatch interfaces. Use
4095 this method to navigate the hierarchy of the object model, e.g.
4096
4097 \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 19
4098*/
4099QAxObject *QAxBase::querySubObject(const char *name,
4100 const QVariant &var1,
4101 const QVariant &var2,
4102 const QVariant &var3,
4103 const QVariant &var4,
4104 const QVariant &var5,
4105 const QVariant &var6,
4106 const QVariant &var7,
4107 const QVariant &var8)
4108{
4109 QList<QVariant> vars;
4110 QVariant var = var1;
4111 int argc = 1;
4112 while(var.isValid()) {
4113 vars << var;
4114 switch(++argc) {
4115 case 2: var = var2; break;
4116 case 3: var = var3; break;
4117 case 4: var = var4; break;
4118 case 5: var = var5; break;
4119 case 6: var = var6; break;
4120 case 7: var = var7; break;
4121 case 8: var = var8; break;
4122 default:var = QVariant(); break;
4123 }
4124 }
4125
4126 return querySubObject(name, vars);
4127}
4128
4129/*!
4130 \overload
4131
4132 The QVariant objects in \a vars are updated when the method has
4133 out-parameters.
4134*/
4135QAxObject *QAxBase::querySubObject(const char *name, QList<QVariant> &vars)
4136{
4137 QAxObject *object = 0;
4138 VARIANTARG res;
4139 VariantInit(&res);
4140
4141 QByteArray rettype;
4142 if (!dynamicCallHelper(name, &res, vars, rettype))
4143 return 0;
4144
4145 switch (res.vt) {
4146 case VT_DISPATCH:
4147 if (res.pdispVal) {
4148 if (rettype.isEmpty() || rettype == "IDispatch*" || rettype == "QVariant") {
4149 object = new QAxObject(res.pdispVal, qObject());
4150 } else if (QMetaType::type(rettype)) {
4151 QVariant qvar = VARIANTToQVariant(res, rettype, 0);
4152 object = *(QAxObject**)qvar.constData();
4153// qVariantGet(qvar, object, rettype);
4154 res.pdispVal->AddRef();
4155 }
4156 if (object)
4157 ((QAxBase*)object)->d->tryCache = true;
4158 }
4159 break;
4160 case VT_UNKNOWN:
4161 if (res.punkVal) {
4162 if (rettype.isEmpty() || rettype == "IUnknown*") {
4163 object = new QAxObject(res.punkVal, qObject());
4164 } else if (QMetaType::type(rettype)) {
4165 QVariant qvar = VARIANTToQVariant(res, rettype, 0);
4166 object = *(QAxObject**)qvar.constData();
4167// qVariantGet(qvar, object, rettype);
4168 res.punkVal->AddRef();
4169 }
4170 if (object)
4171 ((QAxBase*)object)->d->tryCache = true;
4172 }
4173 break;
4174 case VT_EMPTY:
4175#ifdef QT_CHECK_STATE
4176 {
4177 const char *coclass = metaObject()->classInfo(metaObject()->indexOfClassInfo("CoClass")).value();
4178 qWarning("QAxBase::querySubObject: %s: Error calling function or property in %s (%s)"
4179 , name, control().toLatin1().data(), coclass ? coclass: "unknown");
4180 }
4181#endif
4182 break;
4183 default:
4184#ifdef QT_CHECK_STATE
4185 {
4186 const char *coclass = metaObject()->classInfo(metaObject()->indexOfClassInfo("CoClass")).value();
4187 qWarning("QAxBase::querySubObject: %s: Method or property is not of interface type in %s (%s)"
4188 , name, control().toLatin1().data(), coclass ? coclass: "unknown");
4189 }
4190#endif
4191 break;
4192 }
4193
4194 clearVARIANT(&res);
4195 return object;
4196}
4197
4198class QtPropertyBag : public IPropertyBag
4199{
4200public:
4201 QtPropertyBag() :ref(0) {}
4202 virtual ~QtPropertyBag() {}
4203
4204 HRESULT __stdcall QueryInterface(REFIID iid, LPVOID *iface)
4205 {
4206 *iface = 0;
4207 if (iid == IID_IUnknown)
4208 *iface = this;
4209 else if (iid == IID_IPropertyBag)
4210 *iface = this;
4211 else
4212 return E_NOINTERFACE;
4213
4214 AddRef();
4215 return S_OK;
4216 }
4217 unsigned long __stdcall AddRef() { return ++ref; }
4218 unsigned long __stdcall Release()
4219 {
4220 if (!--ref) {
4221 delete this;
4222 return 0;
4223 }
4224 return ref;
4225 }
4226
4227 HRESULT __stdcall Read(LPCOLESTR name, VARIANT *var, IErrorLog *)
4228 {
4229 if (!var)
4230 return E_POINTER;
4231
4232 QString property = QString::fromWCharArray(name);
4233 QVariant qvar = map.value(property);
4234 QVariantToVARIANT(qvar, *var);
4235 return S_OK;
4236 }
4237 HRESULT __stdcall Write(LPCOLESTR name, VARIANT *var)
4238 {
4239 if (!var)
4240 return E_POINTER;
4241 QString property = QString::fromWCharArray(name);
4242 QVariant qvar = VARIANTToQVariant(*var, 0);
4243 map[property] = qvar;
4244
4245 return S_OK;
4246 }
4247
4248 QAxBase::PropertyBag map;
4249
4250private:
4251 unsigned long ref;
4252};
4253
4254/*!
4255 Returns a name:value map of all the properties exposed by the COM
4256 object.
4257
4258 This is more efficient than getting multiple properties
4259 individually if the COM object supports property bags.
4260
4261 \warning It is not guaranteed that the property bag implementation
4262 of the COM object returns all properties, or that the properties
4263 returned are the same as those available through the IDispatch
4264 interface.
4265*/
4266QAxBase::PropertyBag QAxBase::propertyBag() const
4267{
4268 PropertyBag result;
4269
4270 if (!d->ptr && !d->initialized) {
4271 ((QAxBase*)this)->initialize(&d->ptr);
4272 d->initialized = true;
4273 }
4274
4275 if (isNull())
4276 return result;
4277 IPersistPropertyBag *persist = 0;
4278 d->ptr->QueryInterface(IID_IPersistPropertyBag, (void**)&persist);
4279 if (persist) {
4280 QtPropertyBag *pbag = new QtPropertyBag();
4281 pbag->AddRef();
4282 persist->Save(pbag, false, true);
4283 result = pbag->map;
4284 pbag->Release();
4285 persist->Release();
4286 return result;
4287 } else {
4288 const QMetaObject *mo = metaObject();
4289 for (int p = mo->propertyOffset(); p < mo->propertyCount(); ++p) {
4290 const QMetaProperty property = mo->property(p);
4291 QVariant var = qObject()->property(property.name());
4292 result.insert(QLatin1String(property.name()), var);
4293 }
4294 }
4295 return result;
4296}
4297
4298/*!
4299 Sets the properties of the COM object to the corresponding values
4300 in \a bag.
4301
4302 \warning
4303 You should only set property bags that have been returned by the
4304 propertyBag function, as it cannot be guaranteed that the property
4305 bag implementation of the COM object supports the same properties
4306 that are available through the IDispatch interface.
4307
4308 \sa propertyBag()
4309*/
4310void QAxBase::setPropertyBag(const PropertyBag &bag)
4311{
4312 if (!d->ptr && !d->initialized) {
4313 initialize(&d->ptr);
4314 d->initialized = true;
4315 }
4316
4317 if (isNull())
4318 return;
4319 IPersistPropertyBag *persist = 0;
4320 d->ptr->QueryInterface(IID_IPersistPropertyBag, (void**)&persist);
4321 if (persist) {
4322 QtPropertyBag *pbag = new QtPropertyBag();
4323 pbag->map = bag;
4324 pbag->AddRef();
4325 persist->Load(pbag, 0);
4326 pbag->Release();
4327 persist->Release();
4328 } else {
4329 const QMetaObject *mo = metaObject();
4330 for (int p = mo->propertyOffset(); p < mo->propertyCount(); ++p) {
4331 const QMetaProperty property = mo->property(p);
4332 QVariant var = bag.value(QLatin1String(property.name()));
4333 qObject()->setProperty(property.name(), var);
4334 }
4335 }
4336}
4337
4338/*!
4339 Returns true if the property \a prop is writable; otherwise
4340 returns false. By default, all properties are writable.
4341
4342 \warning
4343 Depending on the control implementation this setting might be
4344 ignored for some properties.
4345
4346 \sa setPropertyWritable(), propertyChanged()
4347*/
4348bool QAxBase::propertyWritable(const char *prop) const
4349{
4350 return d->propWritable.value(prop, true);
4351}
4352
4353/*!
4354 Sets the property \a prop to writable if \a ok is true, otherwise
4355 sets \a prop to be read-only. By default, all properties are
4356 writable.
4357
4358 \warning
4359 Depending on the control implementation this setting might be
4360 ignored for some properties.
4361
4362 \sa propertyWritable(), propertyChanged()
4363*/
4364void QAxBase::setPropertyWritable(const char *prop, bool ok)
4365{
4366 d->propWritable[prop] = ok;
4367}
4368
4369/*!
4370 Returns true if there is no COM object loaded by this wrapper;
4371 otherwise return false.
4372
4373 \sa control
4374*/
4375bool QAxBase::isNull() const
4376{
4377 return !d->ptr;
4378}
4379
4380/*!
4381 Returns a QVariant that wraps the COM object. The variant can
4382 then be used as a parameter in e.g. dynamicCall().
4383*/
4384QVariant QAxBase::asVariant() const
4385{
4386 if (!d->ptr && !d->initialized) {
4387 ((QAxBase*)this)->initialize(&d->ptr);
4388 d->initialized = true;
4389 }
4390
4391 QVariant qvar;
4392 QByteArray cn(className());
4393 if (cn == "QAxObject" || cn == "QAxWidget" || cn == "QAxBase") {
4394 if (d->dispatch())
4395 qVariantSetValue(qvar, d->dispatch());
4396 else if (d->ptr)
4397 qVariantSetValue(qvar, d->ptr);
4398 } else {
4399 cn = cn.mid(cn.lastIndexOf(':') + 1);
4400 QObject *object = qObject();
4401 if (QMetaType::type(cn))
4402 qvar = QVariant(qRegisterMetaType<QObject*>(cn + '*'), &object);
4403// qVariantSetValue(qvar, qObject(), cn + '*');
4404 }
4405
4406 return qvar;
4407}
4408
4409// internal function that creates a QAxObject from an iface
4410// used by type-conversion code (types.cpp)
4411void *qax_createObjectWrapper(int metaType, IUnknown *iface)
4412{
4413 if (!iface)
4414 return 0;
4415
4416 QAxObject *object = (QAxObject*)QMetaType::construct(metaType, 0);
4417 QAxBasePrivate *d = object->d;
4418
4419 d->ptr = iface;
4420 d->initialized = true;
4421
4422 // no release, since no addref
4423
4424 return object;
4425}
4426
4427/*!
4428 \fn void QAxBase::signal(const QString &name, int argc, void *argv)
4429
4430 This generic signal gets emitted when the COM object issues the
4431 event \a name. \a argc is the number of parameters provided by the
4432 event (DISPPARAMS.cArgs), and \a argv is the pointer to the
4433 parameter values (DISPPARAMS.rgvarg). Note that the order of parameter
4434 values is turned around, ie. the last element of the array is the first
4435 parameter in the function.
4436
4437 \snippet doc/src/snippets/code/src_activeqt_container_qaxbase.cpp 20
4438
4439 Use this signal if the event has parameters of unsupported data
4440 types. Otherwise, connect directly to the signal \a name.
4441*/
4442
4443/*!
4444 \fn void QAxBase::propertyChanged(const QString &name)
4445
4446 If the COM object supports property notification, this signal gets
4447 emitted when the property called \a name is changed.
4448*/
4449
4450/*!
4451 \fn void QAxBase::exception(int code, const QString &source, const QString &desc, const QString &help)
4452
4453 This signal is emitted when the COM object throws an exception while called using the OLE automation
4454 interface IDispatch. \a code, \a source, \a desc and \a help provide information about the exception as
4455 provided by the COM server and can be used to provide useful feedback to the end user. \a help includes
4456 the help file, and the help context ID in brackets, e.g. "filename [id]".
4457*/
4458
4459/*!
4460 \fn QObject *QAxBase::qObject() const
4461 \internal
4462*/
4463
4464/*!
4465 \fn const char *QAxBase::className() const
4466 \internal
4467*/
4468
4469QT_END_NAMESPACE
4470#endif //QT_NO_WIN_ACTIVEQT
Note: See TracBrowser for help on using the repository browser.