~oif-team/ubuntu/natty/qt4-x11/xi2.1

« back to all changes in this revision

Viewing changes to doc/src/plugins-howto.qdoc

  • Committer: Bazaar Package Importer
  • Author(s): Alessandro Ghersi
  • Date: 2009-11-02 18:30:08 UTC
  • mfrom: (1.2.2 upstream)
  • mto: (15.2.5 experimental)
  • mto: This revision was merged to the branch mainline in revision 88.
  • Revision ID: james.westby@ubuntu.com-20091102183008-b6a4gcs128mvfb3m
Tags: upstream-4.6.0~beta1
ImportĀ upstreamĀ versionĀ 4.6.0~beta1

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/****************************************************************************
2
 
**
3
 
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4
 
** Contact: Nokia Corporation (qt-info@nokia.com)
5
 
**
6
 
** This file is part of the documentation of the Qt Toolkit.
7
 
**
8
 
** $QT_BEGIN_LICENSE:LGPL$
9
 
** Commercial Usage
10
 
** Licensees holding valid Qt Commercial licenses may use this file in
11
 
** accordance with the Qt Commercial License Agreement provided with the
12
 
** Software or, alternatively, in accordance with the terms contained in
13
 
** a written agreement between you and Nokia.
14
 
**
15
 
** GNU Lesser General Public License Usage
16
 
** Alternatively, this file may be used under the terms of the GNU Lesser
17
 
** General Public License version 2.1 as published by the Free Software
18
 
** Foundation and appearing in the file LICENSE.LGPL included in the
19
 
** packaging of this file.  Please review the following information to
20
 
** ensure the GNU Lesser General Public License version 2.1 requirements
21
 
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22
 
**
23
 
** In addition, as a special exception, Nokia gives you certain
24
 
** additional rights. These rights are described in the Nokia Qt LGPL
25
 
** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26
 
** package.
27
 
**
28
 
** GNU General Public License Usage
29
 
** Alternatively, this file may be used under the terms of the GNU
30
 
** General Public License version 3.0 as published by the Free Software
31
 
** Foundation and appearing in the file LICENSE.GPL included in the
32
 
** packaging of this file.  Please review the following information to
33
 
** ensure the GNU General Public License version 3.0 requirements will be
34
 
** met: http://www.gnu.org/copyleft/gpl.html.
35
 
**
36
 
** If you are unsure which license is appropriate for your use, please
37
 
** contact the sales department at http://www.qtsoftware.com/contact.
38
 
** $QT_END_LICENSE$
39
 
**
40
 
****************************************************************************/
41
 
 
42
 
/*!
43
 
    \page plugins-howto.html
44
 
    \title How to Create Qt Plugins
45
 
    \brief A guide to creating plugins to extend Qt applications and functionality provided by Qt.
46
 
    \ingroup howto
47
 
 
48
 
    \keyword QT_DEBUG_PLUGINS
49
 
    \keyword QT_NO_PLUGIN_CHECK
50
 
 
51
 
    Qt provides two APIs for creating plugins:
52
 
 
53
 
    \list
54
 
    \o A higher-level API for writing extensions to Qt itself: custom database
55
 
       drivers, image formats, text codecs, custom styles, etc.
56
 
    \o A lower-level API for extending Qt applications.
57
 
    \endlist
58
 
 
59
 
    For example, if you want to write a custom QStyle subclass and
60
 
    have Qt applications load it dynamically, you would use the
61
 
    higher-level API.
62
 
 
63
 
    Since the higher-level API is built on top of the lower-level API,
64
 
    some issues are common to both.
65
 
 
66
 
    If you want to provide plugins for use with \QD, see the QtDesigner
67
 
    module documentation.
68
 
 
69
 
    Topics:
70
 
 
71
 
    \tableofcontents
72
 
 
73
 
    \section1 The Higher-Level API: Writing Qt Extensions
74
 
 
75
 
    Writing a plugin that extends Qt itself is achieved by
76
 
    subclassing the appropriate plugin base class, implementing a few
77
 
    functions, and adding a macro.
78
 
 
79
 
    There are several plugin base classes. Derived plugins are stored
80
 
    by default in sub-directories of the standard plugin directory. Qt
81
 
    will not find plugins if they are not stored in the right
82
 
    directory.
83
 
 
84
 
    \table
85
 
    \header \o Base Class              \o Directory Name                \o Key Case Sensitivity
86
 
    \row    \o QAccessibleBridgePlugin \o \c accessiblebridge \o Case Sensitive
87
 
    \row    \o QAccessiblePlugin       \o \c accessible       \o Case Sensitive
88
 
    \row    \o QDecorationPlugin       \o \c decorations      \o Case Insensitive
89
 
    \row    \o QFontEnginePlugin       \o \c fontengines      \o Case Insensitive
90
 
    \row    \o QIconEnginePlugin       \o \c iconengines      \o Case Insensitive
91
 
    \row    \o QImageIOPlugin          \o \c imageformats     \o Case Sensitive
92
 
    \row    \o QInputContextPlugin     \o \c inputmethods     \o Case Sensitive
93
 
    \row    \o QKbdDriverPlugin        \o \c kbddrivers       \o Case Insensitive
94
 
    \row    \o QMouseDriverPlugin      \o \c mousedrivers     \o Case Insensitive
95
 
    \row    \o QPictureFormatPlugin    \o \c pictureformats   \o Case Sensitive
96
 
    \row    \o QScreenDriverPlugin     \o \c gfxdrivers       \o Case Insensitive
97
 
    \row    \o QScriptExtensionPlugin  \o \c script           \o Case Sensitive
98
 
    \row    \o QSqlDriverPlugin        \o \c sqldrivers       \o Case Sensitive
99
 
    \row    \o QStylePlugin            \o \c styles           \o Case Insensitive
100
 
    \row    \o QTextCodecPlugin        \o \c codecs           \o Case Sensitive
101
 
    \endtable
102
 
 
103
 
    But where is the \c{plugins} directory? When the application
104
 
    is run, Qt will first treat the application's executable directory
105
 
    as the \c{pluginsbase}. For example if the application is in
106
 
    \c{C:\Program Files\MyApp} and has a style plugin, Qt will look in
107
 
    \c{C:\Program Files\MyApp\styles}. (See
108
 
    QCoreApplication::applicationDirPath() for how to find out where
109
 
    the application's executable is.) Qt will also look in the
110
 
    directory specified by
111
 
    QLibraryInfo::location(QLibraryInfo::PluginsPath), which typically
112
 
    is located in \c QTDIR/plugins (where \c QTDIR is the directory
113
 
    where Qt is installed). If you want Qt to look in additional
114
 
    places you can add as many paths as you need with calls to
115
 
    QCoreApplication::addLibraryPath(). And if you want to set your
116
 
    own path or paths you can use QCoreApplication::setLibraryPaths().
117
 
    You can also use a \c qt.conf file to override the hard-coded
118
 
    paths that are compiled into the Qt library. For more information,
119
 
    see the \l {Using qt.conf} documentation. Yet another possibility
120
 
    is to set the \c QT_PLUGIN_PATH environment variable before running
121
 
    the application. If set, Qt will look for plugins in the
122
 
    paths (separated by the system path separator) specified in the variable.
123
 
 
124
 
    Suppose that you have a new style class called \c MyStyle that you
125
 
    want to make available as a plugin. The required code is
126
 
    straightforward, here is the class definition (\c
127
 
    mystyleplugin.h):
128
 
 
129
 
    \snippet doc/src/snippets/code/doc_src_plugins-howto.qdoc 0
130
 
 
131
 
    Ensure that the class implementation is located in a \c .cpp file
132
 
    (including the class definition):
133
 
 
134
 
    \snippet doc/src/snippets/code/doc_src_plugins-howto.qdoc 1
135
 
 
136
 
    (Note that QStylePlugin is case insensitive, and the lower-case
137
 
    version of the key is used in our
138
 
    \l{QStylePlugin::create()}{create()} implementation; most other
139
 
    plugins are case sensitive.)
140
 
 
141
 
    For database drivers, image formats, text codecs, and most other
142
 
    plugin types, no explicit object creation is required. Qt will
143
 
    find and create them as required. Styles are an exception, since
144
 
    you might want to set a style explicitly in code. To apply a
145
 
    style, use code like this:
146
 
 
147
 
    \snippet doc/src/snippets/code/doc_src_plugins-howto.qdoc 2
148
 
 
149
 
    Some plugin classes require additional functions to be
150
 
    implemented. See the class documentation for details of the
151
 
    virtual functions that must be reimplemented for each type of
152
 
    plugin.
153
 
 
154
 
    Qt applications automatically know which plugins are available,
155
 
    because plugins are stored in the standard plugin subdirectories.
156
 
    Because of this applications don't require any code to find and load
157
 
    plugins, since Qt handles them automatically.
158
 
 
159
 
    The default directory for plugins is \c{QTDIR/plugins} (where \c
160
 
    QTDIR is the directory where Qt is installed), with each type of
161
 
    plugin in a subdirectory for that type, e.g. \c styles. If you
162
 
    want your applications to use plugins and you don't want to use
163
 
    the standard plugins path, have your installation process
164
 
    determine the path you want to use for the plugins, and save the
165
 
    path, e.g. using QSettings, for the application to read when it
166
 
    runs. The application can then call
167
 
    QCoreApplication::addLibraryPath() with this path and your
168
 
    plugins will be available to the application. Note that the final
169
 
    part of the path (e.g., \c styles) cannot be changed.
170
 
 
171
 
    The normal way to include a plugin with an application is either
172
 
    to \l{Static Plugins}{compile it in with the application} or to
173
 
    compile it into a dynamic library and use it like any other
174
 
    library.
175
 
 
176
 
    If you want the plugin to be loadable then one approach is to
177
 
    create a subdirectory under the application and place the plugin
178
 
    in that directory. If you distribute any of the plugins that come
179
 
    with Qt (the ones located in the \c plugins directory), you must
180
 
    copy the sub-directory under \c plugins where the plugin is
181
 
    located to your applications root folder (i.e., do not include the
182
 
    \c plugins directory).
183
 
 
184
 
    For more information about deployment,
185
 
    see the \l {Deploying Qt Applications} documentation.
186
 
 
187
 
    The \l{Style Plugin Example} shows how to implement a plugin
188
 
    that extends the QStylePlugin base class.
189
 
 
190
 
    \section1 The Lower-Level API: Extending Qt Applications
191
 
 
192
 
    Not only Qt itself but also Qt application can be extended
193
 
    through plugins. This requires the application to detect and load
194
 
    plugins using QPluginLoader. In that context, plugins may provide
195
 
    arbitrary functionality and are not limited to database drivers,
196
 
    image formats, text codecs, styles, and the other types of plugin
197
 
    that extend Qt's functionality.
198
 
 
199
 
    Making an application extensible through plugins involves the
200
 
    following steps:
201
 
 
202
 
    \list 1
203
 
    \o Define a set of interfaces (classes with only pure virtual
204
 
       functions) used to talk to the plugins.
205
 
    \o Use the Q_DECLARE_INTERFACE() macro to tell Qt's
206
 
       \l{meta-object system} about the interface.
207
 
    \o Use QPluginLoader in the application to load the plugins.
208
 
    \o Use qobject_cast() to test whether a plugin implements a given
209
 
       interface.
210
 
    \endlist
211
 
 
212
 
    Writing a plugin involves these steps:
213
 
 
214
 
    \list 1
215
 
    \o Declare a plugin class that inherits from QObject and from the
216
 
       interfaces that the plugin wants to provide.
217
 
    \o Use the Q_INTERFACES() macro to tell Qt's \l{meta-object
218
 
       system} about the interfaces.
219
 
    \o Export the plugin using the Q_EXPORT_PLUGIN2() macro.
220
 
    \o Build the plugin using a suitable \c .pro file.
221
 
    \endlist
222
 
 
223
 
    For example, here's the definition of an interface class:
224
 
 
225
 
    \snippet examples/tools/plugandpaint/interfaces.h 2
226
 
 
227
 
    Here's the definition of a plugin class that implements that
228
 
    interface:
229
 
 
230
 
    \snippet examples/tools/plugandpaintplugins/extrafilters/extrafiltersplugin.h 0
231
 
 
232
 
    The \l{tools/plugandpaint}{Plug & Paint} example documentation
233
 
    explains this process in detail. See also \l{Creating Custom
234
 
    Widgets for Qt Designer} for information about issues that are
235
 
    specific to \QD. You can also take a look at the \l{Echo Plugin
236
 
    Example} is a more trivial example on how to implement a plugin
237
 
    that extends Qt applications. Please note that a QCoreApplication
238
 
    must have been initialized before plugins can be loaded.
239
 
 
240
 
    \section1 Loading and Verifying Plugins Dynamically
241
 
 
242
 
    When loading plugins, the Qt library does some sanity checking to
243
 
    determine whether or not the plugin can be loaded and used. This
244
 
    provides the ability to have multiple versions and configurations of
245
 
    the Qt library installed side by side.
246
 
 
247
 
    \list
248
 
    \o Plugins linked with a Qt library that has a higher version number
249
 
       will not be loaded by a library with a lower version number.
250
 
 
251
 
      \br
252
 
      \bold{Example:} Qt 4.3.0 will \e{not} load a plugin built with Qt 4.3.1.
253
 
 
254
 
    \o Plugins linked with a Qt library that has a lower major version
255
 
       number will not be loaded by a library with a higher major version
256
 
       number.
257
 
 
258
 
      \br
259
 
      \bold{Example:} Qt 4.3.1 will \e{not} load a plugin built with Qt 3.3.1.
260
 
      \br
261
 
      \bold{Example:} Qt 4.3.1 will load plugins built with Qt 4.3.0 and Qt 4.2.3.
262
 
 
263
 
    \o The Qt library and all plugins are built using a \e {build
264
 
       key}. The build key in the Qt library is examined against the build
265
 
       key in the plugin, and if they match, the plugin is loaded. If the
266
 
       build keys do not match, then the Qt library refuses to load the
267
 
       plugin.
268
 
 
269
 
      \br \bold{Rationale:} See the \l{#The Build Key}{The Build Key} section below.
270
 
    \endlist
271
 
 
272
 
    When building plugins to extend an application, it is important to ensure
273
 
    that the plugin is configured in the same way as the application. This means
274
 
    that if the application was built in release mode, plugins should be built
275
 
    in release mode, too.
276
 
 
277
 
    If you configure Qt to be built in both debug and release modes,
278
 
    but only build applications in release mode, you need to ensure that your
279
 
    plugins are also built in release mode. By default, if a debug build of Qt is
280
 
    available, plugins will \e only be built in debug mode. To force the
281
 
    plugins to be built in release mode, add the following line to the plugin's
282
 
    project file:
283
 
 
284
 
    \snippet doc/src/snippets/code/doc_src_plugins-howto.qdoc 3
285
 
 
286
 
    This will ensure that the plugin is compatible with the version of the library
287
 
    used in the application.
288
 
 
289
 
    \section2 The Build Key
290
 
 
291
 
    When loading plugins, Qt checks the build key of each plugin against its
292
 
    own configuration to ensure that only compatible plugins are loaded; any
293
 
    plugins that are configured differently are not loaded.
294
 
 
295
 
    The build key contains the following information:
296
 
    \list
297
 
    \o Architecture, operating system and compiler.
298
 
 
299
 
       \e {Rationale:}
300
 
       In cases where different versions of the same compiler do not
301
 
       produce binary compatible code, the version of the compiler is
302
 
       also present in the build key.
303
 
 
304
 
    \o Configuration of the Qt library. The configuration is a list
305
 
       of the missing features that affect the available API in the
306
 
       library.
307
 
 
308
 
       \e {Rationale:}
309
 
       Two different configurations of the same version of
310
 
       the Qt library are not binary compatible. The Qt library that
311
 
       loads the plugin uses the list of (missing) features to
312
 
       determine if the plugin is binary compatible.
313
 
 
314
 
       \e {Note:} There are cases where a plugin can use features that are
315
 
       available in two different configurations. However, the
316
 
       developer writing plugins would need to know which features are
317
 
       in use, both in their plugin and internally by the utility
318
 
       classes in Qt. The Qt library would require complex feature
319
 
       and dependency queries and verification when loading plugins.
320
 
       Requiring this would place an unnecessary burden on the developer, and
321
 
       increase the overhead of loading a plugin. To reduce both
322
 
       development time and application runtime costs, a simple string
323
 
       comparision of the build keys is used.
324
 
 
325
 
    \o Optionally, an extra string may be specified on the configure
326
 
       script command line.
327
 
 
328
 
       \e {Rationale:}
329
 
       When distributing binaries of the Qt library with an
330
 
       application, this provides a way for developers to write
331
 
       plugins that can only be loaded by the library with which the
332
 
       plugins were linked.
333
 
    \endlist
334
 
 
335
 
    For debugging purposes, it is possible to override the run-time build key
336
 
    checks by configuring Qt with the \c QT_NO_PLUGIN_CHECK preprocessor macro
337
 
    defined.
338
 
 
339
 
    \section1 Static Plugins
340
 
 
341
 
    Plugins can be linked statically against your application. If you
342
 
    build the static version of Qt, this is the only option for
343
 
    including Qt's predefined plugins.
344
 
 
345
 
    When compiled as a static library, Qt provides the following
346
 
    static plugins:
347
 
 
348
 
    \table
349
 
    \header \o Plugin name                  \o Type               \o Description
350
 
    \row    \o \c qtaccessiblecompatwidgets \o Accessibility      \o Accessibility for Qt 3 support widgets
351
 
    \row    \o \c qtaccessiblewidgets       \o Accessibility      \o Accessibility for Qt widgets
352
 
    \row    \o \c qdecorationdefault        \o Decorations (Qt Extended) \o Default style
353
 
    \row    \o \c qdecorationwindows        \o Decorations (Qt Extended) \o Windows style
354
 
    \row    \o \c qgif                      \o Image formats      \o GIF
355
 
    \row    \o \c qjpeg                     \o Image formats      \o JPEG
356
 
    \row    \o \c qmng                      \o Image formats      \o MNG
357
 
    \row    \o \c qico                      \o Image formats      \o ICO
358
 
    \row    \o \c qsvg                      \o Image formats      \o SVG
359
 
    \row    \o \c qtiff                     \o Image formats      \o TIFF
360
 
    \row    \o \c qimsw_multi               \o Input methods (Qt Extended) \o Input Method Switcher
361
 
    \row    \o \c qwstslibmousehandler      \o Mouse drivers (Qt Extended) \o \c tslib mouse
362
 
    \row    \o \c qgfxtransformed           \o Graphic drivers (Qt Extended) \o Transformed screen
363
 
    \row    \o \c qgfxvnc                   \o Graphic drivers (Qt Extended) \o VNC
364
 
    \row    \o \c qscreenvfb                \o Graphic drivers (Qt Extended) \o Virtual frame buffer
365
 
    \row    \o \c qsqldb2                   \o SQL driver         \o IBM DB2    \row    \o \c qsqlibase       \o SQL driver         \o Borland InterBase
366
 
    \row    \o \c qsqlite                   \o SQL driver         \o SQLite version 3
367
 
    \row    \o \c qsqlite2                  \o SQL driver         \o SQLite version 2
368
 
    \row    \o \c qsqlmysql                 \o SQL driver         \o MySQL
369
 
    \row    \o \c qsqloci                   \o SQL driver         \o Oracle (OCI)
370
 
    \row    \o \c qsqlodbc                  \o SQL driver         \o Open Database Connectivity (ODBC)
371
 
    \row    \o \c qsqlpsql                  \o SQL driver         \o PostgreSQL
372
 
    \row    \o \c qsqltds                   \o SQL driver         \o Sybase Adaptive Server (TDS)
373
 
    \row    \o \c qcncodecs                 \o Text codecs        \o Simplified Chinese (People's Republic of China)
374
 
    \row    \o \c qjpcodecs                 \o Text codecs        \o Japanese
375
 
    \row    \o \c qkrcodecs                 \o Text codecs        \o Korean
376
 
    \row    \o \c qtwcodecs                 \o Text codecs        \o Traditional Chinese (Taiwan)
377
 
    \endtable
378
 
 
379
 
    To link statically against those plugins, you need to use the
380
 
    Q_IMPORT_PLUGIN() macro in your application and you need to add
381
 
    the required plugins to your build using \c QTPLUGIN.
382
 
    For example, in your \c main.cpp:
383
 
 
384
 
    \snippet doc/src/snippets/code/doc_src_plugins-howto.qdoc 4
385
 
 
386
 
    In the \c .pro file for your application, you need the following
387
 
    entry:
388
 
 
389
 
    \snippet doc/src/snippets/code/doc_src_plugins-howto.qdoc 5
390
 
 
391
 
    It is also possible to create your own static plugins, by
392
 
    following these steps:
393
 
 
394
 
    \list 1
395
 
    \o Add \c{CONFIG += static} to your plugin's \c .pro file.
396
 
    \o Use the Q_IMPORT_PLUGIN() macro in your application.
397
 
    \o Link your application with your plugin library using \c LIBS
398
 
       in the \c .pro file.
399
 
    \endlist
400
 
 
401
 
    See the \l{tools/plugandpaint}{Plug & Paint} example and the
402
 
    associated \l{tools/plugandpaintplugins/basictools}{Basic Tools}
403
 
    plugin for details on how to do this.
404
 
 
405
 
    \note If you are not using qmake to build your application you need
406
 
    to make sure that the \c{QT_STATICPLUGIN} preprocessor macro is
407
 
    defined.
408
 
 
409
 
    \sa QPluginLoader, QLibrary, {Plug & Paint Example}
410
 
 
411
 
    \section1 The Plugin Cache
412
 
 
413
 
    In order to speed up loading and validation of plugins, some of
414
 
    the information that is collected when plugins are loaded is cached
415
 
    through QSettings. This includes information about whether or not
416
 
    a plugin was successfully loaded, so that subsequent load operations
417
 
    don't try to load an invalid plugin. However, if the "last modified"
418
 
    timestamp of a plugin has changed, the plugin's cache entry is
419
 
    invalidated and the plugin is reloaded regardless of the values in
420
 
    the cache entry, and the cache entry itself is updated with the new
421
 
    result.
422
 
 
423
 
    This also means that the timestamp must be updated each time the
424
 
    plugin or any dependent resources (such as a shared library) is
425
 
    updated, since the dependent resources might influence the result
426
 
    of loading a plugin.
427
 
 
428
 
    Sometimes, when developing plugins, it is necessary to remove entries
429
 
    from the plugin cache. Since Qt uses QSettings to manage the plugin
430
 
    cache, the locations of plugins are platform-dependent; see
431
 
    \l{QSettings#Platform-Specific Notes}{the QSettings documentation}
432
 
    for more information about each platform.
433
 
 
434
 
    For example, on Windows the entries are stored in the registry, and the
435
 
    paths for each plugin will typically begin with either of these two strings:
436
 
 
437
 
    \snippet doc/src/snippets/code/doc_src_plugins-howto.qdoc 6
438
 
 
439
 
    \section1 Debugging Plugins
440
 
 
441
 
    There are a number of issues that may prevent correctly-written plugins from
442
 
    working with the applications that are designed to use them. Many of these
443
 
    are related to differences in the way that plugins and applications have been
444
 
    built, often arising from separate build systems and processes.
445
 
 
446
 
    The following table contains descriptions of the common causes of problems
447
 
    developers experience when creating plugins:
448
 
 
449
 
    \table
450
 
    \header \o Problem \o Cause \o Solution
451
 
    \row \o Plugins sliently fail to load even when opened directly by the
452
 
            application. \QD shows the plugin libraries in its
453
 
            \gui{Help|About Plugins} dialog, but no plugins are listed under each
454
 
            of them.
455
 
         \o The application and its plugins are built in different modes.
456
 
         \o Either share the same build information or build the plugins in both
457
 
            debug and release modes by appending the \c debug_and_release to
458
 
            the \l{qmake Variable Reference#CONFIG}{CONFIG} variable in each of
459
 
            their project files.
460
 
    \row \o A valid plugin that replaces an invalid (or broken) plugin fails to load.
461
 
         \o The entry for the plugin in the plugin cache indicates that the original
462
 
            plugin could not be loaded, causing Qt to ignore the replacement.
463
 
         \o Either ensure that the plugin's timestamp is updated, or delete the
464
 
            entry in the \l{#The Plugin Cache}{plugin cache}.
465
 
    \endtable
466
 
 
467
 
    You can also use the \c QT_DEBUG_PLUGINS environment variable to obtain
468
 
    diagnostic information from Qt about each plugin it tries to load. Set this
469
 
    variable to a non-zero value in the environment from which your application is
470
 
    launched.
471
 
*/