【问题标题】:How to cast Interfaces with VCL components in C++Builder如何在 C++Builder 中使用 VCL 组件转换接口
【发布时间】:2018-02-08 13:15:46
【问题描述】:

此问题中接受的答案显示了如何在 Delphi 中检查和使用 VCL 派生对象上的接口。

How to use Interface with VCL Classes - Part 2

procedure Test;
var
  O1: TSomeThing;
  Intf: ISomething;
begin
  O1 := TSomeThing.Create(nil);

  if Supports(O1, ISomething, Intf) then
  begin
    Intf.DoSomething;
  end;

基本上,我想在 C++Builder 中做同样的事情,但还没有弄清楚如何使用 C++ 中的“支持”。

在使用 VCL 派生类时尝试使用 <dynamic_cast> 在编译时失败...

TSomeThing * O1;
ISomething *i = dynamic_cast<ISomething*>(O1);  // Error: Can't cast

建议的Inheritance and Interfaces 文章提到了TObject::GetInterface(),但是当我尝试时收到错误“已调用纯虚函数”。

_di_IFoo mc;

if (this->GetInterface(mc)) ... 

更新:首先,我添加接口的对象是现有的 VCL 控件,因此不是从 TInterfacedObject 派生的。

第二个 - 不涉及 COM - 我希望! Interfaces 的使用纯粹是为了让我可以使用接口的概念与 C++Builder(至少在 2010 年)不支持的 VCL 组件进行多重继承。

所以我的界面看起来像这样(注意没有__addref/__release 等...):

 __interface INTERFACE_UUID("{C527B88F-3F8E-1134-80e0-01A04F57B270}") IMyInterface : public IInterface
 {
 public:
     virtual UTF8String getHello() = 0;
 };

我的对象看起来像这样

class TMyPanel: public TPanel, IMyInterface
{
    ...
public:
    UTF8String getHello() { return "Hello from a TMyPanel";}
    ...
};

class TMyLabel: public TLabel, IMyInterface
{
    ...
public:
    UTF8String getHello() { return "Hello from a TMyLabel";}
    ...
};

这很简单,正如 Embarcadero 接口文档中所述。

但是,如何判断一个特定的TObject 是否支持IMyInterface???

以下模板函数为我执行此操作,基于来自system.pasTObject.GetInterfaceEntry()

template<typename T>
T* getInterface(TObject *obj)
{
    T *intf = NULL;

    PInterfaceEntry interfaceEntry = obj->GetInterfaceEntry(__uuidof(T) );
    if (interfaceEntry && interfaceEntry->IOffset != 0)
    {
        intf = (T*)(((char *)obj) + interfaceEntry->IOffset);
    }

    return intf;
}

我们这样使用它:

IMyInterface *myIf = getInterface<IMyInterface>(aRandomTObject);
if (myIf)
{
    UTF8String s = myIf->getHello();
}

如果有比这更好的方法,请告诉我,因为 VTable/Pointer spelunking 让我的牙齿发痒......

【问题讨论】:

  • @J... 也许我很笨,需要用勺子喂食,但我之前读过那个页面,找不到一个例子来说明如何确定一个对象是否支持特定的界面。
  • @J... 不,对象类型在编译时是未知的,并且 - 因为我正在向 VCL 控件添加接口,所以它们不是从 TInterfacedObject 派生的,所以没有 Supports() .我有一个对我有用的“答案”——但感觉很可怕。我会把它贴出来看看谁喝醉了。
  • 您不需要TInterfacedObject 来使用Supports()。它可以查询TObject 的接口表,然后枚举它以查找请求的接口。
  • @RemyLebeau - 我明白了 - 我可以使用 ::Supports() 但不能使用 this-&gt;Supports() ??

标签: delphi interface c++builder


【解决方案1】:

您在 C++ 中的操作方式与在 Delphi 中相同 - 通过 Sysutils::Supports() 函数。

当我尝试时,这对我有用:

//---------------------------------------------------------------------------

#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
#include <Vcl.ExtCtrls.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:    // IDE-managed Components
    void __fastcall FormClick(TObject *Sender);
private:    // User declarations
    TPanel *p;
    TLabel *l;
    void Test();
public:     // User declarations
    __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm7 *Form1;
//---------------------------------------------------------------------------
__interface INTERFACE_UUID("{C527B88F-3F8E-1134-80e0-01A04F57B270}") IMyInterface : public IInterface
{
public:
    virtual UTF8String getHello() = 0;
};

#if !defined(INTFOBJECT_IMPL_IUNKNOWN)
#define INTFOBJECT_IMPL_IUNKNOWN(BASE) \
    ULONG   __stdcall AddRef() { return BASE::_AddRef();} \
    ULONG   __stdcall Release(){ return BASE::_Release();} \
    HRESULT __stdcall QueryInterface(REFIID iid, void** p){ return BASE::QueryInterface(iid, p);} 
#endif

class TMyPanel : public TPanel, public IMyInterface
{
    INTFOBJECT_IMPL_IUNKNOWN(TPanel)
public:
    __fastcall TMyPanel(TComponent *Owner) : TPanel(Owner) {}
    UTF8String getHello() { return "Hello from a TMyPanel"; }
};

class TMyLabel : public TLabel, public IMyInterface
{
    INTFOBJECT_IMPL_IUNKNOWN(TLabel)
public:
    __fastcall TMyLabel(TComponent *Owner) : TLabel(Owner) {}
    UTF8String getHello() { return "Hello from a TMyLabel"; }
};
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
    p = new TMyPanel(this);
    p->Parent = this;

    l = new TMyLabel(this);
    l->Parent = p;
    l->Caption = L"Test";
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormClick(TObject *Sender)
{
    Test();
}
//---------------------------------------------------------------------------
void TForm1::Test()
{
    DelphiInterface<IMyInterface> Intf;

    if (Supports(p, __uuidof(IMyInterface), (void*)&Intf))
    {
        UTF8String s = Intf->getHello();
        ShowMessage(s);
        Intf.Release();
}

    if (Supports(l, __uuidof(IMyInterface), (void*)&Intf))
    {
        UTF8String s = Intf->getHello();
        ShowMessage(s);
        Intf.Release();
    }
}
//---------------------------------------------------------------------------

【讨论】:

  • “调用纯虚函数”。我认为这最终会调用我真的不想要或不需要的 AddRef 和 Release。
  • 你无法避免使用 Delphi 风格的接口进行引用计数,但 TComponent 禁用了实际的引用计数(它将 AddRef()Release() 覆盖为无操作)。无论如何,我在 C++ 中测试了代码,它对我来说运行良好,这意味着您可能没有在您的项目中正确实现它。我用我测试过的确切代码更新了我的答案。
  • 谢谢。我正在使用 C++Builder 2010。没有INTFOBJECT_IMPL_IUNKNOWN,我想这会增加一个 QueryInterface 方法...?
  • INTFOBJECT_IMPL_IUNKNOWN 在 XE 中添加。根据this documentation,它是实现IUnknown 以将其方法调用转发到IInterface 实现的辅助宏。 IInterface 是 Delphi 对 IUnknown 的实现,但在 C++ 中,IInterface 方法签名与 IUnknown 方法签名不同,因此您必须实现 IUnknown 方法以避免在编译时出现抽象错误。我在给你的回答中添加了INTFOBJECT_IMPL_IUNKNOWN 的声明
  • 谢谢,我现在可以正常工作了,我们仍然需要引用计数样板,这很烦人,但我想这就是继续使用 RTL 调用的代价。需要权衡好处与我原来使用起来感觉更清洁的“解决方案”。
猜你喜欢
  • 2013-04-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-17
  • 2022-12-16
  • 2013-04-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多