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

Last change on this file since 1147 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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