/**************************************************************************** ** ** 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 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} */