Delphi Clinic C++Builder Gate Training & Consultancy Delphi Notes Weblog Dr.Bob's Webshop
Dr.Bob's Delphi Notes Dr.Bob's Delphi Clinics
 Porting Delphi 1.x code to 32-bits
See Also: Delphi Papers and Columns

This paper combines a number of previous articles about porting Delphi 1.x code to 32-bits. When I write about Delphi-32, I mean any 32-bits version of Delphi: Delphi-2.x or higher!

Delphi-32 defines several new data types that exploit the features available by Windows 95. Delphi-32 has also changed a few data types to take advantage of the 32-bit environment. New Data Types include character type, character pointer types, string type, variant type and the currency type.

Character Type
Delphi-32 introduces new wide character types to support Unicode. Delphi 1.x treated characters as 8-bit values of type Char.
These are the standard types that represent characters in Delphi-32.

Character-pointer types:
Pointer typeCharacter type
PANSICharANSIChar
PWideCharWideChar
PCharChar

The semantics of all the character-pointer types are identical. The only thing that varies is the size of the character pointed to.

String Type
Delphi-32 supports strings of nearly unlimited length in addition to the 255-character counted strings previously supported. A new compiler directive, $H, controls whether the reserved word "string" represents a short string or the new, long string. The default state of $H, is $H+, using long strings by default. All Delphi-32 components use the new long string type.
These are the new string types.

Although most string code works interchangeably between short strings and long strings, there are certain short-string operations that either won't work on long strings at all or which operate more efficiently when done a different way. The following table summarizes these changes.

Short String operationLong string equivalentExplanation
PString typestringAll long strings are dynamically allocated, so PString is redundant and requires more bookkeeping.
S[0] := LSetLength(S,L) SetString(S,P,L)Because long strings are dynamically allocated, you must call the SetLength procedure to allocate the appropriate amount of memory.
StrPCopy(Buffer, S)StrPCopy(Buffer, PChar(S))You can typecast long strings into null-terminated strings. The address of the long string is the address of its first character, and the long string is followed by a null.
S := StrPas(P)S := PLong strings can automatically copy from null-terminated strings.

Long strings cannot be passed to OpenString-type parameters or var short-string parameters.

Variant Type
Delphi-32 introduces variant types to give you the flexibility to dynamically change the type of a variable. This is useful when implementing OLE automation or certain kinds of database operations where the parameter types on the server are unknown to your Delphi-built client application.
A variant type is a 16-byte structure that has type information embedded in it along with its value, which can represent a string, integer, or floating-point value. The compiler recognizes the standard type identifier Variant as the declaration of a variant.
In cases where the type of a variant is incompatible with the type needed to complete an operation, the variant will automatically promote its value to a compatible value, if possible. For instance, if a variant contains an integer and you assign it to a string, the variant converts its value into the string representing the integer number, which is then assigned to the string.
You can also assign a variant expression to a variable of a standard type or pass the variant as a parameter to a routine that expects a standard type as a parameter. Delphi coerces the variant value into a compatible type if necessary, and raises an exception if it cannot create a compatible value.

Currency Type
Delphi-32 defines a new type called Currency, which is a floating-point type specifically designed to handle large values with great precision. Currency is assignment-compatible with all other floating-point types (and variant types), but is actually stored in a 64-bit integer value much like the Comp type.
Currency-type values have a four-decimal-place precision. That is, the floating-point value is stored in the integer format with the four least significant digits implicitly representing four decimal places.


Changed Data Types
The implementation-dependent types Integer and Cardinal are 32-bit values in Delphi-32, where they were 16-bit values in Delphi 1.x. To explicitly declare 16-bit integer data types, use the SmallInt and Word types.


Additional Syntax
You can include an optional finalization section in a unit. Finalization is the counterpart of initialization, and takes place when the application shuts down. You can think of the finalization section as "exit code" for a unit. The finalization section corresponds to calls to ExitProc and AddExitProc in Delphi 1.x.
The finalization begins with the reserved word finalization. The finalization section must appear after the initialization section, but before the final end. statement.
Once execution enters an initialization section of a unit, the corresponding finalization section is guaranteed to execute when the application shuts down. Finalization sections must handle partially-initialized data properly, just as class destructors must.
Finalization sections execute in the opposite order that units were initialized. For example, if your application initializes units A, B, and C, in that order, it will finalize them in the order C, B, and A.
The outline for a unit therefore looks like this:

  unit UnitName;
  interface
  { uses clause; optional }
  ...
  implementation
  { uses clause; optional }
  ...
  initialization { optional }
  ...
  finalization { optional }
  ...
  end.

Porting (1) - Integer Types
This article will be the first of a regular series of tips, tricks and hints to help ease the pain of moving, porting and upgrading Delphi 1.x applications to Win32-land. First published in the UK-BUG Delphi NewsLetter.

First of all, Borland claims that a normal 16-bit project will port to 32-bits with but a single recompile. Well, let's see if we can define normal here...

Normal means we can't use 16-bit VBX controls. For every 16-bit VBX control, we have to find a 32-bit OCX counterpart (or just replace it with native VCL code from somewhere else). Now you know why I prefer to use the LightLib VCL over the ChartFX VBX controls. The same holds for general DLLs, of course: for any 'foreign' DLL that you rapplication is using, you now need to obtain the 32-bit version of this DLL. And you can expect that in the first few weeks after the next version of Delphi ships, only the standard Delphi add-on DLLs that are supplied by Borland (such as the 32-bit BDE) will be available.

Ok, so we must be careful for external files. That's not so bad, is it? but even for a fully native Delphi program, there are things to watch out for when porting to 32-bit. Like 16- or 32-bit data types, for example. The 16-bit Borland Pascal had the following fundamental integer types: byte, word, shortint, integer and longint. Delphi 1.x also introduced the smallint, which is in 16-bit world the same as integer, and cardinal, which is in 16-bit world the same as word. In the next release of Delphi (the 32-bit version 2.0), both integer and cardinal will be 'upgraded' to 32-bits. We could state that integer and cardinal are the 'native' machine words (resp. signed and unsigned).
In a table, the complete overview is as follows:

Borland PascalDelphi 1.xDelphi-32
ShortInt-128..127ShortInt-128..127ShortInt-128..127
SmallInt-32768..32767SmallInt-32768..32767
LongInt-2G..2G-1LongInt-2G..2G-1LongInt-2G..2G-1
Byte0..255Byte0..255Byte0..255
Word0..65535Word0..65535Word0..65535
Integer-32768..32767Integer-32768..32767Integer-2G..2G-1
Cardinal0..65535Cardinal0..2G-1

So, if we want to write portable code between Borland Pascal and the 16- and 32-bit versions of Delphi, we are to be careful with the native integer and cardinal types (especially when these are read from files).

Note that the 32-bit cardinal type is in fact 31-bits, since it would take a 33-bit type to be able to parse both 32-bit integer and 32-bit cardinal values. If you wonder about that, consider this: in the 16-bit world, we had 16-bit unsigned words, ranging from 0 to 65535, and 16-bit signed integers, ranging from -32768 to 32767. In order for the compiler to be able to parse values from the combined range of -32768 to 65535, it actually needed at least 17-bit integer type (17-bit or more, that is). That's why even the old Turbo Pascal already had the LongInt type; a 32-bit signed long integer type, capable of parsing values from the 16-bit Word and Integer ranges (and the LongInt range itself of course). The availability of the LongInt type to the end-users of Turbo Pascal was just an added bonus; Borland could have kept it to itself, but they obviously decided to let us share this first 32-bit type! Now, back in the 32-bit world, the 32-bit version of Delphi still only has a 32-bit signed long integer as 'biggest' integer type; there is no such thing as a 64-bit integer (or at least a 33-bit integer). So, there is no way the compiler can parse a value from a combined -2G..4G-1 range (the combined range from a 32-bit signed integer and 32-bit unsigned word). The only solution was to drop the 32-bit unsigned word range to 31-bits, or create a 33-bit type integer of their own. Since the last solution would cost too much (also in performance!) it was obviously decided to just drop the range of the 32-bit Cardinal to 31-bit, and hence the range of 0..2G-1 for the Cardinal type.


Porting (2) - Long Strings
As a long-term Turbo Pascal user, I've known Strings to be limited to 255 characters only, with the 0th 'character' being the actual length-Byte (range 0..255). This lead to dirty-but-fast code such as the following to trim the spaces on the right side of the String:

  procedure RightTrim16(var Str: String);
  var Len: Byte absolute Str; { Len is overlaid with the 0th length byte of Str }
  begin
    while Str[Len] = ' ' do Dec(Len)
  end {RightTrim16};
This code still works for the 16-bit version of Delphi. But with the release of Delphi 2.0, Borland has provided us with a new implementation of Strings: Long Strings, where all we've known and hacked before has become illegal.

A Long String is just what is says: a long String, of up to 2Gb characters in size (in practice a long String is limited by the amount of available memory on your machine). Since we now need a LongInt to store the size of the long String, the 0th character (or byte) is no longer sufficient to indicate the Length of the string. And hence, all legacy code that reads, writes and uses this 0th byte has become a severe cause for Access Violations. Using the old memory layout, we could state that the Length LongInt (4 bytes) now starts at offset -3. But that's not all. A long String also has a Reference Count LongInt at position -7, and a Allocation Size LongInt at position -11. Long String constants do not have the allocation size LongInt, but only the first two.

Length
Now that we know we cannot use the 0th Byte of a string as length indicator, how can we remove the trailing spaces from a long String? Well, we can still use the Length function to get the length of a String. We only need to realise that it's a 32-bit integer now. To remove trailing spaces from a (long) String, we now need to walk back from the end of the long String and count the number of spaces that are at the end. After having counted them, we can just Delete them from the String (you don't want to Delete the spaces one at a time, since this could involve copying the long String for every Delete operation, something you'd like to avoid as much as possible for performance's sake).

  procedure RightTrim32(var Str: String);
  var SPos,SLen: Integer;
  begin
    Spos := Length(Str);
    if Str[SPos] = ' ' then { are there trailing spaces to begin with? }
    begin
      SLen := 0;
      while (SPos > 0) and (Str[SPos] = ' ') do
      begin
        Inc(SLen);
        Dec(SPos)
      end;
      Delete(Str,SPos+1,SLen);
    { SetLength(Str,SPos); }
    end
  end {RightTrim32};
Note the commented call to SetLength. SetLength is used to set the length of a (long) String. Note that the length is something different than the allocated size, so the length of a String should always be shorter or equal to the allocated size. Fortunately, SetLength also makes sure that enough memory is allocated for the long String including a terminating #0 character (to ensure compatibility with PChars - read the ObjectPascal Language Guide page 22 for more about this). For short strings, SetLength only sets the length byte, and the length should be between 0 and 255 characters. So, if you want to prepare your 16-bits Delphi code for the use of SetLength, you can add the following code to your projects right now:
  {$IFNDEF WIN32} { to make sure it doesn't get added into Delphi-32 code }
  procedure SetLength(var Str: String; Len: Integer);
  begin
    Str[0] := Chr(Len);
  end {SetLength};
  {$ENDIF}
Actually, we might be able to get away with using Delete in our RightTrim routine, by using SetLength instead (just set the length of the string to the new value, like the previous 16-bits hack). This leads to the following 32-bit RightTrim procedure:
  procedure RightTrim16and32(var Str: String);
  var SPos: Integer;
  begin
    Spos := Length(Str);
    if Str[SPos] = ' ' then { are there trailing spaces to begin with? }
    begin
      while (SPos > 0) and (Str[SPos] = ' ') do Dec(SPos);
      SetLength(Str,SPos);
    end
  end {RightTrim16and32};
This version suddenly looks a lot like the 16-bit fast version. And why shouldn't it? After all, we still use the same algorithm. If we compare this procedure with the TrimRight function Borland provides, we'll see that they use a similar algorithm, but one that copies the resulting string.
  function TrimRight(const S: string): string;
  { TrimRight trims trailing spaces and control characters from the given
    string. }
  var I: Integer;
  begin
    I := Length(S);
    while (I > 0) and (S[I] <= ' ') do Dec(I);
    Result := Copy(S, 1, I);
  end;
Copying a long String would seem to take longer than setting the length of one, right? Well, this is a bit of a paradox actually, and that's where Reference Counters come in...

Reference Count
When a long String is copied, the reference count of the original is incremented and only a pointer is copied. This takes very little time, which explains why even copying long Strings of several megabytes long takes no time at all. Of course, once you change the copy (assign something new to the 12,345,678th element of a 20Mb long String) then the actual memory for a new long String must be allocated to hold the 20Mb long sting copy with the new 12,345,678th element. This is referred to as 'copy-on-write', but I would rather call it a 'delayed-performance-hit', since that's what your users will be experiencing. There are ways around it, such as using the UniqueString function, but that is not really a portability issue anymore...

To make a Long story Short...
Finally something to take care of when writing or calling procedures that have var parameters of type string, and local variables. I sometimes get reports of people having Access Violations after using the copy procedure to assign data to one of the var parameters. It seems that Delphi thinks these parameters are huge strings even if you set this option to off ($H- or the Compiler | Options box).
This leads to my final recommendation for this month: except when you're writing general string routines, always explicitly state whether you're using a long or a short string. Don't depend on the {$H} compiler directive (which works on a unit basis), but use the ShortString and AnsiString predefined types instead. The compiler may use a String as a Long String while you think it's a Short String or vice versa, but when you use ShortString or AnsiString types this will never happen.


Porting (3) - Components
Components come in many shapes and sizes. In source or .DCU form, with or without supporting DLLs. Not every one of them will port to Delphi-32 easily. For example, if we only have the 16-bit compiled .DCU file for a Delphi 1.x component, we'll get the following fatal error when we try to add it to the 32-bit component palette of Delphi-32:

Now, why would Delphi-32 need the source file when the compiled version is already available? Well, first of all, the .DCU file format is subject to change with every release of Delphi. For previous Borland Pascal programmers this may not come as a surprise, since the .TPU file format changed also with every major release of Turbo/Borland Pascal. But the major reason is of course that Delphi-32 needs 32-bit code to be generated, and this includes the .DCU file. Which leads to our first observation: without source code, you cannot add your Delphi 1.x components to Delphi-32's Component Palette.
Now, suppose we have the source code, and install the component. We also need to check if it isn't needing a VBX or DLL. VBXs won't be supported at all by Delphi-32 (and are replaced by OCXs), and for DLLs we can basically use the same rule as above: an existing 16-bits DLL cannot be used by Delphi-32 (unless we go through a complex and painful process of thunking down to the 16-bit DLL, a topic we won't discuss here).
For component users, it all boils down to one thing: the component builder must have made special precautions to make the component work in Delphi-32. For component builders; read the remainder of this column for an insight in problems we can expect...

Component Builders
First of all, Component Builders need to ask themselves a good question: are we willing to answer support calls by users trying to install the wrong version of our .DCU files, or do we just distribute the source code so Delphi (1 and 2) can re-compile this source while installing the components. The later solution might decrease the support our component users need. Besides, the changing .DCU file format may be a major reason NOT to use any component that doesn't come in source form (note that the source doesn't have to be part of the demo, just make it available if/when people buy or register the package).
Distributing the source code of a component leads to another recommendation: always (try to) make your source code compile with both Delphi 1.x and Delphi-32. This will enable our component users to just install the same source file with either or both Delphi 1.x and 2. Without problems of using the wrong version in the wrong place. It will also ensure to our component users that the same set of features will be available for the 16-bit and the 32-bit version. Orpheus version 2 (by TurboPower) is a good example of this; the new version can be used with both Delphi 1.x and 2, and yet uses a single source code set.
Of course, it is highly unlikely that a single set of source code can compile in Delphi 1.x and 2 as-is. At some places we may need to distinguish between the compiler versions, and to do that we can use the predefined compiler directive WIN32.
A first place where we can use this is the place where we include the .RES file that contains the component bitmap for the component palette. This file is normally distributed as the .DCR file, but since the 16-bit file format changed when moving to 32-bit, we can no longer distribute a single .DCR file. And since we want to use a single source code set for both versions of Delphi, we have to think of another way. A solution is to rename the .DCR file (so the compiler won't find it automatically) and include the 16-bit or 32-bit version of the renamed .DCR file depending of the compiler version. A good way of dealing with all these problems is the following: we should rename the 16-bit version to .R16 and the 32-bit version to .R32, and use the following code to include either resource file:

{$IFDEF WIN32}
  {$R COMPONENT.R32}
{$ELSE}
  {$R COMPONENT.R16}
{$ENDIF}
The same idea can be used for source code itself. For example, in Delphi 1.x we have to use the SetText method to add more than one line at a time to a StringList (of a ListBox, for example). With Delphi-32, we can automatically assign them to the Text property. This is a nice feature, but to be sure we only use it when we compile for Delphi-32, we need the following construction:
{$IFDEF WIN32}
  ListBox1.Items.Text := 'Bob'#13+
                         'Yvonne'#13+
                         'Erik'#13+
                         'Tasha';
{$ELSE}
  ListBox1.Items.SetText('Bob'#13+
                         'Yvonne'#13+
                         'Erik'#13+
                         'Tasha');
{$ENDIF}
Note that the WIN32 alternative is used as first choice, because it's safe to assume that the alternative will not need expanding in future versions of Delphi (we may need to make another choice in the first WIN32 alternative, like the example below):
{$IFDEF WIN32}
  {$IFDEF VER9}
    // this only works in Delphi 2.0
  {$ELSE}
    // but here's a new feature for Delphi 3.0 and up...
  {$ENDIF}
{$ELSE}
  { and here is Delphi 1.x code using the old comment style }
{$ENDIF}
By the way, to convert your 16-bit .DCR file to a 32-bit version, you can either use Resource Workshop (rename the .DCR file to a .RES file, convert it to a .RC file, set the preference to WIN32 and recompile it to a 32-bit .RES file) or my ResConv stand-alone program or Delphi expert.

DLLs
For DLLs basically the same is true. We should (try to) use a single source set to compile to a 16-bit and/or 32-bit DLL. An additional problem is the fact that the sharing of the DLLs data segment (by multiple instances of the same 16-bit program) doesn't work anymore with a 32-bit DLL, since each instantion of the DLL runs in its own memory space. We need to use a WIN32 feature called memory mapped files to actually be able to share information in the same DLL (again a topic we won't discuss at this time).
Components that need support DLLs should unfortunately, be accompanied by two versions of the DLL, one 16-bit and one 32-bit version. Fortunately, both need to be installed in different directories, so the component builder "only" needs to ensure his installation program is working correctly (what installation program?).


Porting (4) - 32-bits DLLs
This time, we'll focus on Dynamic Link Libraries (DLLs) and how they differ in 32-bit Win95/NT World from the plain old 16-bit Windows World we've been used to the past few years.

The DLL
A dynamic link library can be used to share code (and/or resources) between different applications. It is a library of code, that can be loaded dynamically on demand (if needed, or preloaded).
When porting a DLL to Win32, we need to change only a few things: the calling convention and the exporting of the functions.

stdcall
With 16-bits Delphi 1.x DLLs we needed to export the functions to the outside world using the export keyword. In Win32, this keyword is replaced by the stdcall (actually a new Win32 calling-convention), and export is now meaningless. This means we need to use our first {$IFDEF} here to make sure our functions are defined correctly regardless of the version of Delphi:

  function MyFunction(Param1: Integer; Param2: PChar): Word; export;
          {$IFDEF WIN32} stdcall; {$ENDIF}
Note that the types also change, so beware of any Integers and/or Strings, since they change from Delphi 1.x to 2 (see the first column on porting code from Delphi 1.x to Delphi-32).

no index
Delphi 1.x can export functions by name and index number. In Win32, functions are no longer exported by index, but only by name. The index number is only used as a hint to indicate to the loader where to start looking for the specified function name. While this doesn't lead to much different code, it's good to realise what goes on behind the scenes!
We can still return index (hint) numbers as well as names as follows:

  exports
    MyFunction index 1 name 'MYFUNCTIONNAME';

The Import Unit
When porting an import unit to Win32, we need to adjust a few things: the stdcall calling convention, of course, but also the library extension and the resulting handle type when using LoadLibrary.

importing
Unlike 16-bit DLLs, we now need to specify the DLL extension with the library name. Note that Win95 works in both cases (i.e. either the DLL extension or no extension), while WinNT only works with extension. To avoid problems, we should use an extension always when importing a 32-bit DLL. This leads again to a simple use of {$IFDEF} in our import unit:

  Const
    LibName = 'MYLIB' {$IFDEF WIN32} + '.DLL' {$ENDIF} ;

handle
We can use the resulting LibName in two ways: either by specifying it with an implicit import function definition, as follows:

  function MyFunction(Param1: Integer; Param2: PChar): Word; stdcall;
           external LibName index 1; { index is only a hint here }
Using an explicit import unit, we need to LoadLibrary/FreeLibrary the DLL by ourselves, and keep the handle to the DLL to do a GetProcAddress of MyFunction. The code is as follows:
  begin
    LoadLibrary(LibName);
    @MyFunction := GetProcAddress(DLLHandle, 'MYFUNCTIONNAME');
    { now you can just call MyFunction() }
    FreeLibrary(DLLHandle);
  end
MyFunction is defined as a function pointer of the following type:
  MyFunction: function(Param1: Integer; Param2: PChar): Word
              {$IFDEF WIN32} stdcall {$ENDIF};
which is what we would expect, given the type definition we saw earlier.

The DLLHandle is a bit more tricky, as it's a Word in 16-bit import units, but a DWORD in 32-bit import units. I changed it into a Cardinal, to be usable in both environments without having to use an {$IFDEF}. If you forget to change it from a Word, you won't get any compiler errors, but the DLL will seem to have not loaded at all (I got an error message 0, telling me the DLL itself might be corrupted in some way - a real time-consuming problem if you're looking in the wrong way ;-)

Note that a general THandle might even a better solution, which would have been portable in the first place...


Migrating Delphi 2.0x applications to Delphi 3
  1. OLE
  2. BDE
  3. QReports
  4. DCU Formats
  5. Packages
  6. TTreeView
  7. TDataSet
  8. TypeLibraries
  9. Internet and Web Features
  10. Decision Cube
  11. TeeChart
  12. ReportSmith

1. OLE
There have been major renovations in the way that the OLE FrameWork has been implemented in Delphi 3.0x. As a direct result of this, recompilation of your Delphi 2.0x application will be insufficient to take advantage of this new technology. You may Cut and Paste most of your code, but some massaging may be necessary.
In Delphi 3.0x, you should use this new syntax:
  IFoo = interface(IUnknown);
  Tfoo = Class(TObject, Ifoo);

New implementation of keyword 'Interface': allows creation of a COM Interface Object (essentially creates a pure virtual/Abstractclass). Using the keyword interface in Delphi 3 will also allow you to use the interfacename as a Class ID (TCLSID) constant.
Example:

  if IsEqualCLSID(iid, IMalloc) then
See Chapter 13 in the Object Pascal Language Reference Guide for more information on Interfaces.
Most of the Types that were declared in the OLE2.PAS file in Delphi 2.0x have been moved to ACTIVEX.PAS in Delphi 3. There are significant changes in OLECTRLS.PAS, OLECTNRS.PAS, and SHLOBJ.PAS.
Chapter 13 in the Object Pascal Language Reference explains the new use of the keyword INTERFACE.

2. Borland Database Engine
Migrating applications that use the BDE from Delphi 2 to Delphi 3 is not very involved. The main concern is how you will use the new features. Check out the Delphi 3 help file topic Compatability with Delphi 2. This covers a number of issues that are likely to be of interest to BDE users migrating. The following are the highlights of the new features:

3. QReports
Migrating QuickReports application from Delphi 2 to Delphi 3 will involve some restructuring of the reports. Some of the components have changed and there are new components added. Borland has a limited ability to characterize the changes in this tool as it is a third party tool that is bundled with Delphi. As such we do not create or maintain the source code, nor do we have access to it for support purposes. The file Qrpt2man.doc in the QuickRpt directory contains step by step instructions for converting projects from QReports 1 to QReports 2. Refer to section 6 appendix C for further information on this subject.

Delphi 2 QR 1.0d components: QRBand, QRDBCalc, QRDetailLink, QRDBText, QRGroup, QRLabel, QRMemo, QRPreview, QRPrinter, QRSysData, QRShape, QuickReport, QRCustomControl

Delphi 3 QR 2 components: QuickRep, QRSubDetail, QRBand, QRChildBand, QRGroup, QRLabel, QRDBText,QRExpr, QRSysData, QRMemo, QRRichText, QRDBRichText, QRShape, QRImage, QRDBImage, QRCompositeReport, QRPreview, QRChart

New to Delphi 3 QR 2.0: QRComposite, QRRichText, QRDBRichText, QRImage, QRDBImage, QRChart

4. .DCU Formats
When a project is compiled or run, Delphi's compiler produces an intermediate output file on disk from a unit's source code. This compiled version of a unit is stored in a binary-format file with the same name as the unit file, but with the extension .DCU (Delphi Compiled Unit). A .DCU file should not be edited or opened under any circumstance and is not necessary for deployment. Since this is a proprietary format it is liable to change from version to version of the product. If a project is created in Delphi 1 or Delphi 2 the only files necessary to recompile it under Delphi 3 are the .DFM, .PAS, and .DPR file.
If a project contains resource files (.RES) consisting of custom cursors, icons, etc. they will also need to be included. It is critical to NOT relocate the .DCU files along with the other files as that it may lead to strange and unexplainable behaviors with an application.

5. Packages
Packages (.DPL) are special dynamic-link libray used by Delphi 3 applications, the IDE or both (see DELPHI3.HLP). Packages come in two flavors, runtime and design-time. Runtime packages providefunctionality when running an application while design-time packages provide development phasefunctionality. One notable change in Delphi 3 concerning packages is of using ExitProc() (seeDELPHI3.HLP, 'Changes introduced by packages'). For more information on the new file types introduced by packages see TI3213 - Delphi 3 File Types with Descriptions.

Migration Issues
A. 3rd Party Components: (see DELPHI3.HLP, 'using older third party components').
When migrating applications that utilize 3rd party components from Delphi 2 to Delphi 3 it is essential to keep in mind these 3 items:

  1. .DCU files are not cross version compatible. The source code is necessary for recompiling 3rd party components under Delphi 3.
  2. Changes in the VCL source or version specific syntax may have occured between versions. While the external interface for the VCL will remain the same, the internal workings of the VCL may have changed.
  3. Always make sure that the 3rd party vendor provides a Delphi 3 compatible version of their component.

B. Custom Components
Most of the issues regarding custom components also fall into the above listed categories. Additionally, components must be made into packages before being added into the component palette (see DELPHI3.HLP).

C. Deployment to end users
Like Delphi 2, a standalone executable can still be easily created with Delphi 3. Delphi 3 also provides the option to use packages which in turn deploys runtime packages for your application.

D. Deploying Components
With the invention of packages in Delphi 3 the concept of deploying runtime and design-time components changes from Delphi 2. In order to deploy a component for runtime it must be compiled using runtime packages or the appropriate .DPL file must be included along with the .EXE.
If a component is being deployed for design-time use it must be designated as being a design-time package and also be deployed with it's corresponding .DCU file.

6. TTreeView
If a custom component descending from TTreeView has been created in Delphi 2 the event handlers have been reworked. For example: The OnExpanded, OnExpanding, OnCollapsed, and OnCollapsing events now occur in all parent nodes no matter how the nodes are expanded or collapsed.

7. TDataSet - Database Object Differences
The primary differences in the database component hierarchy between Delphi 2 and Delphi 3 involve the introduction of the TBDEDataSet. In Delphi 2, is was not practical to make data-aware controls that did not use the BDE. TBDEDataSet was designed to implement BDE-specific code and TDataSet was generalized sothat it knew nothing about the BDE and merely knew how to do general things like dealing with records. It is now possible to make objects that descend from TDataSet and implement database functionality without involving the BDE at all. These TDataSet descendent components will then also work with all of the standard Delphi data-aware controls.
Chapter 5 of the Delphi 3 "Developer's Guide" gives details and examples for the TDataSet and Chapter 6 discusses the TBDEDataSet.

Remote Data Sets
Unlike Delphi 2, Delphi 3 gives the developer the ability to make a very light-weight database client where the BDE is actually running on a different machine than the client program. This remote, multi-tier capability is implemented by the new Data Access controls: Provider, ClientDataSet, and RemoteServer. Chapter 18 of the Delphi 3 "Developer's Guide" gives details and examples for using remote data sets.

New Data-Aware Controls
Delphi 3 also implements several nice new database controls: DBRichEdit for storing memo-type data in "Rich Text Format", and the DBChart which uses the Tee-Chart charting engine to quickly make graphs directly for the data in database tables without requiring any code. Chapter 14 of the Delphi 3 "Developer's Guide" discusses the TDBRichEdit control, the TDBChart component as well as all the other Tee-Chart related controls are documented in the "TCHARV3.DOC" file in the ..\Borland\Delphi 3\TeeChart directory after Delphi 3 is installed.

New decision support capabilities were added with the Decision Cube set of components which allow a user to drill-down into their data and do multi-dimensional analysis without any additional programming. Chapter 16 of the Delphi 3 "Developer's Guide" gives details and examples relating to the new Decision Support components.
After many requests, the DBGrid now has the ability to show the contents of database memo fields due to the BDE's new memo caching capability.

8. TypeLibrary Editor
The Type Library Editor is the new feature that allows users to easily open type libraries from .tlb, .ocx, or .exe files that contain them. It also allows the user to generate a type library for deployment or use with other development tools. For more information on the Type Library Editor you can refer to the Delphi 3 Help and search under Type Library Editor, or for more information on type libraries in general search the Help for type libraries. Additional information can be found in the MSDN.

9. Internet and Web Features
Delphi 2 allowed users to create
CGI application, ISAPI DLLs, NSAPI DLLs, and WinCGI applications but Delphi 3 now provides a Wizard which greatly simplifies the process and eliminates a great deal of the work involved. Delphi 3 also provides the user with the isapter.dll that eliminates creation of separate ISAPI and NSAPI version of the web application. Finally, with WebBroker, Delphi 3 provides a series of new web enabled components on the internet tab of the component palette. These include components like the PageProducer that simplify web application development and the new socket components that wrap the winsock functionality.

10. Decision Cube - Decision Support Components
In Delphi Client/Server, a new group of components facilitates data analysis for decision support. These components include TDecisionCube, a multidimensional array that caches cross-tabulated data from a dataset; TDecisionQuery, a special dataset object that simplifies use of Decision Cubes; TDecisionSource, which represents a pivot state on data from a Decision Cube; TDecisionGrid, which displays cross-tabulated data from a Decision Source; and TDecisionGraph, which displays Decision-Source data in dynamic graphs.
For further information on these components see the Delphi Help files and from the contents select What's New. Under this heading the Decision Support Components are listed. To create a simple example using the Decision Support Components do File|New..., then select the business tab from the object repository. Now select the Decision Cube object and hit return.

11. TeeChart - TeeChart Graphing Components
100% Native Data-Aware Charting Component Library for Borland Delphi, TeeChart 3 runtime version is packaged with Delphi version 3. It is a fully functional charting library packed with many powerful charting capabilities.
For further information on TeeChart refer to the online documentation including the teechart.hlp file in the default Delphi 3 help subdirectory and the tchartv3.doc file in the teechart directory under the Delphi 3 directory. This component suite also has an exhaustive example program in the demos subdirectory under the Delphi 3 subdirectory. Finally, selecting File|New..., selecting the Business tab, then selecting the TeeChart Wizard allows for rapid use of this component suite.

12. ReportSmith
Delphi 3 no longer deploys with ReportSmith and although the TReport component is not on the Delphi 3 palette it can be added. Simply select Component|Configure Palette, selecting all, and then select the TReport component from the very bottom of the list and then click the Show button. This allows your ReportSmith applications to be migrated to Delphi 3. Keep in mind that Strategic Reporting System is now supporting the ReportSmith tool.

For information on what has changed with respect to deployment of applications be sure to read deploy.txt. This documents the details of supported deployment methods for your application.


This webpage © 2000-2017 by Bob Swart (aka Dr.Bob - www.drbob42.com). All Rights Reserved.