0
|
1 |
/****************************************************************************
|
|
2 |
**
|
|
3 |
** Copyright (C) 2009 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 documentation of the Qt Toolkit.
|
|
8 |
**
|
|
9 |
** $QT_BEGIN_LICENSE:LGPL$
|
|
10 |
** No Commercial Usage
|
|
11 |
** This file contains pre-release code and may not be distributed.
|
|
12 |
** You may use this file in accordance with the terms and conditions
|
|
13 |
** contained in the Technology Preview License Agreement accompanying
|
|
14 |
** this package.
|
|
15 |
**
|
|
16 |
** GNU Lesser General Public License Usage
|
|
17 |
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
18 |
** General Public License version 2.1 as published by the Free Software
|
|
19 |
** Foundation and appearing in the file LICENSE.LGPL included in the
|
|
20 |
** packaging of this file. Please review the following information to
|
|
21 |
** ensure the GNU Lesser General Public License version 2.1 requirements
|
|
22 |
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
23 |
**
|
|
24 |
** In addition, as a special exception, Nokia gives you certain additional
|
|
25 |
** rights. These rights are described in the Nokia Qt LGPL Exception
|
|
26 |
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
27 |
**
|
|
28 |
** If you have questions regarding the use of this file, please contact
|
|
29 |
** Nokia at qt-info@nokia.com.
|
|
30 |
**
|
|
31 |
**
|
|
32 |
**
|
|
33 |
**
|
|
34 |
**
|
|
35 |
**
|
|
36 |
**
|
|
37 |
**
|
|
38 |
** $QT_END_LICENSE$
|
|
39 |
**
|
|
40 |
****************************************************************************/
|
|
41 |
|
|
42 |
/*!
|
|
43 |
\page activeqt-dotnet.html
|
|
44 |
\title Dot Net Example (ActiveQt)
|
|
45 |
|
|
46 |
The Dot Net example demonstrates how Qt objects can be used in a
|
|
47 |
.NET environment, and how .NET objects can be used in a Qt
|
|
48 |
environment.
|
|
49 |
|
|
50 |
If you need to combine Qt and Win Forms widgets in the same
|
|
51 |
application, you might want to use the higher-level
|
|
52 |
\l{QtWinForms Solution} instead.
|
|
53 |
|
|
54 |
Contents:
|
|
55 |
|
|
56 |
\tableofcontents
|
|
57 |
|
|
58 |
\section1 Qt vs. .NET
|
|
59 |
|
|
60 |
Qt is a C++ library and is compiled into traditional, native
|
|
61 |
binaries that make full use of the performance provided by the
|
|
62 |
runtime environment.
|
|
63 |
|
|
64 |
One of the key concepts of .NET is the idea of "intermediate language
|
|
65 |
code" - the source code is compiled into a bytecode format, and at
|
|
66 |
runtime, that bytecode is executed in a virtual machine - the \e
|
|
67 |
{Common Language Runtime} (CLR).
|
|
68 |
|
|
69 |
Another key concept is that of \e {managed code}. This is essentially
|
|
70 |
intermediate language code written in such a way that the CLR can take
|
|
71 |
care of the memory management, i.e. the CLR will do automatic garbage
|
|
72 |
collection, so the application code does not need to explicitly free
|
|
73 |
the memory for unused objects.
|
|
74 |
|
|
75 |
The MS compilers for C# and VB.NET will only produce managed
|
|
76 |
code. Such programs cannot directly call normal, native functions
|
|
77 |
or classes. \footnote The .NET framework provides Platform Invocation
|
|
78 |
Services - P/Invoke - that enable managed code to call native C (not
|
|
79 |
C++) functions located in DLLs directly. The resulting application
|
|
80 |
then becomes partially unmanaged.\endfootnote
|
|
81 |
|
|
82 |
The MS C++ compiler for .NET on the other hand, can produce both
|
|
83 |
normal and managed code. To write a C++ class that can be compiled
|
|
84 |
into managed code, the developer must flag the class as managed using
|
|
85 |
the \c __gc keyword, and restrict the code to only use the subset of
|
|
86 |
C++ known as "Managed Extensions for C++", or MC++ for short. The
|
|
87 |
advantage is that MC++ code can freely call and use normal C++
|
|
88 |
functions and classes. And it also works the other way around: normal
|
|
89 |
C++ code can call managed functions and use managed classes (e.g. the
|
|
90 |
entire .NET framework class library), including managed functions and
|
|
91 |
classes implemented in C# or VB.NET. This feature of mixing managed
|
|
92 |
and normal C++ code immensely eases the interoperability with .NET,
|
|
93 |
and is by Microsoft referred to as the "It Just Works" (IJW) feature.
|
|
94 |
|
|
95 |
This document demonstrates two different ways of integrating normal
|
|
96 |
C++ code (that uses Qt) with managed .NET code. First, the manual way
|
|
97 |
is presented, which includes using a thin MC++ wrapper class around
|
|
98 |
the normal Qt/C++ class. Then, the automated way is presented, which
|
|
99 |
utilizes the ActiveQt framework as a generic bridge. The advantage of
|
|
100 |
the first method is that it gives the application developer full
|
|
101 |
control, while the second method requires less coding and relieves the
|
|
102 |
developer of dealing with the conversion between managed and normal
|
|
103 |
data objects.
|
|
104 |
|
|
105 |
The impatient reader, who right away wants to see a QPushButton
|
|
106 |
and a custom Qt widget (\l{activeqt/multiple}{QAxWidget2}) run in
|
|
107 |
a .NET GUI application is referred to the example directory of
|
|
108 |
ActiveQt. It contains the result of this walkthrough using both
|
|
109 |
C# and VB.NET, created with Visual Studio .NET (not 2003).
|
|
110 |
Load \c {examples/dotnet/walkthrough/csharp.csproj},
|
|
111 |
\c {examples/dotnet/walkthrough/vb.vbproj}
|
|
112 |
or \c {examples/dotnet/wrapper/wrapper.sln} into the IDE and run
|
|
113 |
the solution.
|
|
114 |
|
|
115 |
\bold{Remark:} You will notice that in the generated code the following line is
|
|
116 |
commented out:
|
|
117 |
|
|
118 |
\snippet doc/src/snippets/code/doc_src_examples_activeqt_dotnet.qdoc 0
|
|
119 |
|
|
120 |
This line is regenerated without comment whenever you change the
|
|
121 |
dialog, in which case you have to comment it out again to be able
|
|
122 |
to run the project. This is a bug in the original version of
|
|
123 |
Visual Studio.NET, and is fixed in the 2003 edition.
|
|
124 |
|
|
125 |
\section1 Walkthrough: .NET interop with MC++ and IJW
|
|
126 |
|
|
127 |
Normal C++ classes and functions can be used from managed .NET code by
|
|
128 |
providing thin wrapper classes written in MC++. The wrapper class will
|
|
129 |
take care of forwarding the calls to the normal C++ functions or
|
|
130 |
methods, and converting parameter data as necessary. Since the wrapper
|
|
131 |
class is a managed class, it can be used without further ado in any
|
|
132 |
managed .NET application, whether written in C#, VB.NET, MC++ or other
|
|
133 |
managed programming language.
|
|
134 |
|
|
135 |
\snippet examples/activeqt/dotnet/wrapper/lib/worker.h 0
|
|
136 |
|
|
137 |
The Qt class has nothing unusual for Qt users, and as even the Qt
|
|
138 |
specialities like \c Q_PROPERTY, \c slots and \c signals are
|
|
139 |
implemented with straight C++ they don't cause any trouble when
|
|
140 |
compiling this class with any C++ compiler.
|
|
141 |
|
|
142 |
\snippet examples/activeqt/dotnet/wrapper/lib/networker.h 0
|
|
143 |
|
|
144 |
The .NET wrapper class uses keywords that are part of MC++ to indicate
|
|
145 |
that the class is managed/garbage collected (\c {__gc}), and that \c
|
|
146 |
StatusString should be accessible as a property in languages that
|
|
147 |
support this concept (\c {__property}). We also declare an event
|
|
148 |
function \c statusStringChanged(String*) (\c {__event}), the
|
|
149 |
equivalent of the respective signal in the Qt class.
|
|
150 |
|
|
151 |
Before we can start implementing the wrapper class we need a way to
|
|
152 |
convert Qt's datatypes (and potentionally your own) into .NET
|
|
153 |
datatypes, e.g. \c QString objects need to be converted into objects
|
|
154 |
of type \c {String*}.
|
|
155 |
|
|
156 |
When operating on managed objects in normal C++ code, a little extra
|
|
157 |
care must be taken because of the CLR's garbage collection. A normal
|
|
158 |
pointer variable should not \footnote Indeed, the compiler will in
|
|
159 |
many cases disallow it. \endfootnote be used to refer to a managed
|
|
160 |
object. The reason is that the garbage collection can kick in at any
|
|
161 |
time and move the object to another place on the heap, leaving you
|
|
162 |
with an invalid pointer.
|
|
163 |
|
|
164 |
However, two methods are provided that solves this problem easily. The
|
|
165 |
first is to use a \e pinned pointer, i.e. declare the pointer variable
|
|
166 |
with the \c __pin keyword. This guarantees that the object pointed to
|
|
167 |
will not be moved by the garbage collector. It is recommended that
|
|
168 |
this method not be used to keep a references to managed objects for a
|
|
169 |
long time, since it will decrease the efficiency of the garbage
|
|
170 |
collector. The second way is to use the \c gcroot smartpointer
|
|
171 |
template type. This lets you create safe pointers to managed
|
|
172 |
objects. E.g. a variable of type \c gcroot<String> will always point
|
|
173 |
to the String object, even if it has been moved by the garbage
|
|
174 |
collector, and it can be used just like a normal pointer.
|
|
175 |
|
|
176 |
\snippet examples/activeqt/dotnet/wrapper/lib/tools.cpp 0
|
|
177 |
\codeline
|
|
178 |
\snippet examples/activeqt/dotnet/wrapper/lib/tools.cpp 1
|
|
179 |
|
|
180 |
The convertor functions can then be used in the wrapper class
|
|
181 |
implementation to call the functions in the native C++ class.
|
|
182 |
|
|
183 |
\snippet examples/activeqt/dotnet/wrapper/lib/networker.cpp 0
|
|
184 |
\codeline
|
|
185 |
\snippet examples/activeqt/dotnet/wrapper/lib/networker.cpp 1
|
|
186 |
|
|
187 |
The constructor and destructor simply create and destroy the Qt
|
|
188 |
object wrapped using the C++ operators \c new and \c delete.
|
|
189 |
|
|
190 |
\snippet examples/activeqt/dotnet/wrapper/lib/networker.cpp 2
|
|
191 |
|
|
192 |
The netWorker class delegates calls from the .NET code to the native
|
|
193 |
code. Although the transition between those two worlds implies a small
|
|
194 |
performance hit for each function call, and for the type conversion,
|
|
195 |
this should be negligible since we are anyway going to run within the
|
|
196 |
CLR.
|
|
197 |
|
|
198 |
\snippet examples/activeqt/dotnet/wrapper/lib/networker.cpp 3
|
|
199 |
|
|
200 |
The property setter calls the native Qt class before firing the
|
|
201 |
event using the \c __raise keyword.
|
|
202 |
|
|
203 |
This wrapper class can now be used in .NET code, e.g. using C++, C#,
|
|
204 |
Visual Basic or any other programming language available for .NET.
|
|
205 |
|
|
206 |
\snippet examples/activeqt/dotnet/wrapper/main.cs 0
|
|
207 |
\snippet examples/activeqt/dotnet/wrapper/main.cs 1
|
|
208 |
\snippet examples/activeqt/dotnet/wrapper/main.cs 2
|
|
209 |
\snippet examples/activeqt/dotnet/wrapper/main.cs 3
|
|
210 |
|
|
211 |
\section1 Walkthrough: .NET/COM Interop with ActiveQt
|
|
212 |
|
|
213 |
Fortunately .NET provides a generic wrapper for COM objects, the
|
|
214 |
\e {Runtime Callable Wrapper} (RCW). This RCW is a proxy for the
|
|
215 |
COM object and is generated by the CLR when a .NET Framework client
|
|
216 |
activates a COM object. This provides a generic way to reuse COM
|
|
217 |
objects in a .NET Framework project.
|
|
218 |
|
|
219 |
Making a QObject class into a COM object is easily achieved with
|
|
220 |
ActiveQt and demonstrated in the QAxServer examples (e.g., the
|
|
221 |
\l{activeqt/simple}{Simple} example). The walkthrough will use
|
|
222 |
the Qt classes implemented in those examples, so the first thing
|
|
223 |
to do is to make sure that those examples have been built
|
|
224 |
correctly, e.g. by opening the
|
|
225 |
\l{qaxserver-demo-multiple.html}{demonstration pages} in Internet
|
|
226 |
Explorer to verify that the controls are functional.
|
|
227 |
|
|
228 |
\section2 Starting a Project
|
|
229 |
|
|
230 |
Start Visual Studio.NET, and create a new C# project for writing a
|
|
231 |
Windows application. This will present you with an empty form in
|
|
232 |
Visual Studio's dialog editor. You should see the toolbox, which
|
|
233 |
presents you with a number of available controls and objects in
|
|
234 |
different categories. If you right-click on the toolbox it allows
|
|
235 |
you to add new tabs. We will add the tab "Qt".
|
|
236 |
|
|
237 |
\section2 Importing Qt Widgets
|
|
238 |
|
|
239 |
The category only has a pointer tool by default, and we have to add
|
|
240 |
the Qt objects we want to use in our form. Right-click on the empty
|
|
241 |
space, and select "Customize". This opens a dialog that has two
|
|
242 |
tabs, "COM Components" and ".NET Framework Components". We used
|
|
243 |
ActiveQt to wrap QWidgets into COM objects, so we select the "COM
|
|
244 |
Components" page, and look for the classes we want to use, e.g.
|
|
245 |
"QPushButton" and "QAxWidget2".
|
|
246 |
|
|
247 |
When we select those widgets and close the dialog the two widgets
|
|
248 |
will now be available from the toolbox as grey squares with their
|
|
249 |
name next to it \footnote Icons could be added by modifying the
|
|
250 |
way the controls register themselves. \endfootnote.
|
|
251 |
|
|
252 |
\section2 Using Qt Widgets
|
|
253 |
|
|
254 |
We can now add an instance of QAxWidget2 and a QPushButton to
|
|
255 |
the form. Visual Studio will automatically generate the RCW for the
|
|
256 |
object servers. The QAxWidget2 instance takes most of the upper
|
|
257 |
part of the form, with the QPushButton in the lower right corner.
|
|
258 |
|
|
259 |
In the property editor of Visual Studio we can modify the properties
|
|
260 |
of our controls - QPushButton exposes the \c QWidget API and has many
|
|
261 |
properties, while QAxWidget2 has only the Visual Studio standard
|
|
262 |
properties in addition to its own property "lineWidth" in the
|
|
263 |
"Miscellaneous" category. The objects are named "axQPushButton1" and
|
|
264 |
"axQAxWidget21", and since especially the last name is a bit
|
|
265 |
confusing we rename the objects to "resetButton" and "circleWidget".
|
|
266 |
|
|
267 |
We can also change the Qt properties, e.g. set the "text" property
|
|
268 |
of the \c resetButton to "Reset", and the "lineWidth" property of the
|
|
269 |
\c circleWidget to 5. We can also put those objects into the layout
|
|
270 |
system that Visual Studio's dialog editor provides, e.g. by setting
|
|
271 |
the anchors of the \c circleWidget to "Left, Top, Right, Bottom", and
|
|
272 |
the anchors of the \c resetButton to "Bottom, Right".
|
|
273 |
|
|
274 |
Now we can compile and start the project, which will open a user
|
|
275 |
interface with our two Qt widgets. If we can resize the dialog,
|
|
276 |
the widgets will resize appropriately.
|
|
277 |
|
|
278 |
\section2 Handling Qt Signals
|
|
279 |
|
|
280 |
We will now implement event handlers for the widgets. Select the
|
|
281 |
\c circleWidget and select the "Events" page in the property
|
|
282 |
editor. The widget exposes events because the QAxWidget2 class has
|
|
283 |
the "StockEvents" attribute set in its class definition. We implement
|
|
284 |
the event handler \c circleClicked for the \c ClickEvent to increase
|
|
285 |
the line width by one for every click:
|
|
286 |
|
|
287 |
\snippet examples/activeqt/dotnet/walkthrough/Form1.cs 0
|
|
288 |
|
|
289 |
In general we can implement a default event handler by double
|
|
290 |
clicking on the widget in the form, but the default events for
|
|
291 |
our widgets are right now not defined.
|
|
292 |
|
|
293 |
We will also implement an event handler for the \c clicked signal
|
|
294 |
emitted by QPushButton. Add the event handler \c resetLineWidth to
|
|
295 |
the \c clicked event, and implement the generated function:
|
|
296 |
|
|
297 |
\snippet examples/activeqt/dotnet/walkthrough/Form1.cs 1
|
|
298 |
|
|
299 |
We reset the property to 1, and also call the \c setFocus() slot
|
|
300 |
to simulate the user style on Windows, where a button grabs focus
|
|
301 |
when you click it (so that you can click it again with the spacebar).
|
|
302 |
|
|
303 |
If we now compile and run the project we can click on the circle
|
|
304 |
widget to increase its line width, and press the reset button to
|
|
305 |
set the line width back to 1.
|
|
306 |
|
|
307 |
\section1 Summary
|
|
308 |
|
|
309 |
Using ActiveQt as a universal interoperability bridge between the
|
|
310 |
.NET world and the native world of Qt is very easy, and makes it
|
|
311 |
often unnecessary to implement a lot of handwritten wrapper classes.
|
|
312 |
Instead, the QAxFactory implementation in the otherwise completely
|
|
313 |
cross-platform Qt project provides the glue that .NET needs to to
|
|
314 |
generate the RCW.
|
|
315 |
|
|
316 |
If this is not sufficient we can implement our own wrapper classes
|
|
317 |
thanks to the C++ extensions provided by Microsoft.
|
|
318 |
|
|
319 |
\section2 Limitations
|
|
320 |
|
|
321 |
All the limitations when using ActiveQt are implied when using this
|
|
322 |
technique to interoperate with .NET, e.g. the datatypes we can use
|
|
323 |
in the APIs can only be those supported by ActiveQt and COM. However,
|
|
324 |
since this includes subclasses of QObject and QWidget we can wrap
|
|
325 |
any of our datatypes into a QObject subclass to make its API
|
|
326 |
available to .NET. This has the positive side effect that the same
|
|
327 |
API is automatically available in
|
|
328 |
\l{http://qt.nokia.com/products/qsa/}{QSA}, the cross platform
|
|
329 |
scripting solution for Qt applications, and to COM clients in general.
|
|
330 |
|
|
331 |
When using the "IJW" method, in priciple the only limitation is the
|
|
332 |
time required to write the wrapper classes and data type conversion
|
|
333 |
functions.
|
|
334 |
|
|
335 |
\section2 Performance Considerations
|
|
336 |
|
|
337 |
Every call from CLR bytecode to native code implies a small
|
|
338 |
performance hit, and necessary type conversions introduce an
|
|
339 |
additional delay with every layer that exists between the two
|
|
340 |
frameworks. Consequently every approach to mix .NET and native
|
|
341 |
code should try to minimize the communication necessary between
|
|
342 |
the different worlds.
|
|
343 |
|
|
344 |
As ActiveQt introduces three layers at once - the RCW, COM and finally
|
|
345 |
ActiveQt itself - the performance penalty when using the generic
|
|
346 |
Qt/ActiveQt/COM/RCW/.NET bridge is larger than when using a
|
|
347 |
hand-crafted IJW-wrapper class. The execution speed however is still
|
|
348 |
sufficient for connecting to and modifying interactive elements in a
|
|
349 |
user interface, and as soon as the benefit of using Qt and C++ to
|
|
350 |
implement and compile performance critical algorithms into native code
|
|
351 |
kicks in, ActiveQt becomes a valid choice for making even non-visual
|
|
352 |
parts of your application accessible to .NET.
|
|
353 |
|
|
354 |
\sa {QtWinForms Solution}
|
|
355 |
*/
|