Delphi 中 Form 分发与其生命周期相关的接口对象的安全方法?

发布于 2024-12-09 03:53:24 字数 312 浏览 0 评论 0原文

我有一个 Delphi 表单,它提供接口对象背后的功能,代码的其他部分也通过属于该表单的属性获取引用。我无法将接口功能委托给子对象,因为太多的功能是由表单上的控件/组件提供的。我无法使用 TAggregateObject 或 TContainedObject 链接传递给 Form 的接口对象的生命周期,因为 TForm 类不从 TinterfacedObject 继承,并且 Delphi 不支持多重继承,因此我无法将 TInterfacedObject 混合到继承链中。如果表单被销毁,而其他一些代码保存着由表单传递的接口引用之一,则这种情况可能会导致访问冲突。有人能想出一个好的解决方案来解决这个问题吗?

I have a Delphi Form that provides the functionality behind an interface object that other parts of the code get references too via a property belonging to the Form. I can't delegate the interface functionality to a child object because too much of that functionality is serviced by controls/components on the form. I can't use TAggregatedObject or TContainedObject to link the lifetime of the interfaced objects being passed around to the Form because the TForm class does not inherit from TinterfacedObject and Delphi does not support multiple inheritance so I can't mix in TInterfacedObject into the inheritance chain. This situation can lead to access violations if a Form gets destroyed while some other code holds one of the interface references passed out by the Form. Can anyone think of a good solution to this problem?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

栖迟 2024-12-16 03:53:25

您可以将接口委托给子对象,只需让该对象包含指向表单的内部指针,以便它可以在需要时访问表单的控件,这与您现在已经做的没有什么不同。

您可以根据需要使用 TAggregateObjectTContainedObject。它们不需要从 TInterfacedObject 派生 Form。它们所需要的只是一个 IInterface 接口指针,而 TComponent 派生自 IInterface(并覆盖 _AddRef()_Release() 以禁用引用计数),因此您可以将表单本身(作为 TComponent 后代)作为所需的 IInterface 指针传递。

这就剩下唯一的问题了——当活动接口引用被其他代码持有时,表单关闭。最简单的解决方案是 1) 重写该代码,以便在窗体关闭时不保留这些引用,或者 2) 在释放这些引用之前不允许窗体关闭。

You can delegate the interface to a child object, just have that object contain an internal pointer to the Form so it can access the Form's controls when needed, no different then you are already doing right now.

You can use TAggregateObject or TContainedObject for your needs. They do not require the Form to derive from TInterfacedObject. All they do require is an IInterface interface pointer, and TComponent derives from IInterface (and overrides the _AddRef() and _Release() to disable reference counting), so you can pass the Form itself (being a TComponent descendant) as the required IInterface pointer.

That leaves the only issue remaining - the Form closing while active interface references are being held by other code. The simpliest solution is to either 1) rewrite that code to not hold on to those references while the Form is closing, or 2) don't allow the Form to close until those references have been released.

生生漫 2024-12-16 03:53:25

注意:只有当您的使用者也是从 TComponent 派生时,这才有效。

为了避免死引用,您可以从表单查询 IInterfaceComponentReference(每个 TComponent 都可用),在该接口上调用 GetComponent 并将自己附加到 FreeNotification返回的组件/表单的代码>。

现在发生的情况是:当表单被销毁时,它将通过调用消费者上的 Notification 方法(将其自身(表单)作为 AComponentAComponent)来通知所有“监听者”它将销毁自己。 code> 和 opRemove 作为操作。从而允许您将接口引用归零。
但要注意对象引用和接口引用不能相等。
另外,请确保在不再需要通知时调用 RemoveFreeNotification 以避免不必要的调用。

TSomeConsumer = class(TComponent)
private
  FInterfaceToAService: ISomeInterface;        
protected
  procedure Notification(AComponent: TComponent; Operation: TOperation); override;
public
  procedure SetService(const Value: ISomeInterface); 
end;

procedure TSomeConsumer.Notification(AComponent: TComponent; Operation: TOperation);
begin
  inherited;
  if (Operation = opRemove) and (AComponent = TObject(FInterfaceToAService)) then
    SetService(nil); // Takes care of niling the interface as well.
end;

procedure TSomeConsumer.SetService(const Value: ISomeInterface);
var
  comRef: IInterfaceComponentReference;
begin
  if Supports(FInterfaceToAService, IInterfaceComponentReference, comRef) then
    comRef.GetComponent.RemoveFreeNotification(self);

  FInterfaceToAService := Value;

  if Supports(FInterfaceToAService, IInterfaceComponentReference, comRef) then
    comRef.GetComponent.FreeNotification(self);
end;

Note: This will only work, if your consumer is also derived from TComponent.

To avoid the dead references you can query the IInterfaceComponentReference (available on every TComponent) from your form, call GetComponent on that interface and attach yourself to the FreeNotification of the returned Component/Form.

What happens now is: When the Form gets destroyed it will notify all "listners" that its going to destroy itself by calling the Notification method on the consumer with itself (form) as AComponent and opRemove as operation. Thus allowing you to nil your interface reference.
But be aware that the object references and interface references must not be equal.
Also make sure to call RemoveFreeNotification when you don't need the Notification any more to avoid unnecessary calls.

TSomeConsumer = class(TComponent)
private
  FInterfaceToAService: ISomeInterface;        
protected
  procedure Notification(AComponent: TComponent; Operation: TOperation); override;
public
  procedure SetService(const Value: ISomeInterface); 
end;

procedure TSomeConsumer.Notification(AComponent: TComponent; Operation: TOperation);
begin
  inherited;
  if (Operation = opRemove) and (AComponent = TObject(FInterfaceToAService)) then
    SetService(nil); // Takes care of niling the interface as well.
end;

procedure TSomeConsumer.SetService(const Value: ISomeInterface);
var
  comRef: IInterfaceComponentReference;
begin
  if Supports(FInterfaceToAService, IInterfaceComponentReference, comRef) then
    comRef.GetComponent.RemoveFreeNotification(self);

  FInterfaceToAService := Value;

  if Supports(FInterfaceToAService, IInterfaceComponentReference, comRef) then
    comRef.GetComponent.FreeNotification(self);
end;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文