加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

Delphi [weak]引用属性在多个库中执行时产生“无效类typecast”

发布时间:2020-12-15 04:27:09 所属栏目:大数据 来源:网络整理
导读:德尔福柏林10.1增加了[弱]参考. Marco Cantu’s Blog有一些基础. 对于我的测试,我创建了两个拥有两个自动化对象类型的COM库.容器对象保存内容对象的列表,而内容对象持有对其容器的弱引用. 以下两种情况进行了测试和正常工作(弱引用设置为null并释放内存):
德尔福柏林10.1增加了[弱]参考. Marco Cantu’s Blog有一些基础.

对于我的测试,我创建了两个拥有两个自动化对象类型的COM库.容器对象保存内容对象的列表,而内容对象持有对其容器的弱引用.

以下两种情况进行了测试和正常工作(弱引用设置为null并释放内存):

>具有接口和CoClasses的单个COM库.
>两个COM库,一个具有接口,另一个与CoClasses

但是,当我将coclasses放在两个独立的库中时,代码会产生“无效的类型转换”,当删除[weak]属性时,该错误消失.请原谅这个奇怪的样本,其目的只是为了使问题变得最小,不能作为标准编码习惯

这是第一个定义两个接口的库.ridl文件,同时定义了CoClass
容器:

[
  uuid(E1EE3651-A400-49BF-B5C5-006D9943B9C0),version(1.0)

]
library DelphiIntfComLib
{

  importlib("stdole2.tlb");

  interface IMyContainer;
  interface IMyContent;
  coclass MyContainer;


  [
    uuid(A7EF86F7-40CD-41EE-9DA1-4D9B7B24F06B),helpstring("Dispatch interface for MyContainer Object"),dual,oleautomation
  ]
  interface IMyContainer: IDispatch
  {
    [id(0x000000C9)]
    HRESULT _stdcall Add([in] IMyContent* AMyContent);
  };

  [
    uuid(BFD6D976-8CEF-4264-B95A-B5DA7817F6B3),helpstring("Dispatch interface for MyContent Object"),oleautomation
  ]
  interface IMyContent: IDispatch
  {
    [id(0x000000C9)]
    HRESULT _stdcall SetWeakReferenceToContainer([in] IMyContainer* AContainer);
  };

  [
    uuid(1F56198B-B1BE-4E11-BC78-0E6FF8E55214)
  ]
  coclass MyContainer
  {
    [default] interface IMyContainer;
  };

};

这是我的容器实现

unit Unit1;

{$WARN SYMBOL_PLATFORM OFF}

interface

uses
  ComObj,ActiveX,DelphiIntfComLib_TLB,StdVcl,Generics.Collections;

type
  TMyContainer = class(TAutoObject,IMyContainer)
  private
     FList: TList<IMyContent>;
  protected
    procedure Add(const AMyContent: IMyContent); safecall;
  public
    Destructor Destroy; override;

    procedure Initialize; override;

  end;

implementation

uses ComServ;

procedure TMyContainer.Add(const AMyContent: IMyContent);
begin
  FList.Add(AMyContent);
  AMyContent.SetWeakReferenceToContainer(self);
end;

destructor TMyContainer.Destroy;
begin
  FList.Free;
  inherited;
end;

procedure TMyContainer.Initialize;
begin
  inherited;
  FList := TList<IMyContent>.create;
end;

initialization
  TAutoObjectFactory.Create(ComServer,TMyContainer,Class_MyContainer,ciMultiInstance,tmApartment);
end.

我的第二个库引用第一个,只包含我的内容界面的CoClass

[
  uuid(65659EE4-1949-4112-88CA-F2D5B5D8DA2C),version(1.0)

]
library DelphiImplComLib
{

  importlib("stdole2.tlb");
  importlib("DelphiIntfComLib.dll");

  coclass MyContent;


  [
    uuid(79D1669A-8EB6-4AE6-8F4B-91137E6E6DC1)
  ]
  coclass MyContent
  {
    [default] interface IMyContent;
  };

其实施与弱参考

unit Unit2;

{$WARN SYMBOL_PLATFORM OFF}

interface

uses
  ComObj,DelphiImplComLib_TLB,DelphiIntfComLib_TLB;

type
  TMyContent = class(TAutoObject,IMyContent)
  private
   [Weak] //If included will cause "invalid class typecast" error
    FContainer : IMyContainer;
  protected
    procedure SetWeakReferenceToContainer(const AContainer: IMyContainer); safecall;
  end;

implementation

uses ComServ;

procedure TMyContent.SetWeakReferenceToContainer(const AContainer: IMyContainer);
begin
  FContainer := AContainer;
end;

initialization
  TAutoObjectFactory.Create(ComServer,TMyContent,Class_MyContent,tmApartment);
end.

我测试如下

program Project13;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,DelphiImplComLib_TLB in 'implDelphiImplComLib_TLB.pas',DelphiIntfComLib_TLB in 'IntfDelphiIntfComLib_TLB.pas';

var
  GMyContainer : IMyContainer;
  GMyContent : IMyContent;
begin
  GMyContainer := CoMyContainer.Create;
  GMyContent := CoMyContent.Create;
  GMyContainer.Add(GMyContent);
end.

为什么我在分割实现时遇到错误?如何缓解这个问题?

解决方法

正如Allen Bauer在答案中解释的那样,[weak]不支持COM接口,因为它们不能保证由Delphi TObject派生类支持,这对于[weak]引用在对象被自动删除时是必需的被释放RTL在运行时跟踪弱引用,但不能跟踪库之间的弱引用,除非RTL库的单个实例在它们之间共享(即,如果您使用启动运行时软件包编译库,然后使用可执行文件部署RTL BPL ).

但是,只要不需要使用[weak]的自动修改功能,可以使用无类型的指针:

type
  TMyContent = class(TAutoObject,IMyContent)
  private
    FContainer : Pointer{IMyContainer};
    ...
  end;

只要您需要使用其方法/属性,就必须将FContainer类型转换为IMyContainer,例如:

IMyContainer(FContainer).Add(...);

在10.1及更高版本中,您可以使用[unsafe]属性:

type
  TMyContent = class(TAutoObject,IMyContent)
  private
    [Unsafe] FContainer : IMyContainer;
    ...
  end;

正如马可博客中提到的:

Weak and Unsafe Interface References in Delphi 10.1 Berlin

What if the object has a standard reference count implementation and you want to create an interface reference that is kept out of the total count of references? You can now achieve this by adding the [unsafe] attribute to the interface variable declaration,changing the code above to:

06003

Not that this is a good idea,as the code above would cause a memory leak. By disabling the reference counting,when the variable goes out of scope nothing happens. There are some scenarios in which this is beneficial,as you can still use interfaces and not trigger the extra reference. In other words,an unsafe reference is treated just like… a pointer,with no extra compiler support.

Now before you consider using the unsafe attribute for having a reference without increasing the count,consider that in most cases there is another better option,that is the use of weak references. Weak references also avoid increasing the reference count,but they are managed. This means that the system keeps track of weak references,and in case the actual object gets deleted,it will set the weak reference to nil. With an unsafe reference,instead,you have no way to know the status of the target object (a scenario called dangling reference).

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读