简体   繁体   中英

Component is specific class - does not work in BPL structure

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!

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.

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;

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM