diff options
Diffstat (limited to 'doc/html/activeqt-dotnet.html')
-rw-r--r-- | doc/html/activeqt-dotnet.html | 459 |
1 files changed, 459 insertions, 0 deletions
diff --git a/doc/html/activeqt-dotnet.html b/doc/html/activeqt-dotnet.html new file mode 100644 index 0000000..37a7563 --- /dev/null +++ b/doc/html/activeqt-dotnet.html @@ -0,0 +1,459 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> +<!-- /home/espenr/tmp/qt-3.3.8-espenr-2499/qt-x11-free-3.3.8/extensions/activeqt/examples/dotnet/dotnet.doc:1 --> +<html> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> +<title>Walkthrough: Using Qt objects in Microsoft .NET</title> +<style type="text/css"><!-- +fn { margin-left: 1cm; text-indent: -1cm; } +a:link { color: #004faf; text-decoration: none } +a:visited { color: #672967; text-decoration: none } +body { background: #ffffff; color: black; } +--></style> +</head> +<body> + +<table border="0" cellpadding="0" cellspacing="0" width="100%"> +<tr bgcolor="#E5E5E5"> +<td valign=center> + <a href="index.html"> +<font color="#004faf">Home</font></a> + | <a href="classes.html"> +<font color="#004faf">All Classes</font></a> + | <a href="mainclasses.html"> +<font color="#004faf">Main Classes</font></a> + | <a href="annotated.html"> +<font color="#004faf">Annotated</font></a> + | <a href="groups.html"> +<font color="#004faf">Grouped Classes</font></a> + | <a href="functions.html"> +<font color="#004faf">Functions</font></a> +</td> +<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> + + + + +<!-- toc --> +<ul> +<li><a href="#1"> Introduction +</a> +<ul> +<li><a href="#1-1"> Qt vs .NET +</a> +</ul> +<li><a href="#2"> Walkthrough: .NET interop with MC++ and IJW +</a> +<li><a href="#3"> Walkthrough: .NET/COM Interop with ActiveQt +</a> +<ul> +<li><a href="#3-1"> Starting a Project +</a> +<li><a href="#3-2"> Importing Qt Widgets +</a> +<li><a href="#3-3"> Using Qt Widgets +</a> +<li><a href="#3-4"> Handling Qt Signals +</a> +</ul> +<li><a href="#4"> Summary +</a> +<ul> +<li><a href="#4-1"> Limitations +</a> +<li><a href="#4-2"> Performance Considerations +</a> +</ul> +</ul> +<!-- endtoc --> + +<p> <h2> Introduction +</h2> +<a name="1"></a><p> In the following walkthrough we will show how Qt objects can be used +in a .NET environment, and how .NET objects can be used in a Qt +environment. +<p> <h3> Qt vs .NET +</h3> +<a name="1-1"></a><p> Qt is a C++ library and is compiled into traditional, native +binaries that make full use of the performance provided by the +runtime environment. +<p> 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 <em>Common Language Runtime</em> (CLR). +<p> Another key concept is that of <em>managed code</em>. 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. +<p> The MS compilers for C# and VB.NET will only produce managed +code. Such programs cannot directly call normal, native functions +or classes. <a href="#footnote1"><sup>(1)</sup></a><a name="footnote-call1"></a> +<p> 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 <tt>__gc</tt> 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. +<p> 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 <a href="activeqt.html#ActiveQt">ActiveQt</a> 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. +<p> The impatient reader, who right away wants to see a <a href="qpushbutton.html">QPushButton</a> and a +custom Qt widget (<a href="qaxserver-example-multiple.html">QAxWidget2</a>) 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 <tt>examples/dotnet/walkthrough/csharp.csproj</tt>, +<tt>examples/dotnet/walkthrough/vb.vbproj</tt> +<a href="#footnote2"><sup>(2)</sup></a><a name="footnote-call2"></a> +or <tt>examples/dotnet/wrapper/wrapper.sln</tt> into the IDE and run +the solution. +<p> <h2> Walkthrough: .NET interop with MC++ and IJW +</h2> +<a name="2"></a><p> 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. +<p> + +<pre> // native Qt/C++ class + class Worker : public <a href="qobject.html">QObject</a> + { + <a href="metaobjects.html#Q_OBJECT">Q_OBJECT</a> + Q_PROPERTY(QString statusString READ statusString WRITE setStatusString) + public: + Worker(); + + <a href="qstring.html">QString</a> statusString() const; + + public slots: + void setStatusString(const <a href="qstring.html">QString</a> &string); + + signals: + void statusStringChanged(const <a href="qstring.html">QString</a> &string); + + private: + <a href="qstring.html">QString</a> status; + }; +</pre> +<p> The Qt class has nothing unusual for Qt users, and as even the Qt +specialities like <tt>Q_PROPERTY</tt>, <tt>slots</tt> and <tt>signals</tt> are +implemented with straight C++ they don't cause any trouble when +compiling this class with any C++ compiler. +<p> + +<pre> class Worker; + + // .NET class + public __gc class netWorker + { + public: + netWorker(); + ~netWorker(); + + __property String *get_StatusString(); + __property void set_StatusString(String *string); + + __event void statusStringChanged(String *args); + + private: + Worker *workerObject; + }; +</pre> +<p> The .NET wrapper class uses keywords that are part of MC++ to indicate +that the class is managed/garbage collected (<tt>__gc</tt>), and that <tt>StatusString</tt> should be accessible as a property in languages that +support this concept (<tt>__property</tt>). We also declare an event +function <tt>statusStringChanged(String*)</tt> (<tt>__event</tt>), the +equivalent of the respective signal in the Qt class. +<p> 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. <a href="qstring.html">QString</a> objects need to be converted into objects +of type <tt>String*</tt>. +<p> 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 <a href="#footnote3"><sup>(3)</sup></a><a name="footnote-call3"></a> 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. +<p> However, two methods are provided that solves this problem easily. The +first is to use a <em>pinned</em> pointer, i.e. declare the pointer variable +with the <tt>__pin</tt> 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 <tt>gcroot</tt> smartpointer +template type. This lets you create safe pointers to managed +objects. E.g. a variable of type <tt>gcroot<String></tt> 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. +<p> + +<pre> #include <<a href="qstring-h.html">qstring.h</a>> + + #using <mscorlib.dll> + #include <vcclr.h> + + using namespace System; + + String *QStringToString(const <a href="qstring.html">QString</a> &qstring) + { + <a name="x2467"></a> return new String(qstring.<a href="qstring.html#ucs2">ucs2</a>()); + } +</pre> +<p> <pre> QString StringToQString(String *string) + { + wchar_t __pin *chars = PtrToStringChars(string); + return QString::fromUcs2(chars); + } +</pre> +<p> The convertor functions can then be used in the wrapper class +implementation to call the functions in the native C++ class. +<p> + +<pre> #include "networker.h" + #include "worker.h" + #include "tools.h" + + netWorker::netWorker() + { + workerObject = new Worker(); + } +</pre> +<p> <pre> netWorker::~netWorker() + { + delete workerObject; + } +</pre> +<p> The constructor and destructor simply create and destroy the Qt +object wrapped using the C++ operators <tt>new</tt> and <tt>delete</tt>. +<p> <pre> String *netWorker::get_StatusString() + { + return QStringToString(workerObject->statusString()); + } +</pre> +<p> 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. +<p> <pre> void netWorker::set_StatusString(String *string) + { + workerObject->setStatusString(StringToQString(string)); + __raise statusStringChanged(string); + } +</pre> +<p> The property setter calls the native Qt class before firing the +event using the <tt>__raise</tt> keyword. +<p> 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. +<p> + +<pre> using System; + + namespace WrapperApp + { + class App + { + void Run() + { + netWorker worker = new netWorker(); + + worker.statusStringChanged += new netWorker.__Delegate_statusStringChanged(onStatusStringChanged); + + System.Console.Out.WriteLine(worker.StatusString); + + System.Console.Out.WriteLine("Working cycle begins..."); + worker.StatusString = "Working"; + worker.StatusString = "Lunch Break"; + worker.StatusString = "Working"; + worker.StatusString = "Idle"; + System.Console.Out.WriteLine("Working cycle ends..."); + } + + private void onStatusStringChanged(string str) + { + System.Console.Out.WriteLine(str); + } + + [STAThread] + static void Main(string[] args) + { + App app = new App(); + app.Run(); + } + } + } +</pre> +<p> <h2> Walkthrough: .NET/COM Interop with ActiveQt +</h2> +<a name="3"></a><p> Fortunately .NET provides a generic wrapper for COM objects, the +<em>Runtime Callable Wrapper</em> (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. +<p> Making a <a href="qobject.html">QObject</a> class into a COM object is easily achieved with +ActiveQt and demonstrated in the <a href="qaxserver-examples.html">examples</a>. 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 <a href="qaxserver-demo-multiple.html">demonstration pages</a> in Internet +Explorer to verify that the controls are functional. +<p> <h3> Starting a Project +</h3> +<a name="3-1"></a><p> 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". +<p> <h3> Importing Qt Widgets +</h3> +<a name="3-2"></a><p> 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". +<p> 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 <a href="#footnote4"><sup>(4)</sup></a><a name="footnote-call4"></a> . +<p> <h3> Using Qt Widgets +</h3> +<a name="3-3"></a><p> We can now add an instance of QAxWidget2 and a <a href="qpushbutton.html">QPushButton</a> 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. +<p> In the property editor of Visual Studio we can modify the properties +of our controls - QPushButton exposes the <a href="qwidget.html">QWidget</a> 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". +<p> We can also change the Qt properties, e.g. set the "text" property +of the <tt>resetButton</tt> to "Reset", and the "lineWidth" property of the +<tt>circleWidget</tt> 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 <tt>circleWidget</tt> to "Left, Top, Right, Bottom", and +the anchors of the <tt>resetButton</tt> to "Bottom, Right". +<p> 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. +<p> <h3> Handling Qt Signals +</h3> +<a name="3-4"></a><p> We will now implement event handlers for the widgets. Select the +<tt>circleWidget</tt> 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 <tt>circleClicked</tt> for the <tt>ClickEvent</tt> to increase +the line width by one for every click: +<p> + +<pre> private void circleClicked(object sender, System.EventArgs e) + { + this.circleWidget.lineWidth++; + } +</pre> +<p> 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. +<p> We will also implement an event handler for the <tt>clicked</tt> signal +emitted by <a href="qpushbutton.html">QPushButton</a>. Add the event handler <tt>resetLineWidth</tt> to +the <tt>clicked</tt> event, and implement the generated function: +<p> <pre> private void resetLineWidth(object sender, System.EventArgs e) + { + this.circleWidget.lineWidth = 1; + this.resetButton.setFocus(); + } +</pre> +<p> We reset the property to 1, and also call the <tt>setFocus()</tt> 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). +<p> 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. +<p> <h2> Summary +</h2> +<a name="4"></a><p> 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 <a href="qaxfactory.html">QAxFactory</a> implementation in the otherwise completely +cross-platform Qt project provides the glue that .NET needs to to +generate the RCW. +<p> If this is not sufficient we can implement our own wrapper classes +thanks to the C++ extensions provided by Microsoft. +<p> <h3> Limitations +</h3> +<a name="4-1"></a><p> 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 <a href="qobject.html">QObject</a> and <a href="qwidget.html">QWidget</a> 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 <a href="http://www.trolltech.com/products/qsa">QSA</a>, the cross platform +scripting solution for Qt applications, and to COM clients in general. +<p> When using the "IJW" method, in priciple the only limitation is the +time required to write the wrapper classes and data type conversion +functions. +<p> <h3> Performance Considerations +</h3> +<a name="4-2"></a><p> 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. +<p> 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. +<p> +<hr> +<ol> <li><a name="footnote1"></a> +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. <a href="#footnote-call1">Back...</a> <li><a name="footnote2"></a> + +You will notice that in the generated code the following line is +commented out: <pre> + ' VB is case insensitive, but our C++ controls are not. + ' Me.resetButton.enabled = True + </pre> + +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. + <a href="#footnote-call2">Back...</a> <li><a name="footnote3"></a> +Indeed, the compiler will in +many cases disallow it. <a href="#footnote-call3">Back...</a> <li><a name="footnote4"></a> +Icons could be added by modifying the +way the controls register themselves. <a href="#footnote-call4">Back...</a></ol> +</hr><p>See also <a href="qaxserver-examples.html">The QAxServer Examples</a>. + +<!-- eof --> +<p><address><hr><div align=center> +<table width=100% cellspacing=0 border=0><tr> +<td>Copyright © 2007 +<a href="troll.html">Trolltech</a><td align=center><a href="trademarks.html">Trademarks</a> +<td align=right><div align=right>Qt 3.3.8</div> +</table></div></address></body> +</html> |