Writing Cross Framework code in Delphi

Updated: Mar 16

Sharing Delphi code between your FMX and VCL applications is pretty easy for non-visual functionality, with the exception of zero-based strings (and ARC on mobile platforms which is being rolled back soon to the traditional Delphi memory model.) However, it does get a little tricky when it involves dealing with GUI controls in a cross-framework manner.

One problem is there is no built-in way to detect if the application is based on the FMX or VCL framework. There has been some discussion on this, and one way suggested on a July 2017 forum post by Uwe Raabe (and repeated on a January 2019 StackOverflow response) is to edit the UserTools.proj file and add some XML which will produce a compiler define that determines the framework type. This works for any project on that machine but since it's editing a sparsely documented file in your AppData directory, and every member of your dev team needs to also maintain this custom file on their machine (which is typically not in version control), I do not think this is a good approach. (Sparsely documented may be too kind - is there any documentation at all on UserTools.proj?)

In an October 2016 blog post by Scott Hollows, there was some discussion for VCL/FMX conditional compilation and the suggested response was to check the FireMonkeyVersion constant in the FMX.Types unit:

This does appear to work - but only if FMX.Types is included in your Uses clause. If you are trying to include specific units in your Interface section based on the application's target Framework, then this doesn't seem to be a solution. Perhaps I haven't had enough coffee this morning, but this simply does not work for me. (Update: You can rely on the special Defined block for this...see wiki article.)

The best solution that I have found is to fill in the gap that Embarcadero has left and simply add a new conditional symbol per project. It will follow along with your project in version control, and it makes cross-framework code easier to implement with little effort. There's a project variable that can be leveraged for this purpose: $(FrameworkType) You can add this to your project options (as in the image above) and then support code like:

You could simply stop there, but if you fail to add the compiler define to a project, then the code above will assume you are targeting the VCL framework. I prefer to not rely on the fact that I will remember to add the compiler define, so I have a special include file which validates the build:

When I am developing a unit that works with cross-framework code, I add this include file to the header of the unit to ensure that the required framework define is present.

Note: you could implement your own conditional symbol like FMX_FRAMEWORK and VCL_FRAMEWORK instead of relying on the $(FrameworkType) variable, especially if you are building iOS applications with Delphi XE4 as you will apparently need to support the FMI framework. I started using my own custom symbols until I stumbled across $(FrameworkType) and I switched to this built-in variable.

Example Cross Framework Code

I've added a cross framework ScreenCursorStack to the fairly new iaLib repository on GitHub along with VCL and FMX demo projects to demonstrate how VCL and FMX projects can use identical code for manipulating the screen cursor:

I currently don't do a lot of cross framework coding, but this approach seems to be a way around the missing Framework compiler define. While writing this blog post today, I've put in a new feature request RSP-27405 to provide a built-in conditional symbol to determine the target framework. If you agree with the approach, please add your vote. If you don't, feel free to add a blog comment with a better suggestion as there should be a better way.

820 views0 comments