Jump to content
Wagner Landgraf

RTTI info for TBytes using runtime packages

Recommended Posts

Hi all,

I'm having a hard type with RTTI info using runtime packages. It looks only related to TBytes type, but I'm not sure. 

In summary, I do a test like this:

 

  if TypeInfo1 = TypeInfo2 then

 

Both TypeInfo1 and TypeInfo2 are PTypeInfo values.

TypeInfo2 is retrieved from a TBytes variable declared in Unit1 which is included in Package1.

TypeInfo2 is retrieved from a TBytes variable declared in Unit2 which is included in Package2.

The test above returns false.

 

I know this situation is typical and requires some checks to be sure it works. But I've been through all that I know, which are:

 

1. Make sure your application (exe) is compiled with "runtime packages" option checked.

2. Make sure Package1 and Package2 are included in the list of runtime packages of the application.

3. Make sure the type being tested is declared in a common package which is required (shared) by both Package1 and Package2. Since the type here is TBytes, there is no need for a shared/common package, and both Package1 and Package2 simply require rtl, which I believe is enough.

 

Am I missing something obvious here? Does anyone have any hint which I might be looking for? This is Delphi 10.1. Berlin.

 

Thanks in advance.

Share this post


Link to post
1 hour ago, Wagner Landgraf said:

I've been through all that I know, which are:

 

1. Make sure your application (exe) is compiled with "runtime packages" option checked.

2. Make sure Package1 and Package2 are included in the list of runtime packages of the application.

3. Make sure the type being tested is declared in a common package which is required (shared) by both Package1 and Package2. Since the type here is TBytes, there is no need for a shared/common package, and both Package1 and Package2 simply require rtl, which I believe is enough.

 

Am I missing something obvious here? Does anyone have any hint which I might be looking for?

It is not enough to just enable "runtime packages" in the main application.  Package1 and Package2 must also be compiled with "runtime packages" enabled, and the "common unit" must be in a 3rd runtime package that they both require.  Yes, the RTL package is sufficient, but you have to make sure Package1 and Package2 are linking to it dynamically (ie, you need to ship "rtl240.bpl" alongside your own BPLs) and not statically, otherwise they end up using separate copies of the RTL.

Edited by Remy Lebeau

Share this post


Link to post

Thanks Remy.

 

3 minutes ago, Remy Lebeau said:

you have to make sure Package1 and Package2 are linking to it dynamically (ie, you need to ship "rtl240.bpl" alongside your own BPLs) and not statically, otherwise they end up using separate copies of the RTL.

 

How to link dynamically? Isn't it exactly what "Build with runtime packages" for? I suppose if "rtl" package is required by both Package1 and Pacakge2, and if rtl240.bpl is not present, an error would be raised when the application tries to load the package.

Share this post


Link to post
36 minutes ago, Wagner Landgraf said:

How to link dynamically? Isn't it exactly what "Build with runtime packages" for?

Yes, and you need to make sure that is enabled for not just the EXE but also for your own Packages, too.

Quote

I suppose if "rtl" package is required by both Package1 and Pacakge2, and if rtl240.bpl is not present, an error would be raised when the application tries to load the package.

Yes, but only if your packages actually use functionality from the RTL package so its linkage is not optimized out.

 

It would be really useful if you could post an actual code example that is not working for you.

Edited by Remy Lebeau

Share this post


Link to post

Essentially what the original post tells you is that these two types are distinct. Since they are declared in the rtl package, at least one of your modules is not linking to the rtl as a runtime package. 

Share this post


Link to post

@David Heffernan, thanks, I'm aware of that. The key here is that I don't know what else I'm supposed to do tomake sure that both modules are linking to rtl, as I checked everything I know of (listed above).

 

@Remy Lebeau, I don't see any option "Enable with runtime packages" for packages. I believe it's implicit? I have added "rtl" to the package requires list, and also to the packages needed by the application. I believe this is enough, but obviously something else is missing. 

Share this post


Link to post

The issue is that TBytes is an alias for TArray<Byte> - generic types being used across different modules have several implications such as that they manifest into each module with their own typeinfo being generated.

 

Try this code for example:

 

uses
  Rtti, SysUtils;

var
  ctx: TRttiContext;
begin
  Writeln(ctx.GetType(TypeInfo(TBytes)).QualifiedName);
end.

In a regular console application it will print:

 

System.TArray<System.Byte>

While you would assume that it should print:

 

System.SysUtils.TBytes

Now if you enable runtime packages for this console application and let it link with rtl you will get an ENonPublicType here. Every module that is using TBytes is not referencing some type that is declared in the RTL runtime package but emiting its own version of TArray<Byte> into its binary.

Personally I think this is an issue with the compiler treating the declaration of TBytes = TArray<Byte> different from TBytes = array of Byte in terms of the typeinfo generated - I was sure there is a QP entry somewhere about it but I cannot find it right now.

 

To further provide the proof that aforementioned assumptions of other posters are wrong use the following code with a console application linking with the rtl package:

 

uses
  Rtti, TypInfo, Classes, SysUtils;

var
  ctx: TRttiContext;
begin
  Writeln(ctx.GetType(TBytesStream).GetField('FBytes').FieldType.Handle = TypeInfo(TBytes));
end.

This will print FALSE as the typeinfo of the FBytes field which is of type System.SysUtils.TBytes in fact differs from the typeinfo generated for the usage of TBytes in the console application binary.

 

In fact there does not even exist a type called TBytes but only System.TArray<System.Byte> as TBytes it just an alias as I said and the declaration TBytes = type TArray<Byte> is not legit and raises E2574 Instantiated type can not be used for TYPE'd type declaration

Edited by Stefan Glienke
  • Like 3
  • Thanks 1

Share this post


Link to post

Thanks @Stefan Glienke. That makes sense, even though I'm (almost) sure I had this working in other applications built with runtime (i.e., having TBytes being recognized as the same type from two different packages). The difference here is that this is an application from a customer and is a huge one - it's an ERP composed of hundreds of packages. 

 

If that's the case, I suppose there is no workaround using Delphi builtin types, and I would have to create my own type in my library. Or check for the type name instead of type info (which would be slower). In summary, a kludge...

Share this post


Link to post

The types are assignment compatible. When you declare a variable of TBytes in one module and call a method from a runtime package that uses TBytes that code will compile and work. Just the typeinfo generated is different so if you do some RTTI work you get false results.

Share this post


Link to post

Yes, I'm saying that I had it working considering both type info were equal. Same situation as this. 

 

The code in point here is JSON serialization. User pass one generic TValue (or an object with lots of properties) and I check the type of the TValue (or property) and serialize it accordingly. So I check if a type is TBytes and serialize it as base64-encoded. As far as I know, this works find with an app using runtime packages and TBytes, just in this application it doesn't. I guess.

 

So as I said, instead of checking if the value being serialized has the same type info as TBytes, I would have to check if the type name is "TArray<System.byte>" or maybe check if it's an array of type byte. It's a valid solution, although still makes me uncomfortable that such issues might happen with other types.

Share this post


Link to post

Regardless the current typeinfo issue I would always check for typeKind = tkDynArray (and possibly even tkArray) and ElemType = Byte because then you can also handle any custom declared TMyBytes = array of Byte type.

  • Like 2

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×