summaryrefslogtreecommitdiffstats
path: root/doc/src/examples/activeqt/dotnet.qdoc
blob: 31ca49096d011f9fcc9e6620d81c9e10af4989cd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** GNU Free Documentation License
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of
** this file.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms
** and conditions contained in a signed written agreement between you
** and Nokia.
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

/*!
    \page activeqt-dotnet.html
    \title Dot Net Example (ActiveQt)

    \brief The Dot Net example demonstrates how Qt objects can be used in a
    .NET environment, and how .NET objects can be used in a Qt
    environment.

    If you need to combine Qt and Win Forms widgets in the same
    application, you might want to use the higher-level
    \l{QtWinForms Solution} instead.

    Contents:

    \tableofcontents

    \section1 Qt vs. .NET

    Qt is a C++ library and is compiled into traditional, native
    binaries that make full use of the performance provided by the
    runtime environment.

    One of the key concepts of .NET is the idea of "intermediate language
    code" - the source code is compiled into a bytecode format, and at
    runtime, that bytecode is executed in a virtual machine - the \e
    {Common Language Runtime} (CLR).

    Another key concept is that of \e {managed code}. This is essentially
    intermediate language code written in such a way that the CLR can take
    care of the memory management, i.e. the CLR will do automatic garbage
    collection, so the application code does not need to explicitly free
    the memory for unused objects.

    The MS compilers for C# and VB.NET will only produce managed
    code. Such programs cannot directly call normal, native functions
    or classes. \footnote The .NET framework provides Platform Invocation
    Services - P/Invoke - that enable managed code to call native C (not
    C++) functions located in DLLs directly. The resulting application
    then becomes partially unmanaged.\endfootnote

    The MS C++ compiler for .NET on the other hand, can produce both
    normal and managed code. To write a C++ class that can be compiled
    into managed code, the developer must flag the class as managed using
    the \c __gc keyword, and restrict the code to only use the subset of
    C++ known as "Managed Extensions for C++", or MC++ for short. The
    advantage is that MC++ code can freely call and use normal C++
    functions and classes. And it also works the other way around: normal
    C++ code can call managed functions and use managed classes (e.g. the
    entire .NET framework class library), including managed functions and
    classes implemented in C# or VB.NET. This feature of mixing managed
    and normal C++ code immensely eases the interoperability with .NET,
    and is by Microsoft referred to as the "It Just Works" (IJW) feature.

    This document demonstrates two different ways of integrating normal
    C++ code (that uses Qt) with managed .NET code. First, the manual way
    is presented, which includes using a thin MC++ wrapper class around
    the normal Qt/C++ class. Then, the automated way is presented, which
    utilizes the ActiveQt framework as a generic bridge. The advantage of
    the first method is that it gives the application developer full
    control, while the second method requires less coding and relieves the
    developer of dealing with the conversion between managed and normal
    data objects.

    The impatient reader, who right away wants to see a QPushButton
    and a custom Qt widget (\l{activeqt/multiple}{QAxWidget2}) run in
    a .NET GUI application is referred to the example directory of
    ActiveQt. It contains the result of this walkthrough using both
    C# and VB.NET, created with Visual Studio .NET (not 2003).
    Load \c {examples/dotnet/walkthrough/csharp.csproj},
    \c {examples/dotnet/walkthrough/vb.vbproj}
    or \c {examples/dotnet/wrapper/wrapper.sln} into the IDE and run
    the solution.

    \bold{Remark:} You will notice that in the generated code the following line is
    commented out:

    \snippet doc/src/snippets/code/doc_src_examples_activeqt_dotnet.qdoc 0

    This line is regenerated without comment whenever you change the
    dialog, in which case you have to comment it out again to be able
    to run the project. This is a bug in the original version of
    Visual Studio.NET, and is fixed in the 2003 edition.

    \section1 Walkthrough: .NET interop with MC++ and IJW

    Normal C++ classes and functions can be used from managed .NET code by
    providing thin wrapper classes written in MC++. The wrapper class will
    take care of forwarding the calls to the normal C++ functions or
    methods, and converting parameter data as necessary. Since the wrapper
    class is a managed class, it can be used without further ado in any
    managed .NET application, whether written in C#, VB.NET, MC++ or other
    managed programming language.

    \snippet examples/activeqt/dotnet/wrapper/lib/worker.h 0

    The Qt class has nothing unusual for Qt users, and as even the Qt
    specialities like \c Q_PROPERTY, \c slots and \c signals are
    implemented with straight C++ they don't cause any trouble when
    compiling this class with any C++ compiler.

    \snippet examples/activeqt/dotnet/wrapper/lib/networker.h 0

    The .NET wrapper class uses keywords that are part of MC++ to indicate
    that the class is managed/garbage collected (\c {__gc}), and that \c
    StatusString should be accessible as a property in languages that
    support this concept (\c {__property}).  We also declare an event
    function \c statusStringChanged(String*) (\c {__event}), the
    equivalent of the respective signal in the Qt class.

    Before we can start implementing the wrapper class we need a way to
    convert Qt's datatypes (and potentionally your own) into .NET
    datatypes, e.g. \c QString objects need to be converted into objects
    of type \c {String*}.

    When operating on managed objects in normal C++ code, a little extra
    care must be taken because of the CLR's garbage collection. A normal
    pointer variable should not \footnote Indeed, the compiler will in
    many cases disallow it. \endfootnote be used to refer to a managed
    object. The reason is that the garbage collection can kick in at any
    time and move the object to another place on the heap, leaving you
    with an invalid pointer.

    However, two methods are provided that solves this problem easily. The
    first is to use a \e pinned pointer, i.e. declare the pointer variable
    with the \c __pin keyword. This guarantees that the object pointed to
    will not be moved by the garbage collector. It is recommended that
    this method not be used to keep a references to managed objects for a
    long time, since it will decrease the efficiency of the garbage
    collector. The second way is to use the \c gcroot smartpointer
    template type. This lets you create safe pointers to managed
    objects. E.g. a variable of type \c gcroot<String> will always point
    to the String object, even if it has been moved by the garbage
    collector, and it can be used just like a normal pointer.

    \snippet examples/activeqt/dotnet/wrapper/lib/tools.cpp 0
    \codeline
    \snippet examples/activeqt/dotnet/wrapper/lib/tools.cpp 1

    The convertor functions can then be used in the wrapper class
    implementation to call the functions in the native C++ class.

    \snippet examples/activeqt/dotnet/wrapper/lib/networker.cpp 0
    \codeline
    \snippet examples/activeqt/dotnet/wrapper/lib/networker.cpp 1

    The constructor and destructor simply create and destroy the Qt
    object wrapped using the C++ operators \c new and \c delete.

    \snippet examples/activeqt/dotnet/wrapper/lib/networker.cpp 2

    The netWorker class delegates calls from the .NET code to the native
    code. Although the transition between those two worlds implies a small
    performance hit for each function call, and for the type conversion,
    this should be negligible since we are anyway going to run within the
    CLR.

    \snippet examples/activeqt/dotnet/wrapper/lib/networker.cpp 3

    The property setter calls the native Qt class before firing the
    event using the \c __raise keyword.

    This wrapper class can now be used in .NET code, e.g. using C++, C#,
    Visual Basic or any other programming language available for .NET.

    \snippet examples/activeqt/dotnet/wrapper/main.cs 0
    \snippet examples/activeqt/dotnet/wrapper/main.cs 1
    \snippet examples/activeqt/dotnet/wrapper/main.cs 2
    \snippet examples/activeqt/dotnet/wrapper/main.cs 3

    \section1 Walkthrough: .NET/COM Interop with ActiveQt

    Fortunately .NET provides a generic wrapper for COM objects, the
    \e {Runtime Callable Wrapper} (RCW). This RCW is a proxy for the
    COM object and is generated by the CLR when a .NET Framework client
    activates a COM object. This provides a generic way to reuse COM
    objects in a .NET Framework project.

    Making a QObject class into a COM object is easily achieved with
    ActiveQt and demonstrated in the QAxServer examples (e.g., the
    \l{activeqt/simple}{Simple} example). The walkthrough will use
    the Qt classes implemented in those examples, so the first thing
    to do is to make sure that those  examples have been built
    correctly, e.g. by opening the
    \l{qaxserver-demo-multiple.html}{demonstration pages} in Internet
    Explorer to verify that the controls are functional.

    \section2 Starting a Project

    Start Visual Studio.NET, and create a new C# project for writing a
    Windows application. This will present you with an empty form in
    Visual Studio's dialog editor. You should see the toolbox, which
    presents you with a number of available controls and objects in
    different categories. If you right-click on the toolbox it allows
    you to add new tabs. We will add the tab "Qt".

    \section2 Importing Qt Widgets

    The category only has a pointer tool by default, and we have to add
    the Qt objects we want to use in our form. Right-click on the empty
    space, and select "Customize". This opens a dialog that has two
    tabs, "COM Components" and ".NET Framework Components". We used
    ActiveQt to wrap QWidgets into COM objects, so we select the "COM
    Components" page, and look for the classes we want to use, e.g.
    "QPushButton" and "QAxWidget2".

    When we select those widgets and close the dialog the two widgets
    will now be available from the toolbox as grey squares with their
    name next to it \footnote Icons could be added by modifying the
    way the controls register themselves. \endfootnote.

    \section2 Using Qt Widgets

    We can now add an instance of QAxWidget2 and a QPushButton to
    the form. Visual Studio will automatically generate the RCW for the
    object servers. The QAxWidget2 instance takes most of the upper
    part of the form, with the QPushButton in the lower right corner.

    In the property editor of Visual Studio we can modify the properties
    of our controls - QPushButton exposes the \c QWidget API and has many
    properties, while QAxWidget2 has only the Visual Studio standard
    properties in addition to its own property "lineWidth" in the
    "Miscellaneous" category. The objects are named "axQPushButton1" and
    "axQAxWidget21", and since especially the last name is a bit
    confusing we rename the objects to "resetButton" and "circleWidget".

    We can also change the Qt properties, e.g. set the "text" property
    of the \c resetButton to "Reset", and the "lineWidth" property of the
    \c circleWidget to 5. We can also put those objects into the layout
    system that Visual Studio's dialog editor provides, e.g. by setting
    the anchors of the \c circleWidget to "Left, Top, Right, Bottom", and
    the anchors of the \c resetButton to "Bottom, Right".

    Now we can compile and start the project, which will open a user
    interface with our two Qt widgets. If we can resize the dialog,
    the widgets will resize appropriately.

    \section2 Handling Qt Signals

    We will now implement event handlers for the widgets. Select the
    \c circleWidget and select the "Events" page in the property
    editor. The widget exposes events because the QAxWidget2 class has
    the "StockEvents" attribute set in its class definition. We implement
    the event handler \c circleClicked for the \c ClickEvent to increase
    the line width by one for every click:

    \snippet examples/activeqt/dotnet/walkthrough/Form1.cs 0

    In general we can implement a default event handler by double
    clicking on the widget in the form, but the default events for
    our widgets are right now not defined.

    We will also implement an event handler for the \c clicked signal
    emitted by QPushButton. Add the event handler \c resetLineWidth to
    the \c clicked event, and implement the generated function:

    \snippet examples/activeqt/dotnet/walkthrough/Form1.cs 1

    We reset the property to 1, and also call the \c setFocus() slot
    to simulate the user style on Windows, where a button grabs focus
    when you click it (so that you can click it again with the spacebar).

    If we now compile and run the project we can click on the circle
    widget to increase its line width, and press the reset button to
    set the line width back to 1.

    \section1 Summary

    Using ActiveQt as a universal interoperability bridge between the
    .NET world and the native world of Qt is very easy, and makes it
    often  unnecessary to implement a lot of handwritten wrapper classes.
    Instead, the QAxFactory implementation in the otherwise completely
    cross-platform Qt project provides the glue that .NET needs to to
    generate the RCW.

    If this is not sufficient we can implement our own wrapper classes
    thanks to the C++ extensions provided by Microsoft.

    \section2 Limitations

    All the limitations when using ActiveQt are implied when using this
    technique to interoperate with .NET, e.g. the datatypes we can use
    in the APIs can only be those supported by ActiveQt and COM. However,
    since this includes subclasses of QObject and QWidget we can wrap
    any of our datatypes into a QObject subclass to make its API
    available to .NET. This has the positive side effect that the same
    API is automatically available in
    \l{http://qt.nokia.com/products/qsa/}{QSA}, the cross platform
    scripting solution for Qt applications, and to COM clients in general.

    When using the "IJW" method, in principle the only limitation is the
    time required to write the wrapper classes and data type conversion
    functions.

    \section2 Performance Considerations

    Every call from CLR bytecode to native code implies a small
    performance hit, and necessary type conversions introduce an
    additional delay with every layer that exists between the two
    frameworks. Consequently every approach to mix .NET and native
    code should try to minimize the communication necessary between
    the different worlds.

    As ActiveQt introduces three layers at once - the RCW, COM and finally
    ActiveQt itself - the performance penalty when using the generic
    Qt/ActiveQt/COM/RCW/.NET bridge is larger than when using a
    hand-crafted IJW-wrapper class. The execution speed however is still
    sufficient for connecting to and modifying interactive elements in a
    user interface, and as soon as the benefit of using Qt and C++ to
    implement and compile performance critical algorithms into native code
    kicks in, ActiveQt becomes a valid choice for making even non-visual
    parts of your application accessible to .NET.

    \sa {QtWinForms Solution}
*/