【问题标题】:Understanding C++Builder xxx_TLB.h files了解 C++Builder xxx_TLB.h 文件
【发布时间】:2012-12-20 21:24:03
【问题描述】:

我已经使用 c++Builder 2010(组件/导入组件/类型库向导)导入了一个类型库,并试图了解生成的 xxx_TLB.b 和 xxx_OCX.h 文件中定义的类和类型。 (问题的一些背景信息 - 我试图让我的 COM 对象和方法按名称绑定,而不是按 DISPID - see this question

假设库包含一个类 Foo,我会得到以下 GUID

const GUID IID_IFoo = {0xF1EC45FE, 0x2540, 0x4188,{ 0xAE, 0x14, 0xD8,0x4F, 0x65, 0x6F,0x7A, 0x00} };
const GUID CLSID_Foo = {0xDD1C416D, 0xD8A2, 0x4BBC,{ 0x8E, 0xA8, 0x1A,0x10, 0x77, 0xA4,0x30, 0x0C} };

这对我来说很有意义。还生成了 FooEvents,但我暂时忽略它。我还获得了以下接口和 typedef,每种方法都有两个版本 - 一个“原始”版本,一个带有更友好的包装器,隐藏 HRESULTS

interface DECLSPEC_UUID("{F1EC45FE-2540-4188-AE14-D84F656F7A00}") IFoo;
typedef TComInterface<IFoo, &IID_IFoo> IFooPtr;

interface IFoo  : public IDispatch 
{
  virtual HRESULT STDMETHODCALLTYPE get_Bar(BSTR* Value/*[out,retval]*/) = 0; // [201]
  ...
  BSTR __fastcall get_Bar(void)
  {
    BSTR Value = 0;
    OLECHECK(this->get_Bar((BSTR*)&Value));
    return Value;
  }
}

现在我们来看看我无法理解的东西:另外两个基本公开相同功能但不同的类。

template <class T /* IFoo*/ >
class TCOMIFooT : public TComInterface<IFoo>, public TComInterfaceBase<IUnknown>
{
... again, two versions of each method are provided.
}
typedef TCOMIFooT<IFoo> TCOMIFoo;

template<class T>
class IFooDispT : public TAutoDriver<IFoo>
{
... again, two more versions of each method are provided.
}
typedef IFooDispT<IFoo> IFooDisp;

而且情况变得更糟:在 xxx_OCX.h 文件中,我们发现了这个附加类:-

class PACKAGE TFoo : public Oleserver::TOleServer
{
  IFooPtr m_DefaultIntf;
  _di_IUnknown __fastcall GetDunk();
public:
  ... this tim, just one version of each method
}

所以,对于 Foo 类型的对象,我似乎有四个不同的类来表示它——我应该使用哪一个,什么时候使用?

  IFooPtr f;
  IFooDisp f;
  TCOMIFoo f;
  TFoo *f;

最后,对于IFooDisp f;,我似乎可以通过f-&gt;Bar()f.Bar() 调用Bar() - 它们的工作方式相同,还是有一些细微的差别?

【问题讨论】:

  • 看来您不仅要求使用 COM 服务器,还要求实现它。也许在巫师中迷失了一些? IFooPtr 是您使用 COM 服务器所需要的一切。
  • @HansPassant - 呵呵。好吧,上面的输出纯粹来自使用导入类型库向导生成的 _TLB.h 和 _OCX.h。我勾选的唯一选项是“生成组件包装器” - 错了吗?

标签: com activex c++builder


【解决方案1】:

所以,对于 Foo 类型的对象,我似乎有四个不同的类来表示它

TComInterface 是一个包装类,用于为您管理接口指针的引用计数,很像 ATL 的 CComPtrCComQIPtr 类。这样,您就不必手动调用AddRef()Release()

TCOMIFoo 包装器基本上只是 TComInterface&lt;IFoo&gt; 的 typedef,尽管在同一文件中创建的 TCoClassCreator 派生类也使用它。

TAutoDriver 是一个包装类,用于处理IDispatch 和后期绑定(按名称绑定成员所需要的)。

TOleServer 是一个包装类,用于将 COM 对象公开为 VCL 组件,特别是它可以在设计时放置在 TFormTFrameTDataModule 对象上。

我应该使用哪一个,什么时候使用?

我从未在我的代码中使用TAutoDriver 包装器,并且在设计时不使用 COM 对象(因为如果未安装 COM 对象,它们不允许您进行运行时错误处理)。对于大多数代码,我会坚持使用IFooPtrTCOMIFoo,它们基本上是一样的。

最后,对于 IFooDisp f;看来我可以通过 f->Bar() 和 f.Bar() 调用 Bar() - 它们的工作方式相同,还是有一些细微的差别?

有细微的差别。

f-&gt;Bar()直接调用IFoo::Bar()方法,因此你必须自己做HRESULT检查,如果发生错误,你可以决定如何处理。

f.Bar() 调用包装器IFooDisp::Bar() 方法,该方法调用IFoo::Bar() 方法并为您执行HRESULT 检查,如果发生错误则引发异常。

一般情况下,通过-&gt; 运算符调用的任何方法都将直接调用原始接口,而通过. 运算符调用的任何方法将调用包装器方法。

如果Bar() 返回一个接口指针,则另一个细微的区别是。使用 -&gt; 运算符,您必须对结果指针进行自己的引用计数,例如:

ISomeIntf i;
if (SUCCEEDED(f->Bar(&i)))
{
    ...
    i->Release();
}

除非您使用TComInterface 变量来接收它:

TComInterface<ISomeIntf> i; // or ISomeIntfPtr
if (SUCCEEDED(f->Bar(&i)))
{
    ...
    // Release()'d automatically
}

改用. 运算符,包装器通常会返回TComInterface 而不是原始接口指针。当接口链接在一起时,这尤其有用,您希望获得一个返回返回...的接口的接口,等等。使用-&gt; 操作符更灵活,但不如使用. 操作符干净,例如:

TComInterface<ISomeIntf> i; // or ISomeIntfPtr
if (SUCCEEDED(f->get_SomeIntf(&i)))
{
    TComInterface<IOtherIntf> j; // or IOtherIntfPtr
    if (SUCCEEDED(i->get_OtherIntf(&j)))
    {
        j->DoSomething();
    }
}

对比:

f.SomeIntf.OtherIntf.DoSomething();

【讨论】:

  • 谢谢。这绝对为我指明了正确的方向。对于基于名称的绑定,我将不得不更多地探索 TAutoDriver,看看是否能找到一个好的示例。
猜你喜欢
  • 2011-11-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-06
相关资源
最近更新 更多