From d796c9dd933ab96ec83b9a634feedd5d32e1ba3f Mon Sep 17 00:00:00 2001 From: Timothy Pearson Date: Tue, 8 Nov 2011 12:31:36 -0600 Subject: Test conversion to TQt3 from Qt3 8c6fc1f8e35fd264dd01c582ca5e7549b32ab731 --- doc/html/activeqt-dotnet.html | 459 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 459 insertions(+) create mode 100644 doc/html/activeqt-dotnet.html (limited to 'doc/html/activeqt-dotnet.html') diff --git a/doc/html/activeqt-dotnet.html b/doc/html/activeqt-dotnet.html new file mode 100644 index 000000000..a65d0d3c3 --- /dev/null +++ b/doc/html/activeqt-dotnet.html @@ -0,0 +1,459 @@ + + + + + +Walkthrough: Using TQt objects in Microsoft .NET + + + + + + + +
+ +Home + | +All Classes + | +Main Classes + | +Annotated + | +Grouped Classes + | +Functions +

Walkthrough: Using TQt objects in Microsoft .NET

+ + + + + + + + +

Introduction +

+

In the following walkthrough we will show how TQt objects can be used +in a .NET environment, and how .NET objects can be used in a TQt +environment. +

TQt vs .NET +

+

TQt 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 Common Language Runtime (CLR). +

Another key concept is that of 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. (1) +

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 __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 TQt) with managed .NET code. First, the manual way +is presented, which includes using a thin MC++ wrapper class around +the normal TQt/C++ class. Then, the automated way is presented, which +utilizes the ActiveTQt framework as a generic bridge. The advantage of +the first method is that it gives the application developer full +control, while the second method retquires 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 TQPushButton and a +custom TQt widget (TQAxWidget2) run in a .NET GUI application is referred to the example +directory of ActiveTQt. It contains the result of this walkthrough +using both C# and VB.NET, created with Visual Studio.NET (not 2003). +Load examples/dotnet/walkthrough/csharp.csproj, +examples/dotnet/walkthrough/vb.vbproj +(2) +or examples/dotnet/wrapper/wrapper.sln into the IDE and run +the solution. +

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. +

+ +

    // native TQt/C++ class
+    class Worker : public TQObject
+    {
+        Q_OBJECT
+        Q_PROPERTY(TQString statusString READ statusString WRITE setStatusString)
+    public:
+        Worker();
+
+        TQString statusString() const;
+
+    public slots:
+        void setStatusString(const TQString &string);
+
+    signals:
+        void statusStringChanged(const TQString &string);
+
+    private:
+        TQString status;
+    };
+
+

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

+ +

    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;
+    };
+
+

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

Before we can start implementing the wrapper class we need a way to +convert TQt's datatypes (and potentionally your own) into .NET +datatypes, e.g. TQString objects need to be converted into objects +of type 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 (3) 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 pinned pointer, i.e. declare the pointer variable +with the __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 gcroot smartpointer +template type. This lets you create safe pointers to managed +objects. E.g. a variable of type 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. +

+ +

    #include <qstring.h>
+
+    #using <mscorlib.dll>
+    #include <vcclr.h>
+
+    using namespace System;
+
+    String *TQStringToString(const TQString &qstring)
+    {
+        return new String(qstring.ucs2());
+    }
+
+

    TQString StringToTQString(String *string)
+    {
+        wchar_t __pin *chars = PtrToStringChars(string);
+        return TQString::fromUcs2(chars);
+    }
+
+

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

+ +

    #include "networker.h"
+    #include "worker.h"
+    #include "tools.h"
+
+    netWorker::netWorker()
+    {
+        workerObject = new Worker();
+    }
+
+

    netWorker::~netWorker()
+    {
+        delete workerObject;
+    }
+
+

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

    String *netWorker::get_StatusString()
+    {
+        return TQStringToString(workerObject->statusString());
+    }
+
+

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. +

    void netWorker::set_StatusString(String *string)
+    {
+        workerObject->setStatusString(StringToTQString(string));
+        __raise statusStringChanged(string);
+    }
+
+

The property setter calls the native TQt class before firing the +event using the __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. +

+ +

    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();
+                    }
+            }
+    }
+
+

Walkthrough: .NET/COM Interop with ActiveTQt +

+

Fortunately .NET provides a generic wrapper for COM objects, the +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 TQObject class into a COM object is easily achieved with +ActiveTQt and demonstrated in the examples. The walkthrough will use the TQt 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 demonstration pages in Internet +Explorer to verify that the controls are functional. +

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 "TQt". +

Importing TQt Widgets +

+

The category only has a pointer tool by default, and we have to add +the TQt 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 +ActiveTQt to wrap TQWidgets into COM objects, so we select the "COM +Components" page, and look for the classes we want to use, e.g. +"TQPushButton" and "TQAxWidget2". +

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 (4) . +

Using TQt Widgets +

+

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

In the property editor of Visual Studio we can modify the properties +of our controls - TQPushButton exposes the TQWidget API and has many +properties, while TQAxWidget2 has only the Visual Studio standard +properties in addition to its own property "lineWidth" in the +"Miscellaneous" category. The objects are named "axTQPushButton1" and +"axTQAxWidget21", and since especially the last name is a bit +confusing we rename the objects to "resetButton" and "circleWidget". +

We can also change the TQt properties, e.g. set the "text" property +of the resetButton to "Reset", and the "lineWidth" property of the +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 circleWidget to "Left, Top, Right, Bottom", and +the anchors of the resetButton to "Bottom, Right". +

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

Handling TQt Signals +

+

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

+ +

                    private void circleClicked(object sender, System.EventArgs e)
+                    {
+                            this.circleWidget.lineWidth++;
+                    }
+
+

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 clicked signal +emitted by TQPushButton. Add the event handler resetLineWidth to +the clicked event, and implement the generated function: +

                    private void resetLineWidth(object sender, System.EventArgs e)
+                    {
+                            this.circleWidget.lineWidth = 1;
+                            this.resetButton.setFocus();
+                    }
+
+

We reset the property to 1, and also call the 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. +

Summary +

+

Using ActiveTQt as a universal interoperability bridge between the +.NET world and the native world of TQt is very easy, and makes it +often unnecessary to implement a lot of handwritten wrapper classes. +Instead, the TQAxFactory implementation in the otherwise completely +cross-platform TQt 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. +

Limitations +

+

All the limitations when using ActiveTQt 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 ActiveTQt and COM. However, +since this includes subclasses of TQObject and TQWidget we can wrap +any of our datatypes into a TQObject subclass to make its API +available to .NET. This has the positive side effect that the same +API is automatically available in TQSA, the cross platform +scripting solution for TQt applications, and to COM clients in general. +

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

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 ActiveTQt introduces three layers at once - the RCW, COM and finally +ActiveTQt itself - the performance penalty when using the generic +TQt/ActiveTQt/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 TQt and C++ to +implement and compile performance critical algorithms into native code +kicks in, ActiveTQt becomes a valid choice for making even non-visual +parts of your application accessible to .NET. +

+


+
  1. +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. Back...
  2. + +You will notice that in the generated code the following line is +commented out:
    +    ' VB is case insensitive, but our C++ controls are not.
    +    ' Me.resetButton.enabled = True
    +    
    + +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. + Back...
  3. +Indeed, the compiler will in +many cases disallow it. Back...
  4. +Icons could be added by modifying the +way the controls register themselves. Back...
+

See also The TQAxServer Examples. + + +


+ +
Copyright © 2007 +TrolltechTrademarks +
TQt 3.3.8
+
+ -- cgit v1.2.1