diff options
Diffstat (limited to 'doc/src/examples/activeqt/dotnet.qdoc')
-rw-r--r-- | doc/src/examples/activeqt/dotnet.qdoc | 355 |
1 files changed, 355 insertions, 0 deletions
diff --git a/doc/src/examples/activeqt/dotnet.qdoc b/doc/src/examples/activeqt/dotnet.qdoc new file mode 100644 index 0000000..afe7034 --- /dev/null +++ b/doc/src/examples/activeqt/dotnet.qdoc @@ -0,0 +1,355 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \page activeqt-dotnet.html + \title Dot Net Example (ActiveQt) + + 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://qtsoftware.com/products/qsa/}{QSA}, the cross platform + scripting solution for Qt applications, and to COM clients in general. + + When using the "IJW" method, in priciple 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} +*/ |