繁体   English   中英

组件是特定的 class - 在 BPL 结构中不起作用

[英]Component is specific class - does not work in BPL structure

我正在将 Delphi 软件从 Delphi 6 (2001) 升级到 Delphi 11 Alexandria。

该软件包含许多 BPL,但此代码无法正常工作。 当检查来自 BPL 的组件是否是 TIBQuery 时, is 命令没有返回 True,尽管它确实是。

    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;

我已经考虑过比较类名,但这不适用于后代。 我们尝试切换项目选项,例如“Emit runtime type information”,但这没有任何区别。

如何让这个工作?

谢谢!

is运算符不能跨 BPL (DLL) 工作,原因如下:

  • 您正在检查的 class 在其自己的单元文件中实现。
  • 您构建 BPL,链接单元,然后在 BPL 文件中创建一个 RTTI 部分。
  • 然后,您构建 EXE,链接单元,并在 EXE 文件中创建一个的 RTTI 部分。

现在:两个模块的class name相同,但是is运算符用于检查相等性的 RTTI不同,因此运算符返回FALSE

解决方案:检查 class 名称是否相等。

正如 Antonio Petricca 所写(谢谢),不可能使用is运算符。 我现在通过比较(后代)class 名称来实现这一点 - 我想分享代码:

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;

这样,我可以检查如下:

if IsSameClassOrAncestor(Component, TIBQuery)
begin
  // The code is now executed correctly, also for descendants
  TIBQuery(Component).Database := GetDatabase;
end;

我在某个地方找到了这个,但它似乎与 Antionio 的回答有点矛盾。

使用包时,memory 中的任何单元都只有一个副本。 Forms 一份,SysUtils 一份,System 一份(嗯,大部分),StdCtrls 一份等。所有与类相关的操作,例如“is”和“as”运算符,都依赖于 class参考。 Class 引用实际上只是地址。 它们指向类内部布局的定义。 (它们指向所谓的虚拟方法表,即 VMT。)如果两个类指向同一个 VMT——如果地址相等,则它们是相同的。 当您在 EXE 的 StdCtrls 副本中定义了 class 并且在 DLL 的 StdCtrls 副本中定义了相同的 class 时,这些类实际上将具有不同的地址。 “is”和“as”运算符不适用于跨模块类。 但是,当您使用包时,只有一份 class 副本保存在 vcl70.bpl 中,因此所有引用该 package 的模块将共享一个 class 定义。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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