I am upgrading Delphi software from Delphi 6 (2001) to Delphi 11 Alexandria.
This software consists of many BPL's, but this code is not working properly. The is command is not returning True, when checking if the component from a BPL is an TIBQuery - although it really is.
procedure LoadDLLAndPassDatabaseConnection(const DLLName: string);
var
PackageHandle: HMODULE;
ServiceModule: TMyServiceModule;
I: Integer;
Component: TComponent;
begin
PackageHandle := LoadPackage(PChar(DLLName));
ServiceModule := TMyServiceModule(GetProcAddress(hInst,'GetServiceModule'));
if Assigned(ServiceModule) then
begin
for I:=0 to ServiceModule.ComponentCount - 1 do
begin
Component := ServiceModule.Components[I];
// This component is declared in another bpl.
// It really is an TIBQuery, but the following if never returns True...
// (Evaluating Component.ClassName results in 'TIBQuery')
if Component is TIBQuery then
begin
// this is never executed...
TIBQuery(Component).Database := GetDatabase;
end;
end;
end;
end;
I already considered to compare classnames, but this does not work for descendants. And we tried toggling project options such as "Emit runtime type information", but that's not making any difference.
How to get this working?
Thank you!
CodePudding user response:
The is
operator does not work across BPLs (DLLs) for the following reason:
- The class you are inspecting is implemented inside its own unit file.
- You build the BPL, link the unit, and a RTTI section is created inside the BPL file.
- Then, you build the EXE, link the unit, and a new RTTI section is created inside the EXE file.
Now: the class name
is the same for the two modules, but the RTTI, used by the is
operator to check equality, are different, so the operator returns FALSE!
Solution: check equality againts class name.
CodePudding user response:
As Antonio Petricca wrote (thank you!), it's not possible to use the is
operator. I now have implemented this by comparing (descendant) class names - I want to share the code:
function IsSameClassOrAncestor(const ClazzToCheck: TClass; const Clazz: TClass): Boolean;
begin
Result := SameText(ClazzToCheck.ClassName, Clazz.ClassName);
if not Result and not SameText(ClazzToCheck.ClassName, 'TObject') then
begin
Result := IsSameClassOrAncestor(ClazzToCheck.ClassParent, Clazz);
end;
end;
This way, I can check this as follows:
if IsSameClassOrAncestor(Component, TIBQuery)
begin
// The code is now executed correctly, also for descendants
TIBQuery(Component).Database := GetDatabase;
end;
CodePudding user response:
I found this somewhere, but it seems to contradict Antionio's answer a bit.
When you use packages, there is only ever one copy of any unit in memory. One copy of Forms, one copy of SysUtils, one copy of System (well, most of it), one copy of StdCtrls, etc. All class-related operations, such as the "is" and "as" operators, rely on class references. Class references are actually just addresses. They point to definitions for the layouts of the classes' internals. (They point to what's called the virtual-method table, the VMT.) Two classes are the same if they point to the same VMT -- if the addresses are equal. When you have a class defined in the EXE's copy of StdCtrls and the same class defined in a DLL's copy of StdCtrls, those classes will really have different addresses. The "is" and "as" operators won't work with cross-module clases. But when you use packages, there is only one copy of the class, kept in vcl70.bpl, so all modules that reference that package will share a single class definition.