<!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 TQt 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 TQt objects in Microsoft .NET< / h1 >
<!-- toc -->
< ul >
< li > < a href = "#1" > Introduction
< / a >
< ul >
< li > < a href = "#1-1" > TQt 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 ActiveTQt
< / a >
< ul >
< li > < a href = "#3-1" > Starting a Project
< / a >
< li > < a href = "#3-2" > Importing TQt Widgets
< / a >
< li > < a href = "#3-3" > Using TQt Widgets
< / a >
< li > < a href = "#3-4" > Handling TQt 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 TQt objects can be used
in a .NET environment, and how .NET objects can be used in a TQt
environment.
< p > < h3 > TQt vs .NET
< / h3 >
< a name = "1-1" > < / a > < p > TQt 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 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 < a href = "activentqt.html#ActiveTQt" > ActiveTQt< / 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 = "ntqpushbutton.html" > TQPushButton< / a > and a
custom TQt widget (< a href = "qaxserver-example-multiple.html" > TQAxWidget2< / a > ) 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 < 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 TQt/C++ class
class Worker : public < a href = "ntqobject.html" > TQObject< / a >
{
< a href = "metaobjects.html#TQ_OBJECT" > TQ_OBJECT< / a >
Q_PROPERTY(TQString statusString READ statusString WRITE setStatusString)
public:
Worker();
< a href = "ntqstring.html" > TQString< / a > statusString() const;
public slots:
void setStatusString(const < a href = "ntqstring.html" > TQString< / a > & string);
signals:
void statusStringChanged(const < a href = "ntqstring.html" > TQString< / a > & string);
private:
< a href = "ntqstring.html" > TQString< / a > status;
};
< / pre >
< p > The TQt class has nothing unusual for TQt users, and as even the TQt
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 TQt class.
< p > 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. < a href = "ntqstring.html" > TQString< / 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" > ntqstring.h< / a > >
#using < mscorlib.dll>
#include < vcclr.h>
using namespace System;
String *TQStringToString(const < a href = "ntqstring.html" > TQString< / a > & qstring)
{
< a name = "x2467" > < / a > return new String(qstring.< a href = "ntqstring.html#ucs2" > ucs2< / a > ());
}
< / pre >
< p > < pre > TQString StringToTQString(String *string)
{
wchar_t __pin *chars = PtrToStringChars(string);
return TQString::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 TQt
object wrapped using the C++ operators < tt > new< / tt > and < tt > delete< / tt > .
< p > < pre > String *netWorker::get_StatusString()
{
return TQStringToString(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(StringToTQString(string));
__raise statusStringChanged(string);
}
< / pre >
< p > The property setter calls the native TQt 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 ActiveTQt
< / 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 = "ntqobject.html" > TQObject< / a > class into a COM object is easily achieved with
ActiveTQt and demonstrated in the < a href = "qaxserver-examples.html" > examples< / a > . 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 < 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 "TQt".
< p > < h3 > Importing TQt Widgets
< / h3 >
< a name = "3-2" > < / a > < p > 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".
< 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 TQt Widgets
< / h3 >
< a name = "3-3" > < / a > < p > We can now add an instance of TQAxWidget2 and a < a href = "ntqpushbutton.html" > TQPushButton< / a > 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.
< p > In the property editor of Visual Studio we can modify the properties
of our controls - TQPushButton exposes the < a href = "ntqwidget.html" > TQWidget< / a > 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".
< p > We can also change the TQt 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 TQt widgets. If we can resize the dialog,
the widgets will resize appropriately.
< p > < h3 > Handling TQt 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 TQAxWidget2 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 = "ntqpushbutton.html" > TQPushButton< / 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 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 < a href = "qaxfactory.html" > TQAxFactory< / a > implementation in the otherwise completely
cross-platform TQt 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 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 < a href = "ntqobject.html" > TQObject< / a > and < a href = "ntqwidget.html" > TQWidget< / a > 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 < a href = "http://www.trolltech.com/products/qsa" > TQSA< / a > , the cross platform
scripting solution for TQt 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 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.
< 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 TQAxServer 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 > TQt 3.3.8< / div >
< / table > < / div > < / address > < / body >
< / html >