Migrating your project from older versions of Delphi. The Pitfalls.

A true testament to Delphi is the number of applications out in the wild which were written with much older versions. Every day, I am asked questions about migration from versions as old as 15-20 years, to the latest edition. Thankfully, Delphi makes migration over the decades relatively painless, however, this doesn’t mean effortless!

This page aims to address the most common pitfalls which may befall a migration from any version of Delphi prior to 2009, including Borland versions, to modern Embarcadero versions.

I intend this page to become something of a living document, which I will update over time to include any pitfalls that I uncover. I will also aim to add tips & tricks for the migration process, and links to relevant documentation.
If you come across something which is not included, please register and leave a comment, which I may integrate into this page.

Expectations of Cross Platform.

Before we begin looking at the common pitfalls of a version migration, I would like to take a moment, (actually, a good page or two,) to discuss an often miss-understood feature of modern Delphi versions. If you have no interest in migrating your code to mobile platforms, or the MAC desktop, go ahead and skip to the section entitled “Windows 64-bit migration” and we’ll pick up from there.

Modern Delphi is able to compile code to target Windows 32-bit and 64-bit, MAC OSX, Android and iOS. This fact has lead to an expectation that Delphi can take your Windows application and somehow, make it cross platform. This is not true! It would be some genuine kind of magic if it were true, but it isn’t. What is true is this: Modern versions of Delphi make it possible to write applications for Windows, Mac OSX, Android and iOS.

If your application was originally written for Windows then it uses the Win32 API. If your application is a VCL application, then it uses the Win32 API. One thing that is not available on Mac OSX, Android, and iOS is the Win32 API. See where I’m going? Your application is not going to run on another platform without some significant changes. Thankfully, while Delphi won’t magically fix this problem for you, there are some companion tools to help…

Cross platform applications are written against a new framework named FMX. (Formerly FireMonkey, FMX=FireMonkey X-platform).
The FMX framework does not depend on the Win32 API and it’s controls, but instead, actually opens up a rendering context using OpenGL or DirectX, and renders controls directly to that API. In this way, visual controls become cross platform. The FMX framework has mirror controls for the vast majority of VCL controls which ship with Delphi, for example, VCL and FMX both have a TEdit control, they both have a TButton control, and so on..

There is a tool called the ‘Mida converter’ ( see here ) which is able to open up your existing VCL forms (the .dfm files) and modify them to use the FMX versions of controls instead of the VCL controls. In doing so, the form is effectively ported from VCL to FMX and therefore is made available to platforms other than Windows. There are still one or two pitfalls to this approach however. The Mida converter is not much more than a copy/replace macro tool, if you are using controls which are not a part of the VCL (third party controls for example), then the Mida converter cannot migrate these controls, you’ll have to track down an FMX version of that control if it exists, or an FMX replacement from another third party vendor.

One such vendor of FMX components is TMS Software: https://www.tmssoftware.com/site/tmsfmxpack.asp

Further Cross Platform Considerations

Beyond the visual controls, there are several other considerations when moving from a Windows-Only application to a cross platform solution. For a start, all of the platforms supported by Delphi, other than Windows, are in some way based on a unix/linux variant, and are therefore POSIX based. Some things to consider because of this…

  • POSIX platforms have different file systems. There is no ‘C:’ drive on an Android, iOS or MAC device. If you’ve hard-coded windows paths into your application, then you’re going to have to modify the code, likely using conditional defines, to understand where the same file lies on the other platforms file systems.
  • There is no registry. Though some platforms offer a simulation of the windows registry, the majority of them have no such thing. Instead, configuration is usually stored in a specific directory. The ‘/etc’ directory is most commonly used to store configuration files, and, there’s no pre-specified file format. One application might use a .ini file or similar name-value pairs file, while another may use a JSON file, and yet another might use an XML file. If your VCL application depends on configuration in the registry, you’re going to have to migrate it to a file under the configuration directory for other platforms. If your VCL application modifies the behavior of the OS or other applications through alteration of registry keys, you’re going to have to find out how the same functionality can be achieved on other platforms.
  • There is no Kernel32. If you’re using custom calls to the windows API’s, you’ll have to find the equivalent behavior of the alternate platforms. In many cases the FMX classes will provide an answer, but if your application is VCL it’s likely not using those classes.
  • Not all TCanvases are made equal. If your code, or that of a third party vendor, is making direct calls to TCanvas for rendering, or worse, directly to the GDI, you’ll need to migrate this code to work on other platforms. The FMX framework has a TCanvas just as VCL does, however, the methods are not all the same, they also have different parameter lists. This is unavoidable as the VCL TCanvas was rendering to the windows GDI interface, where-as, the FMX TCanvas is rendering using a 3D API such as OpenGL or DirectX. This is true even for 2D rendering, because modern graphics cards simply don’t offer 2D rendering anymore, instead, they expect you to render 2D graphics into 3D space, and then view them with a special perspective mode which reduces out the Z-Axis. Sorry, you’ll have to migrate the TCanvas code too.
    More on: Using the FMX TCanvas
  • Your database driver probably doesn’t exist. Many developers are familiar with using components named TConnection, TTable, and TQuery (or similarly named equivalents) to connect to databases directly. These component sets usually rely on some binary driver, and unfortunately, unless you’re using InterBase, there probably isn’t a version of that driver available for the ARM-*nix based mobile platforms that you’re targeting. You likely cannot connect your application directly to a database, and even if you could, you may not want to with potentially unstable internet connectivity in many mobile scenarios. The most common solution to this problem is to expose your data in the form of JSON using a REST based web service (with SOAP services making up the second most common solution). Delphi has several components for working with JSON/REST/SOAP and can even adapt a JSON service into a TQuery style component for you, but you’ll have to migrate away from your older data access components.
  • There are some differences in the mobile compilers. Delphi achieves it’s single source code, multiple target platforms functionality by the use of multiple compilers. One compiler for windows 32, another for windows 64, an more still for android, iOS and Mac. Well, not all of these compilers are 100% compatible, though they are a good 99.9%.
    • ARC. The mobile compilers use the ARC (Automatic Reference Counting) memory model, so your code needs to be made ‘ARC safe’, this can be done in such a way that you do not damage the code for desktop platforms, however, you need to be aware of ARC and how it works to ensure that your code is safe for it. Not doing this will lead to bugs which can be quite difficult to track.
      Marco Cantu on ARC: http://blog.marcocantu.com/blog/delphi_arc_android.html
    • No Ansi Strings. The mobile compilers do not support ansi strings at all, period. Functions such as StrAlloc(), StrDispose() etc are missing, and if you absolutely must use ansi strings, well then you’ll have to put them into arrays or buffers and work with them using pointers – but you should probably just give up on ansi-strings, no modern OS or modern software uses them anymore! Unicode migration may instill some fear, but it’s not too frightening, and it is worth-while, as we’ll see later in this page.
    • Strings index from zero! All other array style access is indexed from zero, but for some reason, legacy pascal indexed characters in a string from one. If I ever did know why this was, I’ve long since forgotten, but it’s no longer true for the mobile compilers. Strings now index from zero. This is, sadly, just something we have to swallow. We got away with strings being the odd-one out for a long time, but it’s time to put this long standing syntactical faux-par to rest.

A Final word, and some hope, on Cross Platform migration.

Most developers with the mistaken expectation that Delphi will migrate their application to other platforms for them, are also missing one key important detail, in that they probably don’t actually want to migrate their application to those platforms at all! If you’re among them, stay with me here. Mobile screens are small, and operated with a cumbersome finger rather than the elegant precision of a mouse. Mobile devices also suffer a lack of resources (RAM, Disk Space, CPU cycles etc) when compared to desktops. It’s quite possible that if you did a straight migration of your VCL application to FMX and deployed that to a mobile device, it would be unusable on such a small screen, or perform horribly due to the lack of device resources.

A far more sensible strategy, in many cases, is to consider starting an entirely new project for your mobile application. This “companion app” project can still make use of large portions of your non-visual code, it’s still pascal after-all, but you can limit the new project to only those features which actually make sense on the mobile device. What’s more, Delphi ships with some classes to aid integration of your mobile companion application, into a network along-side your existing desktop application. For example, with the app-tethering classes, your mobile application can directly call actions within your desktop application, and bidirectionally share data.

Windows 64-bit migration.

Migration from Windows 32-bit to Windows 64-bit is a far less treacherous endeavor than migration to entirely different platforms, however, there is one pitfall that I’ve come across in doing so.

It became quite a common practice in the Delphi developer community, to assume that an integer and a pointer are the same size (32-bits), and given that instances of classes in Delphi are actually just a special kind of pointer, you could effectively type-cast a class as an integer for storing in an integer array. In addition to this, the .TAG property on may visual classes of the VCL was also a 32-bit integer and may have been used as an object pointer by some type-casting jiggery-pokery. Well, if you’ve done this, or you’re compiling third party sources which do this, you’ve now got a problem. Under windows 64-bit, pointers are, of course, 64-bits wide, however the integer type remains 32-bits wide. You need to alter this code to account for the different data types.  Some new integer data types have been added to assist. NativeInt and NativeUInt are new datatypes which act as an integer, but which will always match the width of a pointer on the target platform.

Now before you run off and start fixing up your code for 64-bits, ask yourself if you really need to? There are many common misconceptions about 64-bit code, and often there is little benefit in upgrading to it. We’re not in the same situation as were were when computers moved from the 8-bit era to 16-bit, or from 16-bit to 32-bit.

You see, one of the most common reasons to upgrade an application to 64-bits is to give the application more addressable memory space. With 8-bits you can only address 256 bytes of RAM, and to enable for more, certain bank switching operations were required which cost processing time. Things improved with 16-bits, you could address a whopping 64k of RAM, and still bank switching was required to make use of more memory. Enter 32-bit modes and you can address a giant 4GB of RAM! Even today, this is sufficient for the vast majority of applications, and Windows 32-bit would only really ever allow your application to use 2GB of it in any case, reserving the other 2GB for kernel space.

With some few exceptions, if your application is using more than 2GB of RAM, you’re probably doing something wrong.
For example, if you’re dragging back millions of rows of data from a database, do you really need it all held in RAM? For display purposes, no human wants to see those millions of rows all at once, even if you could cram them all on the screen, so perhaps caching to disk file is a better strategy. Essentially, the vast majority of applications will operate with 2GB-4GB of addressable RAM, so this may not be a reason to upgrade.

What about speed? Well, 64-bit processors aren’t really any faster than their 32-bit counterparts, their speed advantages come from being able to handle wider integers. So if your application needs to work with very large numbers, an upgrade to 64-bit might improve performance, but, if your code doesn’t already use 64-bit numerical data types, you’ll have to go through all of your code and replace the 32-bit types with 64-bit equivalents. Recompiling the same application for a 64-bit target won’t cut it.

Ultimately, there might be no good reason to upgrade at all, and remember, 64-bit executable files are significantly larger than 32-bits, because all of the instruction operands and encoding is larger. Be sure you need 64-bits before making the jump.

Unicode Migration.

The dreaded unicode migration, which somehow seems to fill so many developers with fear! Unicode is not so scary as it might sound, in fact, of all the compiler products that have migrated into the unicode era, Delphi is one of the easiest. Some projects will require zero changes, the compiler simply takes care of it for you, while other projects, well they may require some changes, and a few projects will require a lot of changes.

There is a tool ( Unicode statistics tool ) which can help you to determine the number of changes that you’ll need to make. You point this tool at your project and unit files, and it’ll perform a statistical analysis of the code for you, and give you some measure of the effort required.

In most cases the compiler will gracefully coerce the string types so that your code continues to compile and function as expected. The cases in which this is not true typically include the following scenarios:

  • Using strings to store binary. To some this may seem like a crazy idea, but very early versions of Delphi had a limitation on the storage of binary data (no array of byte), which caused creative Delphi engineers to take this option of using a string to store binary data. This type of code should have long since been replaced, I mean, you have had two decades to fix it! You may however still be using code which does this, and you’ll now be forced to correct it because the compiler has no idea what you’re doing. There’s just no way for the compiler to determine that you’re misusing the string data type, and so it can’t help you here.
  • Serialization and Deserialization. If your application has serialized strings (i.e. passed them byte-by-byte into a stream) for storage on disk or in a database, well, those strings were ANSI when the serialization took place. After you migrate your code to newer Delphi compilers, the application will attempt to deserialize the string from the file as a UTF16-LE unicode string, in which each character is 16-bits wide (save some which are 32-bits, but that’s a complication worthy of more reading on unicode). So your old serialized data cannot be deserialzed. One solution would be to read that string as a byte-array, and pass the byte array to one of the many unicode conversion routines which are now a part of the RTL, in order to convert it from an ANSI string to a UTF16LE string.
  • Calls to older APIs. Windows went unicode a long time ago. When the Win32 API was upgraded, new versions of each API call were put in place with a ‘W’ post-fix, indicating that they are wide string versions of the calls. Wide strings are a specialized string type used by windows, but the data contained within them is essentially UTF16-LE, the same type of data as is now the Delphi default string type. Anyway, the compiler may complain to you if you’re passing a string (now UTF16) to an older ANSI based API call. In most cases the compiler will also take care of the conversion for you, but a warning will be generated so that you know this has happened, and you’ll perhaps need to type-cast a parameter or two. This would get you working again, but ideally, you should migrate to the Unicode form of your API if it’s available.
  • Some controls behave differently under unicode!
    As you are likely already aware, most VCL controls are wrappers around the controls provided by the windows API. During migration of the VCL to unicode, many ANSI API calls were replaced by calls to their alternate unicode aware equivalents. I recently discovered a discrepancy in the way that the ‘SelStart’ and ‘SelLength’ properties of the TRichEdit component function. Carriage returns aren’t counted in these properties now, as they were in pre-unicode versions. I’ve added a blog post with some solutions to these problems here:  http://chapmanworld.com/2015/11/04/trichedit-behaves-differently-under-unicode/ 

More details on unicode migration with Delphi (far more detail than is covered here) may be found here at http://www.embarcadero.com/rad-in-action/migration-upgrade-center

The real datatype.

Often neglected in discussions of version migration is that the old ‘real’ datatype has been removed from the compiler, and replaced as an alias to the ‘double’ datatype. The reason it’s often neglected is that it’s so very rarely a problem, however, just as with the unicode migration, this little change can raise it’s head when deserializing data which was serialized with an application compiled with an earlier Delphi version. Head’s up if you use reals in your code.

The BDE is gone!

Don’t panic. The BDE is not actually completely gone yet, but it is being deprecated. By default, the BDE no longer ships with Delphi but may be downloaded separately from your EDN account after you make your Delphi purchase. The reason for this is that the BDE is very old, is lacking in unicode support, and has been replaced by the far superior FireDAC.

If your application makes use of the BDE, it is recommended that you use the reFind tool to migrate the project over to FireDAC. ( Migration from BDE to FireDAC using reFind / David I on Migration to FireDAC and Interbase ) This tool pretty much does the work for you! I haven’t heard of anyone actually having difficulties with this, and so if you have had difficulties and especially if you’ve found solutions, consider registering and leaving a note in the comments!

Still using paradox?

Some older applications are still using the paradox database. Unfortunately this database is no longer supported and so you’re likely going to have to migrate to another database engine. If you’ve only ever used paradox for your database needs, you’ll likely want to learn a little SQL and DDL too, in order to take advantage of more modern databases. Such tuition is beyond the scope of this page, but you’ll find some excellent tutorials online and it’s going to be worth your while, honest!

Marco Cantu on migrating from Paradox and dBase.
W3Schools SQL Tutorial.

Third party components.

I’ve saved the best until last for you! Some developers love them, others hate them, but in both schools you’re probably using at least some third party components. The two biggest reasons for dislike of third party components are:

  • Issues with the vendor. They may be unreliable at providing updates, bug fixes etc, or may have gone away entirely.
  • Issues with the components. Components which are poorly written can affect the stability of your application and even the Rad Studio IDE.

If you chose your vendor carefully however, you’ll have found an excellent third party component set, with frequent maintenance, and which provides stable high quality components which radically enhance Rad Studio / Delphi, and in turn your product too. For this reason, I fall into the love ’em category!

Regardless of where you fall on this, if you’ve used third party components, you’ll need up-to-date versions of them before you can migrate your project from an older Delphi version to a new one. Embarcadero are making efforts to aid in this with their new GetIt feature, which provides a third party component repository to automatically install several popular component sets right within the IDE!

getit

See: RadStudio XE8 gets a component repository!

The collection of components in this repository is small, but growing, and Embarcadero have even funded updates to common component sets who’s maintainers had gone away or abandoned their products. Embarcadero also assures that components in the repository will be kept up-to-date with the latest Delphi versions, so can’t find yourself in the position of not having that component set again in the future.

At the time of writing this, the components installed in the GetIt repository include:

Abbrevia 10.0
AerServ 10.0
AsyncPro for VCL 1.0
Boos 1.39 / 1.55
Essentials TurboPack for VCL 1.0
ICS forFMX and VCL 8.16
Lockbox 2.0
Lockbox 3.5
Modernized AggPas VCL
OmniThread library 3.04a
OnGuard for FMX 1.0
OnGuard for VCL 1.0
Orpheus for VCL 4.0.8
PowerPFD for VCL 1.0
SynEdit 1.0
SysTools for VCL 4.04
VirtualTree for VCL 5.5

If you’re not yet using XE8, several of these components are also available for XE7 from here: Romans Blog

If you’re in the unfortunate position that you have third party components which are no longer maintained, but you were wise enough to purchase the source code, it’s often relatively trivial to update that code to compile in the latest version of Delphi. I recently posted an update to the Advantage database components for example, because SAP had not yet upgraded them for Delphi XE8. The only real change that I had to make in the code was to alter a conditional define to include the current compiler version number, and that’s it!

Also, if you used to use components found on source forge, but which appear to have fallen out of maintenance, be sure to go check at GitHub to see if the project was migrated. Lots of them were, without warning or update.

Finally, if you have third party components which are no longer maintained, and for which you do not have source code, well then I’m sorry but you’re out of luck. Over the decades, companies have entered and left the Delphi community and there was no avoiding that. If you wish to upgrade you’re going to have to find equivalent components from another vendor, and modify your code to suite those.

How long will my migration take?

It’ll take approximately twice as long as half the task. I’m sorry, but project to project things will vary wildly. You may not touch any of the above migration pitfalls, or you might be unfortunate enough to hit all of them.

Some of my C++ customers have reported an average of one day to convert a legacy C++ Builder project with 60,000 lines of code. While C++ Builder is it’s own product in it’s own right, it too is VCL and FMX based and went through the unicode migration, and so these numbers may be a vague guide. Be sure to use the Unicode migration statistics tool linked above, and be conservative but not fearful of migration.

Conclusion.

If this looks like a lengthy page already, keep in mind that I’ve touched upon cross platform migration which I could have left out, and we’re discussing migration over two decades of versions in Delphi. Version to version, Delphi breaks very little, but it’s a mature product that has had to change with the times.

Modern Delphi has a great many features in an IDE which is (at least in my opinion,) second to none. It has all the modern language features you’d expect and is able to stand it’s ground in the desktop, n-tier enterprise, cloud and mobile development spaces. If you’re not using the latest version, you are missing out on a lot! I hope your migration progresses well.

Thanks for reading!

Print Friendly, PDF & Email