source: trunk/src/activeqt/container/qaxscript.cpp@ 123

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

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

File size: 37.1 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the ActiveQt framework of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:BSD$
9** You may use this file under the terms of the BSD license as follows:
10**
11** "Redistribution and use in source and binary forms, with or without
12** modification, are permitted provided that the following conditions are
13** met:
14** * Redistributions of source code must retain the above copyright
15** notice, this list of conditions and the following disclaimer.
16** * Redistributions in binary form must reproduce the above copyright
17** notice, this list of conditions and the following disclaimer in
18** the documentation and/or other materials provided with the
19** distribution.
20** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
21** the names of its contributors may be used to endorse or promote
22** products derived from this software without specific prior written
23** permission.
24**
25** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#ifndef UNICODE
41#define UNICODE
42#endif
43
44#include "qaxscript.h"
45
46#ifndef QT_NO_WIN_ACTIVEQT
47
48#if defined(Q_CC_GNU)
49# define QT_NO_QAXSCRIPT
50#elif defined(Q_CC_BOR) && __BORLANDC__ < 0x560
51# define QT_NO_QAXSCRIPT
52#endif
53
54#include <qapplication.h>
55#include <qfile.h>
56#include <qhash.h>
57#include <qmetaobject.h>
58#include <quuid.h>
59#include <qwidget.h>
60
61#include <qt_windows.h>
62#ifndef QT_NO_QAXSCRIPT
63#include <initguid.h>
64#include <activscp.h>
65#endif
66
67#include "../shared/qaxtypes.h"
68
69QT_BEGIN_NAMESPACE
70
71struct QAxEngineDescriptor { QString name, extension, code; };
72static QList<QAxEngineDescriptor> engines;
73
74class QAxScriptManagerPrivate
75{
76public:
77 QHash<QString, QAxScript*> scriptDict;
78 QHash<QString, QAxBase*> objectDict;
79};
80
81/*
82 \class QAxScriptSite
83 \brief The QAxScriptSite class implements a Windows Scripting Host
84 \internal
85
86 The QAxScriptSite is used internally to communicate callbacks from the script
87 engine to the script manager.
88*/
89
90#ifndef QT_NO_QAXSCRIPT
91
92class QAxScriptSite : public IActiveScriptSite, public IActiveScriptSiteWindow
93{
94public:
95 QAxScriptSite(QAxScript *script);
96
97 ULONG WINAPI AddRef();
98 ULONG WINAPI Release();
99 HRESULT WINAPI QueryInterface(REFIID iid, void **ppvObject);
100
101 HRESULT WINAPI GetLCID(LCID *plcid);
102 HRESULT WINAPI GetItemInfo(LPCOLESTR pstrName, DWORD dwReturnMask, IUnknown **ppiunkItem, ITypeInfo **ppti);
103 HRESULT WINAPI GetDocVersionString(BSTR *pbstrVersion);
104
105 HRESULT WINAPI OnScriptTerminate(const VARIANT *pvarResult, const EXCEPINFO *pexcepinfo);
106 HRESULT WINAPI OnStateChange(SCRIPTSTATE ssScriptState);
107 HRESULT WINAPI OnScriptError(IActiveScriptError *pscripterror);
108 HRESULT WINAPI OnEnterScript();
109 HRESULT WINAPI OnLeaveScript();
110
111 HRESULT WINAPI GetWindow(HWND *phwnd);
112 HRESULT WINAPI EnableModeless(BOOL fEnable);
113
114protected:
115 QWidget *window() const;
116
117private:
118 QAxScript *script;
119 unsigned long ref;
120};
121
122/*
123 Constructs the site for the \a s.
124*/
125QAxScriptSite::QAxScriptSite(QAxScript *s)
126: script(s), ref(1)
127{
128}
129
130/*
131 Implements IUnknown::AddRef
132*/
133ULONG WINAPI QAxScriptSite::AddRef()
134{
135 return ++ref;
136}
137
138/*
139 Implements IUnknown::Release
140*/
141ULONG WINAPI QAxScriptSite::Release()
142{
143 if (!--ref) {
144 delete this;
145 return 0;
146 }
147 return ref;
148}
149
150/*
151 Implements IUnknown::QueryInterface
152*/
153HRESULT WINAPI QAxScriptSite::QueryInterface(REFIID iid, void **ppvObject)
154{
155 *ppvObject = 0;
156 if (iid == IID_IUnknown)
157 *ppvObject = (IUnknown*)(IActiveScriptSite*)this;
158 else if (iid == IID_IActiveScriptSite)
159 *ppvObject = (IActiveScriptSite*)this;
160 else if (iid == IID_IActiveScriptSiteWindow)
161 *ppvObject = (IActiveScriptSiteWindow*)this;
162 else
163 return E_NOINTERFACE;
164
165 AddRef();
166 return S_OK;
167}
168
169/*
170 Implements IActiveScriptSite::GetLCID
171
172 This method is not implemented. Use the system-defined locale.
173*/
174HRESULT WINAPI QAxScriptSite::GetLCID(LCID * /*plcid*/)
175{
176 return E_NOTIMPL;
177}
178
179/*
180 Implements IActiveScriptSite::GetItemInfo
181
182 Tries to find the QAxBase for \a pstrName and returns the
183 relevant interfaces in \a item and \a type as requested through \a mask.
184*/
185HRESULT WINAPI QAxScriptSite::GetItemInfo(LPCOLESTR pstrName, DWORD mask, IUnknown **item, ITypeInfo **type)
186{
187 if (item)
188 *item = 0;
189 else if (mask & SCRIPTINFO_IUNKNOWN)
190 return E_POINTER;
191
192 if (type)
193 *type = 0;
194 else if (mask & SCRIPTINFO_ITYPEINFO)
195 return E_POINTER;
196
197 QAxBase *object = script->findObject(QString::fromUtf16((const ushort*)pstrName));
198 if (!object)
199 return TYPE_E_ELEMENTNOTFOUND;
200
201 if (mask & SCRIPTINFO_IUNKNOWN)
202 object->queryInterface(IID_IUnknown, (void**)item);
203 if (mask & SCRIPTINFO_ITYPEINFO) {
204 IProvideClassInfo *classInfo = 0;
205 object->queryInterface(IID_IProvideClassInfo, (void**)&classInfo);
206 if (classInfo) {
207 classInfo->GetClassInfo(type);
208 classInfo->Release();
209 }
210 }
211 return S_OK;
212}
213
214/*
215 Implements IActiveScriptSite::GetDocVersionString
216
217 This method is not implemented. The scripting engine should assume
218 that the script is in sync with the document.
219*/
220HRESULT WINAPI QAxScriptSite::GetDocVersionString(BSTR * /*version*/)
221{
222 return E_NOTIMPL;
223}
224
225/*
226 Implements IActiveScriptSite::OnScriptTerminate
227
228 This method is usually not called, but if it is it fires
229 QAxScript::finished().
230*/
231HRESULT WINAPI QAxScriptSite::OnScriptTerminate(const VARIANT *result, const EXCEPINFO *exception)
232{
233 emit script->finished();
234
235 if (result && result->vt != VT_EMPTY)
236 emit script->finished(VARIANTToQVariant(*result, 0));
237 if (exception)
238 emit script->finished(exception->wCode,
239 QString::fromUtf16((const ushort*)exception->bstrSource),
240 QString::fromUtf16((const ushort*)exception->bstrDescription),
241 QString::fromUtf16((const ushort*)exception->bstrHelpFile)
242 );
243 return S_OK;
244}
245
246/*
247 Implements IActiveScriptSite::OnEnterScript
248
249 Fires QAxScript::entered() to inform the host that the
250 scripting engine has begun executing the script code.
251*/
252HRESULT WINAPI QAxScriptSite::OnEnterScript()
253{
254 emit script->entered();
255 return S_OK;
256}
257
258/*
259 Implements IActiveScriptSite::OnLeaveScript
260
261 Fires QAxScript::finished() to inform the host that the
262 scripting engine has returned from executing the script code.
263*/
264HRESULT WINAPI QAxScriptSite::OnLeaveScript()
265{
266 emit script->finished();
267 return S_OK;
268}
269
270/*
271 Implements IActiveScriptSite::OnScriptError
272
273 Fires QAxScript::error() to inform the host that an
274 that an execution error occurred while the engine was running the script.
275*/
276HRESULT WINAPI QAxScriptSite::OnScriptError(IActiveScriptError *error)
277{
278 EXCEPINFO exception;
279 memset(&exception, 0, sizeof(exception));
280 DWORD context;
281 ULONG lineNumber;
282 LONG charPos;
283 BSTR bstrLineText;
284 QString lineText;
285
286 error->GetExceptionInfo(&exception);
287 error->GetSourcePosition(&context, &lineNumber, &charPos);
288 HRESULT hres = error->GetSourceLineText(&bstrLineText);
289 if (hres == S_OK) {
290 lineText = QString::fromUtf16((const ushort*)bstrLineText);
291 SysFreeString(bstrLineText);
292 }
293 SysFreeString(exception.bstrSource);
294 SysFreeString(exception.bstrDescription);
295 SysFreeString(exception.bstrHelpFile);
296
297 emit script->error(exception.wCode, QString::fromUtf16((const ushort*)exception.bstrDescription), lineNumber, lineText);
298
299 return S_OK;
300}
301
302/*
303 Implements IActiveScriptSite::OnStateChange
304
305 Fires QAxScript::stateChanged() to inform the
306 the host that the scripting engine has changed states.
307*/
308HRESULT WINAPI QAxScriptSite::OnStateChange(SCRIPTSTATE ssScriptState)
309{
310 emit script->stateChanged(ssScriptState);
311 return S_OK;
312}
313
314/*
315 \internal
316 Returns the toplevel widget parent of this script, or
317 the application' active window if there is no widget parent.
318*/
319QWidget *QAxScriptSite::window() const
320{
321 QWidget *w = 0;
322 QObject *p = script->parent();
323 while (!w && p) {
324 w = qobject_cast<QWidget*>(p);
325 p = p->parent();
326 }
327
328 if (w)
329 w = w->window();
330 if (!w && qApp)
331 w = qApp->activeWindow();
332
333 return w;
334}
335
336/*
337 Implements IActiveScriptSiteWindow::GetWindow
338
339 Retrieves the handle to a window that can act as the owner of a
340 pop-up window that the scripting engine must display.
341*/
342HRESULT WINAPI QAxScriptSite::GetWindow(HWND *phwnd)
343{
344 if (!phwnd)
345 return E_POINTER;
346
347 *phwnd = 0;
348 QWidget *w = window();
349 if (!w)
350 return E_FAIL;
351
352 *phwnd = w->winId();
353 return S_OK;
354}
355
356/*
357 Implements IActiveScriptSiteWindow::EnableModeless
358
359 Causes the host to enable or disable its main window
360 as well as any modeless dialog boxes.
361*/
362HRESULT WINAPI QAxScriptSite::EnableModeless(BOOL fEnable)
363{
364 QWidget *w = window();
365 if (!w)
366 return E_FAIL;
367
368 EnableWindow(w->winId(), fEnable);
369 return S_OK;
370}
371
372#endif //QT_NO_QAXSCRIPT
373
374
375/*!
376 \class QAxScriptEngine
377 \brief The QAxScriptEngine class provides a wrapper around a script engine.
378 \inmodule QAxContainer
379
380 Every instance of the QAxScriptEngine class represents an interpreter
381 for script code in a particular scripting language. The class is usually
382 not used directly. The QAxScript and QAxScriptManager classes provide
383 convenient functions to handle and call script code.
384
385 Direct access to the script engine is provided through
386 queryInterface().
387
388 \warning This class is not available with the bcc5.5 and MingW
389 compilers.
390
391 \sa QAxScript, QAxScriptManager, QAxBase, {ActiveQt Framework}
392*/
393
394/*!
395 \enum QAxScriptEngine::State
396
397 The State enumeration defines the different states a script
398 engine can be in.
399
400 \value Uninitialized The script has been created, but not yet initialized
401 \value Initialized The script has been initialized, but is not running
402 \value Started The script can execute code, but does not yet handle events
403 \value Connected The script can execute code and is connected so
404 that it can handle events
405 \value Disconnected The script is loaded, but is not connected to
406 event sources
407 \value Closed The script has been closed.
408*/
409
410/*!
411 Constructs a QAxScriptEngine object interpreting script code in \a language
412 provided by the code in \a script. This is usually done by the QAxScript
413 class when \link QAxScript::load() loading a script\endlink.
414
415 Instances of QAxScriptEngine should always have both a language and a
416 script.
417*/
418QAxScriptEngine::QAxScriptEngine(const QString &language, QAxScript *script)
419: QAxObject(script), script_code(script), engine(0), script_language(language)
420{
421#ifdef QT_CHECK_STATE
422 if (language.isEmpty())
423 qWarning("QAxScriptEngine: created without language");
424
425 if (!script_code)
426 qWarning("QAxScriptEngine: created without script");
427#endif
428 setObjectName(QLatin1String("QAxScriptEngine_") + language);
429 disableClassInfo();
430 disableEventSink();
431}
432
433/*!
434 Destroys the QAxScriptEngine object, releasing all allocated
435 resources.
436*/
437QAxScriptEngine::~QAxScriptEngine()
438{
439#ifndef QT_NO_QAXSCRIPT
440 if (engine) {
441 engine->SetScriptState(SCRIPTSTATE_DISCONNECTED);
442 engine->Close();
443 engine->Release();
444 }
445#endif
446}
447
448/*!
449 \fn QString QAxScriptEngine::scriptLanguage() const
450 Returns the scripting language, for example "VBScript",
451 or "JScript".
452*/
453
454/*!
455 \reimp
456*/
457bool QAxScriptEngine::initialize(IUnknown **ptr)
458{
459 *ptr = 0;
460
461#ifndef QT_NO_QAXSCRIPT
462 if (!script_code || script_language.isEmpty())
463 return false;
464
465 CLSID clsid;
466 HRESULT hres = CLSIDFromProgID((WCHAR*)script_language.utf16(), &clsid);
467 if(FAILED(hres))
468 return false;
469
470 CoCreateInstance(clsid, 0, CLSCTX_INPROC_SERVER, IID_IActiveScript, (void**)&engine);
471 if (!engine)
472 return false;
473
474 IActiveScriptParse *parser = 0;
475 engine->QueryInterface(IID_IActiveScriptParse, (void**)&parser);
476 if (!parser) {
477 engine->Release();
478 engine = 0;
479 return false;
480 }
481
482 if (engine->SetScriptSite(script_code->script_site) != S_OK) {
483 engine->Release();
484 engine = 0;
485 return false;
486 }
487 if (parser->InitNew() != S_OK) {
488 parser->Release();
489 engine->Release();
490 engine = 0;
491 return false;
492 }
493
494 BSTR bstrCode = QStringToBSTR(script_code->scriptCode());
495#ifdef Q_OS_WIN64
496 hres = parser->ParseScriptText(bstrCode, 0, 0, 0, DWORDLONG(this), 0, SCRIPTTEXT_ISVISIBLE, 0, 0);
497#else
498 hres = parser->ParseScriptText(bstrCode, 0, 0, 0, DWORD(this), 0, SCRIPTTEXT_ISVISIBLE, 0, 0);
499#endif
500 SysFreeString(bstrCode);
501
502 parser->Release();
503 parser = 0;
504
505 script_code->updateObjects();
506
507 if (engine->SetScriptState(SCRIPTSTATE_CONNECTED) != S_OK) {
508 engine = 0;
509 return false;
510 }
511
512 IDispatch *scriptDispatch = 0;
513 engine->GetScriptDispatch(0, &scriptDispatch);
514 if (scriptDispatch) {
515 scriptDispatch->QueryInterface(IID_IUnknown, (void**)ptr);
516 scriptDispatch->Release();
517 }
518#endif
519
520 return *ptr != 0;
521}
522
523/*!
524 \fn bool QAxScriptEngine::isValid() const
525
526 Returns true if the script engine has been initialized
527 correctly; otherwise returns false.
528*/
529
530/*!
531 Returns true if the script engine supports introspection;
532 otherwise returns false.
533*/
534bool QAxScriptEngine::hasIntrospection() const
535{
536 if (!isValid())
537 return false;
538
539 IDispatch *scriptDispatch = 0;
540 QAxBase::queryInterface(IID_IDispatch, (void**)&scriptDispatch);
541 if (!scriptDispatch)
542 return false;
543
544 UINT tic = 0;
545 HRESULT hres = scriptDispatch->GetTypeInfoCount(&tic);
546 scriptDispatch->Release();
547 return hres == S_OK && tic > 0;
548}
549
550/*!
551 Requests the interface \a uuid from the script engine object and
552 sets the value of \a iface to the provided interface, or to 0 if
553 the requested interface could not be provided.
554
555 Returns the result of the QueryInterface implementation of the COM
556 object.
557*/
558long QAxScriptEngine::queryInterface(const QUuid &uuid, void **iface) const
559{
560 *iface = 0;
561 if (!engine)
562 return E_NOTIMPL;
563
564#ifndef QT_NO_QAXSCRIPT
565 return engine->QueryInterface(uuid, iface);
566#else
567 return E_NOTIMPL;
568#endif
569}
570
571/*!
572 Returns the state of the script engine.
573*/
574QAxScriptEngine::State QAxScriptEngine::state() const
575{
576 if (!engine)
577 return Uninitialized;
578
579#ifndef QT_NO_QAXSCRIPT
580 SCRIPTSTATE state;
581 engine->GetScriptState(&state);
582 return (State)state;
583#else
584 return Uninitialized;
585#endif
586}
587
588/*!
589 Sets the state of the script engine to \a st.
590 Calling this function is usually not necessary.
591*/
592void QAxScriptEngine::setState(State st)
593{
594#ifndef QT_NO_QAXSCRIPT
595 if (!engine)
596 return;
597
598 engine->SetScriptState((SCRIPTSTATE)st);
599#endif
600}
601
602/*!
603 Registers an item with the script engine. Script code can
604 refer to this item using \a name.
605*/
606void QAxScriptEngine::addItem(const QString &name)
607{
608#ifndef QT_NO_QAXSCRIPT
609 if (!engine)
610 return;
611
612 engine->AddNamedItem((WCHAR*)name.utf16(), SCRIPTITEM_ISSOURCE|SCRIPTITEM_ISVISIBLE);
613#endif
614}
615
616/*!
617 \class QAxScript
618 \brief The QAxScript class provides a wrapper around script code.
619 \inmodule QAxContainer
620
621 Every instance of the QAxScript class represents a piece of
622 scripting code in a particular scripting language. The code is
623 loaded into the script engine using load(). Functions declared
624 in the code can be called using call().
625
626 The script provides scriptEngine() provides feedback to the
627 application through signals. The most important signal is the
628 error() signal. Direct access to the QAxScriptEngine is provided
629 through the scriptEngine() function.
630
631 \warning This class is not available with the bcc5.5 and MingW
632 compilers.
633
634 \sa QAxScriptEngine, QAxScriptManager, QAxBase, {ActiveQt Framework}
635*/
636
637/*!
638 \enum QAxScript::FunctionFlags
639
640 This FunctionFlags enum describes formatting for function introspection.
641
642 \value FunctionNames Only function names are returned.
643 \value FunctionSignatures Returns the functions with signatures.
644*/
645
646/*!
647 Constructs a QAxScript object called \a name and registers
648 it with the QAxScriptManager \a manager. This is usually done by the
649 QAxScriptManager class when \link QAxScriptManager::load() loading a
650 script\endlink.
651
652 A script should always have a name. A manager is necessary to allow
653 the script code to reference objects in the application. The \a manager
654 takes ownership of the object.
655*/
656QAxScript::QAxScript(const QString &name, QAxScriptManager *manager)
657: QObject(manager), script_name(name), script_manager(manager),
658script_engine(0)
659{
660 if (manager) {
661 manager->d->scriptDict.insert(name, this);
662 connect(this, SIGNAL(error(int,QString,int,QString)),
663 manager, SLOT(scriptError(int,QString,int,QString)));
664 }
665
666#ifndef QT_NO_QAXSCRIPT
667 script_site = new QAxScriptSite(this);
668#else
669 script_site = 0;
670#endif