BCB Compatibility Guide


This document describes the do's and don'ts of component writing when you want your Delphi component to work with C++Builder. Many of these issues were discovered while creating and testing the JVCL packages with C++ Builder. We are trying to test as much as we can, but there might still be other issues. If you know of such issues or even any other issues that might help us and other Delphi developers, please contact the JVCL team through the newsgroups on talkto.net:
news://forums.talkto.net/jedi.vcl

 

Making your component work with BCB

 

Encountered errors and possible solutions when compiling a package for BCB

When creating and/or updating packages with BCB, please keep in mind that the IDE has a tendancy to put static paths in different locations. This will prevent the packages from working on a machine different from your own. For ease of use, you should use the IDE to do add or remove files, but you will have to do some cleanup. First, you must remove all required package as BCB will add a lot of unecessary dependencies. For instance, it adds designintf.bpi in the runtime packages. Then save the packages, close it and reopen it with a text editor. You will see that a bpk file is just a XML file with different nodes for different options. The cleaning process involves:
Once you have cleaned the package, you can reopen it with BCB and compile it. At first there is a great chance that it will complain about missing object files (*.obj) at the linking stage. These are solved by adding one package after the other in the requires list of the package you are building. The usual order for BCB6 is:
rtl.bpi, vcl.bpi, vclx.bpi. Database related packages may also require dbrtl.bpi and vcldb.bpi. With BCB5, rtl.bpi and vcl.bpi are together in vcl50.bpi
If this is the case, use BCB to update your package, save it, close it and verify it is still clean (it should, except maybe for SPARELIBS and some absolute path). Once cleaned again, try to compile it. If it compiles and links, DO NOT save it. It is fine the way it is, you don't want to have to clean it again.
On some occasions, BCB may come up with a dialog saying that your package needs to require other packages in order to be compatible with installed packages. You must accept these changes. BCB will automatically recompile and relink your package. You then must save the changes, reclean your package and retry to compile it.
For design packages, you must try to install it, with all the packages it depends on installed. For instance if your package depends on JvCoreC6R.bpi, you must install yours with JvCoreC6D installed.
If all went fine, your package is ready to use. However, there is a great chance that you will encounter some problems when compiling pas files and the one we know of are listed below.

Null not defined

You must add 'uses Variant' in the implementation section of the incriminated pas file even if it was already in the interface section.
Doing so would break Delphi compilation, so you must enclose it with conditional compilation directives, {$IFDEF BCB6}uses Variant;{$ENDIF}
 

Cannot find somefile.dcu

This error occurs because the pascal compiler (yes, pascal, not c++) couldn't find the indicated dcu file. If the dcu file is available directly, then add the path to where it is in the include path for the package. By doing so, please use the $(BCB) constant and not the absolute path to your installation.
But in most cases, the dcu file is not available in the distribution. If you have the VCL source, you may find the associated .pas file. However, DO NOT compile this file, you would break your installation. The dcu file IS AVAILABLE but not directly, it is included in a .dcp file and this file must be indicated to the pascal compiler. Unfortunately, there is no easy way to do that in the IDE, so you will have to close the package from BCB and reopen it with a text editor. This is an XML file and the node you're looking for is called PFLAGS. This contains the flags passed to the pascal compiler. At the end, just before the closing quote, add the recommended flag (preceded by a space) as indicated in the table below:

Missing file
BCB5 flag
BCB6 flag
AccCtrl.dcu
-LUvcl50

FiltEdit.dcu*
-LUdclstd50
-LUdclstd
DesignIntf.dcu*
-LUdsnide50
-LUDesignIde

*This error should only occur in designtime packages. If you have this error with a runtime package, please notify us.

Incompatible types: Set of char and String

Some files in the JVCL were imported from libraries that had partial support for BCB prior to version 5. As these versions did not support sets, they were replaced by other constructs, most often strings. But because JVCL doesn't support anything below BCB5, all conditional compilation directives defining Delphi sets as string must be removed to only leave Delphi style sets definitions and as such remove this error.
 

Incompatible types

Usually an error that has to do with record constructs being used to perform automatic casting. This a language feature to avoid as it is not supported by BCB.

[Linker Fatal Error] Fatal: Unable to open file 'SOMEFILE.OBJ'
This is because a bpi file is missing from the requires node of the package your are trying to build. The table below indicates the name of the bpi files you need to add for different missing obj files
Missing file
Missing bpi file
BCB5, D5
Others
CHECKLST.OBJ
vclx50.bpi
vclx.bpi
SYSTEM.OBJ
vcl50.bpi
rtl.bpi
CONTROLS.OBJ
vcl50.bpi
vcl.bpi
FILECTRL.OBJ
vclx50.bpi
vclx.bpi
SPIN.OBJ
vclsmp50.bpi
vclsmp.bpi

[Linker Fatal Error] Fatal: Cannot find CJCL60.bpl

You must add CJCL60.bpi to the requires list of your package. The directory to both CJCL60.bpi and CJCL60.bpl MUST be in the PATH environment variable. If you don't have these files, you need to compile the JCL first, with the BCB6 specific packages. Remember that you must use JCL version 1.9 or newer in order to compile the JVCL 3.

[Linker Fatal Error] Fatal: Access violation. Link Terminated.

See "Do not use WEAKPACKAGEUNIT" above. These errors should have been all removed from the JVCL following the discovery of a useless WEAKPACKAGEUNIT directive in the file JclResources.pas from the JCL.  Their team has been informed and the file has been updated in version 1.9 and newer.
Before discovering this error, we found that changing the order of the bpi files in the FILELIST node sometimes solved this crash. It is not clear what is the best order, but you may want to try shuffling around the order of CJCL60.bpi, JvCoreC6R.bpi and JvCoreC6D.bpi

This problem may arise from other files and because it actually is the result of the linker crashing, it is very hard to track down. Borland has been told about this problem, we are yet to receive any answer from them.

Please note that sometimes, the linker decides to crash on its own, giving you two linker errors. If this happens, close BCB and reopen it.

Finally, it seems that Update 4 for BCB6 corrected most errors in the linker and as a result we won't support any BCB installation that doesn't use Update 4.


Encountered errors and possible solutions when compiling an application using a JVCL component

These errors generally occur in a JVCL related hpp file. The hpp files are generated from the interface section of the pas files which requires that your components follow some simple rules so that the generated hpp file is a valid file. Some people have suggested that we should put the hpp files in the distribution packages so that we can modify them in order to repair them after they have been generated. This is not possible because those modified files would be overwritten when the users recompile the JVCL on their computers. And not requiring the users to recompile would mean a precompiled distribution package at least 80M big !
So we are asking every developer in the JVCL team to follow the above mentionned guidelines. However, from times to times, somebody may encounter an error when using one of the JVCL components. The below list contains the known errors and their solutions.

E2238 Multiple declaration for '_fastcall TJvClass::SomeFunction'

This one is coupled with an E2344 error. This problems comes from the fact that C++ Builder puts all the inherited functions in the declaration of a class in a header file and the VCL may already define a method with the same name and the same parameters. If your method is not a constructor, the easiest solution is to rename it so that it is different from the inherited one. However, if the error occurs on a constructor (TJvClass::TJvClass), you must follow the guidelines hereafter. This problem comes from the fact that in C++, constructors are named after the class name and there is no way to change that. So to avoid confusion, avoid calling your constructors anything else than Create. If you need additionnal parameters, use the overload directive and adapt your code to take this into account. The disctinction between the different constructors is made upon the order and the types of their parameters.

But in the end, this may not be enough as Borland's VCL already contains a number of constructors. As a result, the types you chose to use for your own constructor may conflict with an existing constructor. In such a case, you should follow Borland's guidelines from their help files: Add a dummy parameter at the end, giving it a default value. Usually, an Integer parameter is enough, like this:

constructor Create(param1 : string; param2 : Integer; dummyForBCB : Byte = 0);

From the C++ code, to be sure to call the correct constructor, you must give a value for this dummy parameter even if it won't be used.


E2303 Type name expected (on a __property line)

This is because of bad casing on an inherited property. The usual example is having a property named PopUpMenu where it should be PopupMenu. BCB is case sensitive so you must respect casing in your pas files, at least in the interface section.
 

E2040 Declaration terminated incorrectly

This generally happens on a constant declaration and is because one or more pas files declare a value for a constant that already exists in the header files bundled with BCB. You must tell the hpp generator not to include this constant by adding a $EXTERNALSYM compilation directive in the pas file, just after your pascal constant. For instance, you must do this for S_OK

const S_OK : Integer = 0;
{$EXTERNALSYM S_OK}
 

E2316 'TPoint' is not a member of 'Windows'

This is because you placed Windows after Types in the uses for your unit. If you don't have Types in the uses for your unit, either add it (simplest), or find all instances of TPoint and replace them by Types.TPoint. This has no impact on your code as the Windows.pas unit defines its TPoint type to be Types.TPoint. However, it does not end up in the Windows.hpp file for C++ Builder because of a NODEFINE directive.
Note that the Types unit is not available in version 5 of the VCL, which means that this problem does not affect Delphi 5 or C++ Builder 5.

 
Cannot initialize constant

(or something similar, usually provokes 3 errors on the same line, the second being the interesting one). This is because the parameter name you chose for your function is a constant under BCB. The only solution is to change the name of the incriminated parameter. This was found with a parameter called SwitchChars in the GetCmdLineArg function in JvJCLUtils.pas

E2347 Parameter mismatch in access specifier ‘specifier’ of property ‘property’

This is generally because the component doesn't uses Integer as its index type for an indexed property getter or setter. You can always avoid using indexed property setters, so please don't use them.

 
E2015 Ambiguity between SOMETYPE and SomeDomain::SOMETYPE

This is because the type SOMETYPE is a global type (in windows.h, generally) and is also defined in the SomeDomain file. To lift this ambiguity, you must add a line at the beginning of the pas file that does that: "{$HPPEMIT '#define SOMETYPE SomeDomain::SOMETYPE'}". But if SOMETYPE is a JVCL type (starts with TJv) then this is because SomeDomain.pas contains a bogus declaration and should be fixed. Please notify us if that problem occurs.

ATTENTION: If the second type is a function like in "JclWin32::WORD(const unsigned int)" you MUST not add this HPPEMIT directive. The problem in this case comes from the fact that a function defined in the .pas file is named exactly as a macro in the windows header an gets replaced by its value. For instance in the given example, LANGFROMLCID was replaced by its value which is WORD and that lead to the declaration of LANGFROMLCID(const unsigned int) from the pascal code to be replaced by WORD(const unsigned int).

However, if this error is generated in an hpp file not from the JVCL (filename not starting with Jv), such as Controls.hpp, you can't change this file, you must reorder the units in your uses clause so that the conflict goes away. For instance, in the case of Controls.hpp, the solution was to put Controls before Windows in the uses clause.

[Linker Error] Error: Unresolved external SomeType::GetMessageA referenced from SomeUnit.OBJ  (Any function with a capital A at the end can be in the message)

This is because SomeType has a public function (or getter/setter for a public property) that is named GetMessage while GetMessage is also a function from the Win32 API. As this function is implemented as Ansi and Unicode versions, there is a typedef that tells the compiler this: whenever you see GetMessage, replace it by GetMessageA.
So the compiler always uses GetMessageA and is happy about it. But when the linker comes in, it looks for GetMessageA in the SomeType class and of course does not find it.
The ONLY solution to this is to rename the function to a name that is not the name of an API function, and as such must be mentionned to the JVCL developpers.
GetMessage is not the only function name that can trigger this error, any function from the Win32 API that is implemented as Ansi and Unicode is not to be used for a public method of a class.