Articles   Members Online:
-Article/Tip Search
-News Group Search over 21 Million news group articles.
Member Area
-Account Center
-Top 10 NEW!!
-Submit Article/Tip
-Forums Upgraded!!
-My Articles
-Edit Information
-Become a Member
-Why sign up!
-Chat Online!
-Indexes NEW!!
-Build your resume
-Find a job
-Post a job
-Resume Search
-Link to us
Visit Embarcadero
Embarcadero Community
Tips for Converting VCL Components to VCL.NET Turn on/off line numbers in source code. Switch to Orginial background IDE or DSP color Comment or reply to this aritlce/tip for discussion. Bookmark this article to my favorite article(s). Print this article
Delphi 7.x
User Rating
No Votes
# Votes
DSP, Administrator
Reference URL:
			Author: Yorai Aminov 

Tips for Converting VCL Components to VCL.NET


Delphi 8 (actually, Borland® Delphi™ 8 for the 
Microsoft® .NET Framework) introduces Delphi developers to the .NET world (and .NET 
developers to Delphi). Delphi 8 allows developers to create native .NET 
applications using any of the .NET framework's classes, including the standard 
Windows Forms user interface controls and designers. It also provides a migration 
path for existing applications and components using the new VCL.NET framework. 
Using VCL.NET, many existing Delphi applications will port to .NET with little or 
no modifications.

The story is a little different for components. Components tend to be "closer to 
the metal", invoking system calls, managing memory and object lifetime, in 
generally doing things we want to hide from normal application code. Component need 
to be aware of the platform's architecture, and may require substantial work to 
function properly or even compile in the new version.

In this article I'll show some of the conversion issues I've encountered while 
working on a VCL.NET version of some of my components. Some may be obvious, while 
other are more subtle and can be the source of hard-to-find bugs.

Objects and Pointers

Let's start with the big one: managed 
code doesn't 
support pointers. Pointers allow unsupervised memory access, and are therefore 
considered unsafe. A lot of Delphi components rely heavily on pointers, and make 
implicit or explicit assumptions about their content and structure. This has quite 
a few implications for the conversion process.

Use TObject instead of Pointer

In Delphi for Win32 (and Kylix, for that matter), object references are simply 
pointers. When declaring an object-type variable, you're actually declaring a 
pointer variable. You can typecast your objects to pointers and vice-versa. Because 
of this, pointer variables and properties are widely used to store object 
references. Since unmanaged code no longer supports pointers, the first step in 
converting a component to VCL.NET is to search your code for pointers and change 
them to something else.

In most cases, you can safely replace pointers with object references. Remember 
that in .NET, everything is an object. However, there may be cases where you may 
need to refactor your code.

No more Integer typecasts

In 32-bit unmanaged code, a pointer is simply a 32-bit value. This means the 
Integer, Pointer, and TObject types are all interchangeable. Components often use 
this type compatibility, especially when using external code that expects a 
specific type. For example, consider the Win32 EnumWindows function:

function EnumWindows(lpEnumFunc: TFNWndEnumProc; lParam: LPARAM): BOOL; stdcall;
The function expects two parameters: a callback function and a second parameter, of 
type LPARAM, that will be passed back as a parameters to the callback function. 
LPARAM is defined as:

  LPARAM = Longint;

A common usage of the lParam parameter is to hold an object reference. The callback 
function is written as a stub invoking a method of that object. Such code will have 
to be rewritten or marked as unsafe in Delphi 8.

The case of TList

A special case for integer typecasting is the TList class, one of the most widely 
used classes in the VCL. TList is a generic container. In Win32, TList holds 
pointers, since they can be easily typecast to both integers and objects. In Delphi 
8, TList holds TObject references.

PChars are Gone

Like all pointers, PChars are no longer supported. Code that used character 
buffers, called Win32 API functions requiring strings, or passed character strings 
to external DLLs will no longer work.

API functions the required PChars in Win32 now accept standard Delphi strings. 
Functions that returned data into character buffers now accept StringBuilder 

Memory Allocation

Since pointers are gone, code that allocates and deallocates memory for records and 
buffers is no longer valid. Search for calls to GetMem, FreeMem, New, and Dispose - 
they should all go away.
Message Handlers

One type of code that is heavily used in components but rarely in applications is 
message handling. Visual controls handle Windows messages and internal VCL messages 
using message functions. VCL.NET allows you to use your existing message handling 
code, but introduces some quirks.

Message Types

Message handlers are invoked by TObject's Dispatch method. Visual controls call 
Dispatch in their WndProc method, passing a TMessage record as Dispatch's only 
parameters. The Dispatch method can accept any parameter type, but assumes the 
first two bytes of the referenced parameter contain the message ID.

In unmanaged Delphi code, the type of the message parameter doesn't really matter. 
Since WndProc calls Dispatch with a TMessage parameter, as long as you're using a 
compatible record you'll be fine. Not so in Delphi 8. Consider the following code: 

1   type
2     TForm1 = class(TForm)
3     private
4       procedure WMNCPaint(var message: TMessage); message WM_NCPAINT;
5     end;
7     {...}
9   procedure TForm1.WMNCPaint(var message: TMessage);
10  begin
11    inherited;
12  end;

This code compiles just fine on any version of Delphi. When compiled to a Win32 
application, the code runs just fine. It doesn't really do anything with the 
WM_NCPAINT message, so nothing should go wrong. In Delphi 8, however, running the 
application produces a System.NullReferenceException exception. Since the exception 
is raised deep in Delphi's RTL, it is almost impossible to debug. The solution is 
deceptively simple, though:

13  type
14    TForm1 = class(TForm)
15    private
16      procedure WMNCPaint(var message: TWMNCPaint); message WM_NCPAINT;
17    end;
19    {...}
21  procedure TForm1.WMNCPaint(var message: TWMNCPaint);
22  begin
23    inherited;
24  end;

This code works in any version of Delphi, including Delphi 8. All we had to do is 
change the message record type to TWMNCPaint. A little annoying, but fairly easy - 
once you know about it.

A bit more annoying is the fact that this behavior doesn't affect every message, 
just some of them. A message handle for WM_NCACTIVATE, for example, is perfectly 
happy accepting a TMessage record, or in fact any other message record, such as 
TWMNCPaint. Try it.

Once again, the answer is obvious once you already know it: WM_NCPAINT is already 
handled in one of TForm's (or any other visual control's) ancestors - TWinControl, 
where it expects a TWMNCPaint parameter. If your component handles a message that 
is also handled by an ancestor, and calls the inherited handle, you must use the 
same message record type. 

Object References

Windows messages contain data as two integers, historically names wParam and 
lParam. These are often used as pointers to more complex data structures. Since 
normal .NET applications don't use pointers, VCL.NET has to perform some 
behind-the-scenes magic to convert references to integers and manage their 
lifetime. One side-effect of this magic is that the information passed by certain 
messages requires additional handling. Let's take a look at the CM_HINTSHOW 
message, send by the VCL before displaying a hint:

25  type
26    TForm1 = class(TForm)
27    private
28      procedure CMHintShow(var message: TCMHintShow); message CM_HINTSHOW;
29    end;
31    {...}
33  procedure TForm1.CMHintShow(var message: TCMHintShow);
34  begin
35    inherited
36      message.HintInfo.HintStr := 'This is my hint';
37  end;

This code, which works great in Win32, doesn't even compile in Delphi 8. In Delphi 
8, TCMHintShow is an object, and HintInfo is a property of that object. The setter 
method for the HintInfo property can only take an existing HintInfo reference, so 
you can't simply assign values to HintInfo's members. You have to use a separate 
reference variable:

38  procedure TForm1.CMHintShow(var message: TCMHintShow);
39  var
40    HintInfo: THintInfo;
41  begin
42    inherited;
43    HintInfo := message.HintInfo;
44    HintInfo.HintStr := 'This is my hint';
45  end;

This code compiles, but still doesn't work. A little more tweaking, and we get:

46  procedure TForm1.CMHintShow(var message: TCMHintShow);
47  var
48    HintInfo: THintInfo;
49  begin
50    inherited;
51    HintInfo := message.HintInfo;
52    HintInfo.HintStr := 'This is my hint';
53    message.HintInfo := HintInfo;
54  end;

We need to explicitly set the HintInfo property to copy the modified data to a 
record VCL.NET can pass around using messages. This is what the last line does.
FCL Types

The .NET Framework Class Library (FCL) contains many types that are similar to 
Delphi's standard types. In Delphi for .NET, standard types are mapped to their 
.NET equivalents. For example, the string type is mapped to the FCL's System.String 
class (although Delphi extends string handling to support the language syntax), and 
the Integer type is mapped to System.Int32. Other types, such as TDateTime, have 
been reimplemented for .NET.


In Delphi for Win32 (and Kylix), TDateTime is defined as a Double (64-bit 
floating-point number). Date and time information is stored as the count of days 
since midnight on 30-Dec-1899.

In Delphi 8, TDateTime is a record, declared in the Borland.Delphi.System unit. 
TDateTime handles implicit conversions to and from Double values, so existing code 
that assumes TDateTime is a Double value compiles without warnings. Implicit 
conversions to System.DateTime and standard operators are also implemented, so 
Delphi's TDateTime type is fully accessible from other .NET languages. Internally, 
TDateTime stores its value using the System.DateTime type.

The problem is that System.DateTime was designed to hold dates and times, while 
Double (the original TDateTime) was designed to hold floating-point numbers. And 
sometimes, Delphi coders relied on this small, but critical, implementation detail. 
Since TDateTime was a double, it was easy to perform arithmetic operations on it - 
for example, subtracting one TDateTime from another to get the difference between 
them in days. Occasionally, the difference was negative (the first date was later 
then the second date), and if your code allows that, it will no longer work. 
Instead, you'll get the absolute value of the difference.

To work around this, you'll have to use actual Double variables instead of 
TDateTimes. You can use TDateTime's ToOADate method to get a value compatible with 
previous versions of Delphi.

Delphi 8 for .NET is a great product, letting Delphi developers move into the .NET 
world while using their existing skills and re-using their existing code. Still, 
.NET and Win32 are two different platforms, and the transition requires extra 
caution when trying to use skills and processes which were valid in the past.

Borland has made porting code to VCL.NET a fairly smooth process, but one cannot 
expect 100% portability between the platforms. I have shown some of the issues I've 
encountered. I'm sure there are more.

In addition to this article, be sure to read the Delphi 8 help topic, "Language 
Issues in Porting VCL Applications to Delphi 8 for .NET". It's a little hard to 
find (it doesn't seem to be in the contents or the index). If you have Delphi 8 
installed, you can find it at 

Vote: How useful do you find this Article/Tip?
Bad Excellent
1 2 3 4 5 6 7 8 9 10


Share this page
Download from Google

Copyright © Mendozi Enterprises LLC