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

Last change on this file since 5 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

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