source: trunk/doc/html/activeqt-dotnet.html@ 203

Last change on this file since 203 was 190, checked in by rudi, 14 years ago

reference documentation added

File size: 20.9 KB
Line 
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
2<!-- /home/espenr/tmp/qt-3.3.8-espenr-2499/qt-x11-free-3.3.8/extensions/activeqt/examples/dotnet/dotnet.doc:1 -->
3<html>
4<head>
5<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
6<title>Walkthrough: Using Qt objects in Microsoft .NET</title>
7<style type="text/css"><!--
8fn { margin-left: 1cm; text-indent: -1cm; }
9a:link { color: #004faf; text-decoration: none }
10a:visited { color: #672967; text-decoration: none }
11body { background: #ffffff; color: black; }
12--></style>
13</head>
14<body>
15
16<table border="0" cellpadding="0" cellspacing="0" width="100%">
17<tr bgcolor="#E5E5E5">
18<td valign=center>
19 <a href="index.html">
20<font color="#004faf">Home</font></a>
21 | <a href="classes.html">
22<font color="#004faf">All&nbsp;Classes</font></a>
23 | <a href="mainclasses.html">
24<font color="#004faf">Main&nbsp;Classes</font></a>
25 | <a href="annotated.html">
26<font color="#004faf">Annotated</font></a>
27 | <a href="groups.html">
28<font color="#004faf">Grouped&nbsp;Classes</font></a>
29 | <a href="functions.html">
30<font color="#004faf">Functions</font></a>
31</td>
32<td align="right" valign="center"><img src="logo32.png" align="right" width="64" height="32" border="0"></td></tr></table><h1 align=center>Walkthrough: Using Qt objects in Microsoft .NET</h1>
33
34
35
36
37<!-- toc -->
38<ul>
39<li><a href="#1"> Introduction
40</a>
41<ul>
42<li><a href="#1-1"> Qt vs .NET
43</a>
44</ul>
45<li><a href="#2"> Walkthrough: .NET interop with MC++ and IJW
46</a>
47<li><a href="#3"> Walkthrough: .NET/COM Interop with ActiveQt
48</a>
49<ul>
50<li><a href="#3-1"> Starting a Project
51</a>
52<li><a href="#3-2"> Importing Qt Widgets
53</a>
54<li><a href="#3-3"> Using Qt Widgets
55</a>
56<li><a href="#3-4"> Handling Qt Signals
57</a>
58</ul>
59<li><a href="#4"> Summary
60</a>
61<ul>
62<li><a href="#4-1"> Limitations
63</a>
64<li><a href="#4-2"> Performance Considerations
65</a>
66</ul>
67</ul>
68<!-- endtoc -->
69
70<p> <h2> Introduction
71</h2>
72<a name="1"></a><p> In the following walkthrough we will show how Qt objects can be used
73in a .NET environment, and how .NET objects can be used in a Qt
74environment.
75<p> <h3> Qt vs .NET
76</h3>
77<a name="1-1"></a><p> Qt is a C++ library and is compiled into traditional, native
78binaries that make full use of the performance provided by the
79runtime environment.
80<p> One of the key concepts of .NET is the idea of "intermediate language
81code" - the source code is compiled into a bytecode format, and at
82runtime, that bytecode is executed in a virtual machine - the <em>Common Language Runtime</em> (CLR).
83<p> Another key concept is that of <em>managed code</em>. This is essentially
84intermediate language code written in such a way that the CLR can take
85care of the memory management, i.e. the CLR will do automatic garbage
86collection, so the application code does not need to explicitly free
87the memory for unused objects.
88<p> The MS compilers for C# and VB.NET will only produce managed
89code. Such programs cannot directly call normal, native functions
90or classes. <a href="#footnote1"><sup>(1)</sup></a><a name="footnote-call1"></a>
91<p> The MS C++ compiler for .NET on the other hand, can produce both
92normal and managed code. To write a C++ class that can be compiled
93into managed code, the developer must flag the class as managed using
94the <tt>__gc</tt> keyword, and restrict the code to only use the subset of
95C++ known as "Managed Extensions for C++", or MC++ for short. The
96advantage is that MC++ code can freely call and use normal C++
97functions and classes. And it also works the other way around: normal
98C++ code can call managed functions and use managed classes (e.g. the
99entire .NET framework class library), including managed functions and
100classes implemented in C# or VB.NET. This feature of mixing managed
101and normal C++ code immensely eases the interoperability with .NET,
102and is by Microsoft referred to as the "It Just Works" (IJW) feature.
103<p> This document demonstrates two different ways of integrating normal
104C++ code (that uses Qt) with managed .NET code. First, the manual way
105is presented, which includes using a thin MC++ wrapper class around
106the normal Qt/C++ class. Then, the automated way is presented, which
107utilizes the <a href="activeqt.html#ActiveQt">ActiveQt</a> framework as a generic bridge. The advantage of
108the first method is that it gives the application developer full
109control, while the second method requires less coding and relieves the
110developer of dealing with the conversion between managed and normal
111data objects.
112<p> The impatient reader, who right away wants to see a <a href="qpushbutton.html">QPushButton</a> and a
113custom Qt widget (<a href="qaxserver-example-multiple.html">QAxWidget2</a>) run in a .NET GUI application is referred to the example
114directory of ActiveQt. It contains the result of this walkthrough
115using both C# and VB.NET, created with Visual Studio.NET (not 2003).
116Load <tt>examples/dotnet/walkthrough/csharp.csproj</tt>,
117<tt>examples/dotnet/walkthrough/vb.vbproj</tt>
118<a href="#footnote2"><sup>(2)</sup></a><a name="footnote-call2"></a>
119or <tt>examples/dotnet/wrapper/wrapper.sln</tt> into the IDE and run
120the solution.
121<p> <h2> Walkthrough: .NET interop with MC++ and IJW
122</h2>
123<a name="2"></a><p> Normal C++ classes and functions can be used from managed .NET code by
124providing thin wrapper classes written in MC++. The wrapper class will
125take care of forwarding the calls to the normal C++ functions or
126methods, and converting parameter data as necessary. Since the wrapper
127class is a managed class, it can be used without further ado in any
128managed .NET application, whether written in C#, VB.NET, MC++ or other
129managed programming language.
130<p>
131
132<pre> // native Qt/C++ class
133 class Worker : public <a href="qobject.html">QObject</a>
134 {
135 <a href="metaobjects.html#Q_OBJECT">Q_OBJECT</a>
136 Q_PROPERTY(QString statusString READ statusString WRITE setStatusString)
137 public:
138 Worker();
139
140 <a href="qstring.html">QString</a> statusString() const;
141
142 public slots:
143 void setStatusString(const <a href="qstring.html">QString</a> &amp;string);
144
145 signals:
146 void statusStringChanged(const <a href="qstring.html">QString</a> &amp;string);
147
148 private:
149 <a href="qstring.html">QString</a> status;
150 };
151</pre>
152<p> The Qt class has nothing unusual for Qt users, and as even the Qt
153specialities like <tt>Q_PROPERTY</tt>, <tt>slots</tt> and <tt>signals</tt> are
154implemented with straight C++ they don't cause any trouble when
155compiling this class with any C++ compiler.
156<p>
157
158<pre> class Worker;
159
160 // .NET class
161 public __gc class netWorker
162 {
163 public:
164 netWorker();
165 ~netWorker();
166
167 __property String *get_StatusString();
168 __property void set_StatusString(String *string);
169
170 __event void statusStringChanged(String *args);
171
172 private:
173 Worker *workerObject;
174 };
175</pre>
176<p> The .NET wrapper class uses keywords that are part of MC++ to indicate
177that the class is managed/garbage collected (<tt>__gc</tt>), and that <tt>StatusString</tt> should be accessible as a property in languages that
178support this concept (<tt>__property</tt>). We also declare an event
179function <tt>statusStringChanged(String*)</tt> (<tt>__event</tt>), the
180equivalent of the respective signal in the Qt class.
181<p> Before we can start implementing the wrapper class we need a way to
182convert Qt's datatypes (and potentionally your own) into .NET
183datatypes, e.g. <a href="qstring.html">QString</a> objects need to be converted into objects
184of type <tt>String*</tt>.
185<p> When operating on managed objects in normal C++ code, a little extra
186care must be taken because of the CLR's garbage collection. A normal
187pointer variable should not <a href="#footnote3"><sup>(3)</sup></a><a name="footnote-call3"></a> be used to refer to a managed
188object. The reason is that the garbage collection can kick in at any
189time and move the object to another place on the heap, leaving you
190with an invalid pointer.
191<p> However, two methods are provided that solves this problem easily. The
192first is to use a <em>pinned</em> pointer, i.e. declare the pointer variable
193with the <tt>__pin</tt> keyword. This guarantees that the object pointed to
194will not be moved by the garbage collector. It is recommended that
195this method not be used to keep a references to managed objects for a
196long time, since it will decrease the efficiency of the garbage
197collector. The second way is to use the <tt>gcroot</tt> smartpointer
198template type. This lets you create safe pointers to managed
199objects. E.g. a variable of type <tt>gcroot&lt;String&gt;</tt> will always point
200to the String object, even if it has been moved by the garbage
201collector, and it can be used just like a normal pointer.
202<p>
203
204<pre> #include &lt;<a href="qstring-h.html">qstring.h</a>&gt;
205
206 #using &lt;mscorlib.dll&gt;
207 #include &lt;vcclr.h&gt;
208
209 using namespace System;
210
211 String *QStringToString(const <a href="qstring.html">QString</a> &amp;qstring)
212 {
213 <a name="x2467"></a> return new String(qstring.<a href="qstring.html#ucs2">ucs2</a>());
214 }
215</pre>
216<p> <pre> QString StringToQString(String *string)
217 {
218 wchar_t __pin *chars = PtrToStringChars(string);
219 return QString::fromUcs2(chars);
220 }
221</pre>
222<p> The convertor functions can then be used in the wrapper class
223implementation to call the functions in the native C++ class.
224<p>
225
226<pre> #include "networker.h"
227 #include "worker.h"
228 #include "tools.h"
229
230 netWorker::netWorker()
231 {
232 workerObject = new Worker();
233 }
234</pre>
235<p> <pre> netWorker::~netWorker()
236 {
237 delete workerObject;
238 }
239</pre>
240<p> The constructor and destructor simply create and destroy the Qt
241object wrapped using the C++ operators <tt>new</tt> and <tt>delete</tt>.
242<p> <pre> String *netWorker::get_StatusString()
243 {
244 return QStringToString(workerObject-&gt;statusString());
245 }
246</pre>
247<p> The netWorker class delegates calls from the .NET code to the native
248code. Although the transition between those two worlds implies a small
249performance hit for each function call, and for the type conversion,
250this should be negligible since we are anyway going to run within the
251CLR.
252<p> <pre> void netWorker::set_StatusString(String *string)
253 {
254 workerObject-&gt;setStatusString(StringToQString(string));
255 __raise statusStringChanged(string);
256 }
257</pre>
258<p> The property setter calls the native Qt class before firing the
259event using the <tt>__raise</tt> keyword.
260<p> This wrapper class can now be used in .NET code, e.g. using C++, C#,
261Visual Basic or any other programming language available for .NET.
262<p>
263
264<pre> using System;
265
266 namespace WrapperApp
267 {
268 class App
269 {
270 void Run()
271 {
272 netWorker worker = new netWorker();
273
274 worker.statusStringChanged += new netWorker.__Delegate_statusStringChanged(onStatusStringChanged);
275
276 System.Console.Out.WriteLine(worker.StatusString);
277
278 System.Console.Out.WriteLine("Working cycle begins...");
279 worker.StatusString = "Working";
280 worker.StatusString = "Lunch Break";
281 worker.StatusString = "Working";
282 worker.StatusString = "Idle";
283 System.Console.Out.WriteLine("Working cycle ends...");
284 }
285
286 private void onStatusStringChanged(string str)
287 {
288 System.Console.Out.WriteLine(str);
289 }
290
291 [STAThread]
292 static void Main(string[] args)
293 {
294 App app = new App();
295 app.Run();
296 }
297 }
298 }
299</pre>
300<p> <h2> Walkthrough: .NET/COM Interop with ActiveQt
301</h2>
302<a name="3"></a><p> Fortunately .NET provides a generic wrapper for COM objects, the
303<em>Runtime Callable Wrapper</em> (RCW). This RCW is a proxy for the
304COM object and is generated by the CLR when a .NET Framework client
305activates a COM object. This provides a generic way to reuse COM
306objects in a .NET Framework project.
307<p> Making a <a href="qobject.html">QObject</a> class into a COM object is easily achieved with
308ActiveQt and demonstrated in the <a href="qaxserver-examples.html">examples</a>. The walkthrough will use the Qt classes implemented
309in those examples, so the first thing to do is to make sure that those
310examples have been built correctly, e.g. by opening the <a href="qaxserver-demo-multiple.html">demonstration pages</a> in Internet
311Explorer to verify that the controls are functional.
312<p> <h3> Starting a Project
313</h3>
314<a name="3-1"></a><p> Start Visual Studio.NET, and create a new C# project for writing a
315Windows application. This will present you with an empty form in
316Visual Studio's dialog editor. You should see the toolbox, which
317presents you with a number of available controls and objects in
318different categories. If you right-click on the toolbox it allows
319you to add new tabs. We will add the tab "Qt".
320<p> <h3> Importing Qt Widgets
321</h3>
322<a name="3-2"></a><p> The category only has a pointer tool by default, and we have to add
323the Qt objects we want to use in our form. Right-click on the empty
324space, and select "Customize". This opens a dialog that has two
325tabs, "COM Components" and ".NET Framework Components". We used
326ActiveQt to wrap QWidgets into COM objects, so we select the "COM
327Components" page, and look for the classes we want to use, e.g.
328"QPushButton" and "QAxWidget2".
329<p> When we select those widgets and close the dialog the two widgets
330will now be available from the toolbox as grey squares with their
331name next to it <a href="#footnote4"><sup>(4)</sup></a><a name="footnote-call4"></a> .
332<p> <h3> Using Qt Widgets
333</h3>
334<a name="3-3"></a><p> We can now add an instance of QAxWidget2 and a <a href="qpushbutton.html">QPushButton</a> to
335the form. Visual Studio will automatically generate the RCW for the
336object servers. The QAxWidget2 instance takes most of the upper
337part of the form, with the QPushButton in the lower right corner.
338<p> In the property editor of Visual Studio we can modify the properties
339of our controls - QPushButton exposes the <a href="qwidget.html">QWidget</a> API and has many
340properties, while QAxWidget2 has only the Visual Studio standard
341properties in addition to its own property "lineWidth" in the
342"Miscellaneous" category. The objects are named "axQPushButton1" and
343"axQAxWidget21", and since especially the last name is a bit
344confusing we rename the objects to "resetButton" and "circleWidget".
345<p> We can also change the Qt properties, e.g. set the "text" property
346of the <tt>resetButton</tt> to "Reset", and the "lineWidth" property of the
347<tt>circleWidget</tt> to 5. We can also put those objects into the layout
348system that Visual Studio's dialog editor provides, e.g. by setting
349the anchors of the <tt>circleWidget</tt> to "Left, Top, Right, Bottom", and
350the anchors of the <tt>resetButton</tt> to "Bottom, Right".
351<p> Now we can compile and start the project, which will open a user
352interface with our two Qt widgets. If we can resize the dialog,
353the widgets will resize appropriately.
354<p> <h3> Handling Qt Signals
355</h3>
356<a name="3-4"></a><p> We will now implement event handlers for the widgets. Select the
357<tt>circleWidget</tt> and select the "Events" page in the property
358editor. The widget exposes events because the QAxWidget2 class has
359the "StockEvents" attribute set in its class definition. We implement
360the event handler <tt>circleClicked</tt> for the <tt>ClickEvent</tt> to increase
361the line width by one for every click:
362<p>
363
364<pre> private void circleClicked(object sender, System.EventArgs e)
365 {
366 this.circleWidget.lineWidth++;
367 }
368</pre>
369<p> In general we can implement a default event handler by double
370clicking on the widget in the form, but the default events for
371our widgets are right now not defined.
372<p> We will also implement an event handler for the <tt>clicked</tt> signal
373emitted by <a href="qpushbutton.html">QPushButton</a>. Add the event handler <tt>resetLineWidth</tt> to
374the <tt>clicked</tt> event, and implement the generated function:
375<p> <pre> private void resetLineWidth(object sender, System.EventArgs e)
376 {
377 this.circleWidget.lineWidth = 1;
378 this.resetButton.setFocus();
379 }
380</pre>
381<p> We reset the property to 1, and also call the <tt>setFocus()</tt> slot
382to simulate the user style on Windows, where a button grabs focus
383when you click it (so that you can click it again with the spacebar).
384<p> If we now compile and run the project we can click on the circle
385widget to increase its line width, and press the reset button to
386set the line width back to 1.
387<p> <h2> Summary
388</h2>
389<a name="4"></a><p> Using ActiveQt as a universal interoperability bridge between the
390.NET world and the native world of Qt is very easy, and makes it
391often unnecessary to implement a lot of handwritten wrapper classes.
392Instead, the <a href="qaxfactory.html">QAxFactory</a> implementation in the otherwise completely
393cross-platform Qt project provides the glue that .NET needs to to
394generate the RCW.
395<p> If this is not sufficient we can implement our own wrapper classes
396thanks to the C++ extensions provided by Microsoft.
397<p> <h3> Limitations
398</h3>
399<a name="4-1"></a><p> All the limitations when using ActiveQt are implied when using this
400technique to interoperate with .NET, e.g. the datatypes we can use
401in the APIs can only be those supported by ActiveQt and COM. However,
402since this includes subclasses of <a href="qobject.html">QObject</a> and <a href="qwidget.html">QWidget</a> we can wrap
403any of our datatypes into a QObject subclass to make its API
404available to .NET. This has the positive side effect that the same
405API is automatically available in <a href="http://www.trolltech.com/products/qsa">QSA</a>, the cross platform
406scripting solution for Qt applications, and to COM clients in general.
407<p> When using the "IJW" method, in priciple the only limitation is the
408time required to write the wrapper classes and data type conversion
409functions.
410<p> <h3> Performance Considerations
411</h3>
412<a name="4-2"></a><p> Every call from CLR bytecode to native code implies a small
413performance hit, and necessary type conversions introduce an
414additional delay with every layer that exists between the two
415frameworks. Consequently every approach to mix .NET and native
416code should try to minimize the communication necessary between
417the different worlds.
418<p> As ActiveQt introduces three layers at once - the RCW, COM and finally
419ActiveQt itself - the performance penalty when using the generic
420Qt/ActiveQt/COM/RCW/.NET bridge is larger than when using a
421hand-crafted IJW-wrapper class. The execution speed however is still
422sufficient for connecting to and modifying interactive elements in a
423user interface, and as soon as the benefit of using Qt and C++ to
424implement and compile performance critical algorithms into native code
425kicks in, ActiveQt becomes a valid choice for making even non-visual
426parts of your application accessible to .NET.
427<p>
428<hr>
429<ol> <li><a name="footnote1"></a>
430The .NET framework provides Platform Invocation
431Services - P/Invoke - that enable managed code to call native C (not
432C++) functions located in DLLs directly. The resulting application
433then becomes partially unmanaged. <a href="#footnote-call1">Back...</a> <li><a name="footnote2"></a>
434
435You will notice that in the generated code the following line is
436commented out: <pre>
437 ' VB is case insensitive, but our C++ controls are not.
438 ' Me.resetButton.enabled = True
439 </pre>
440
441This line is regenerated without comment whenever you change the
442dialog, in which case you have to comment it out again to be able
443to run the project. This is a bug in the original version of
444Visual Studio.NET, and is fixed in the 2003 edition.
445 <a href="#footnote-call2">Back...</a> <li><a name="footnote3"></a>
446Indeed, the compiler will in
447many cases disallow it. <a href="#footnote-call3">Back...</a> <li><a name="footnote4"></a>
448Icons could be added by modifying the
449way the controls register themselves. <a href="#footnote-call4">Back...</a></ol>
450</hr><p>See also <a href="qaxserver-examples.html">The QAxServer Examples</a>.
451
452<!-- eof -->
453<p><address><hr><div align=center>
454<table width=100% cellspacing=0 border=0><tr>
455<td>Copyright &copy; 2007
456<a href="troll.html">Trolltech</a><td align=center><a href="trademarks.html">Trademarks</a>
457<td align=right><div align=right>Qt 3.3.8</div>
458</table></div></address></body>
459</html>
Note: See TracBrowser for help on using the repository browser.