【问题标题】:How does CreateStdDispatch know what method to invoke?CreateStdDispatch 如何知道调用什么方法?
【发布时间】:2011-09-24 09:56:06
【问题描述】:

我面临着实现IDispatch 接口。有四种方法,幸好其中三种很简单:

function TIEEventsSink.GetTypeInfoCount(...): HResult;
{
   Result := E_NOTIMPL;
}

function TIEEventsSink.GetTypeInfo(...): HResult;
{
   Result := E_NOTIMPL;
}

function TIEEventsSink.GetIDsOfNames(...): HResult;
{
   Result := E_NOTIMPL;
}

这是最后一种方法,Invoke,这很难。在这里,我不得不实际处理 DispID,并调用我的适当方法;从变体数组中解组参数。

function Invoke(  
  dispIdMember: DISPID;
  riid: REFIID;
  lcid: LCID;
  wFlags: WORD;
  var pDispParams: DISPPARAMS;
  var pVarResult: VARIANT;
  var pExcepInfo: EXCEPINFO;
  var puArgErr: DWORD
): HRESULT;

不想编写所有乏味的样板代码,我肯定会有错误,我去谷歌搜索 - 而不是做任何工作。

我在the MSDN Documentation of IDispatch.Invoke 上找到了这个片段:

通常,您不应直接实现 Invoke

太棒了!反正我也不想实施!继续阅读:

改为使用调度接口创建函数CreateStdDispatchDispInvoke。详情请参考CreateStdDispatchDispInvokeCreating the IDispatch InterfaceExposing ActiveX Objects

Creating the IDispatch Interface 链接说:

您可以通过以下任何方式实现 IDispatch:

  • [剪辑]
  • 调用CreateStdDispatch 函数。这种方法是最简单的,但它不提供丰富的错误处理或多种国家语言。
  • [剪辑]

太好了,CreateStdDispatch 是:

通过单个函数调用创建 IDispatch 接口的标准实现。这简化了通过自动化暴露对象的过程。

HRESULT CreateStdDispatch(  
  IUnknown FAR*  punkOuter,        
  void FAR*  pvThis,               
  ITypeInfo FAR*  ptinfo,          
  IUnknown FAR* FAR* ppunkStdDisp  
);

我打算这样称呼它:

CreateStdDispatch(
    myUnk,          //Pointer to the object's IUnknown implementation.
    anotherObject,  //Pointer to the object to expose.
    nil             //Pointer to the type information that describes the exposed object (i has no type info)
    dispInterface   //the IUnknown of the object that implements IDispatch for me
);

我无法弄清楚CreateStdDispatch 的 Windows API 实现如何知道在我的对象上调用什么方法 - 特别是因为 CreateStdDispatch 不知道我正在使用什么面向对象的语言,或者它的调用约定.

CreateStdDispatch 怎么知道

  • 为给定的dispid 调用什么方法?
  • 我的语言的调用约定?
  • 如何处理编写面向对象对象的语言的异常?

注意:我别无选择,只能实现dispinterface;我没有定义the interface。我希望这是一个简单的早期绑定IUnknown,但事实并非如此。

【问题讨论】:

  • 你不只是从TAutoIntfObject 派生出来还是我遗漏了什么?
  • 如果 那些 确实是您对其他三种方法的实现,那么您也不必真正实现Invoke,因为没有消费者会走得足够远无论如何都要调用它。消费者将调用GetIDsOfNames 以找出它想要调用的方法或属性的dispid,但您将响应它没有实现。如果没有 dispid,消费者无法填写第一个 Invoke 参数。此外,文档并没有说E_NOTIMPL 甚至是GetIDsOfNames 的有效结果。
  • 如果问题中没有关于 delphi 的任何内容,为什么这会被标记为“delphi”? Delphi 从未被提及,所有显示的代码都是 C。
  • @Rob Kennedy:不正确。这是一个调度接口,打电话给我的人(Internet Explorer)已经知道它想要调用的dispIDs。请参阅 Techvanguard 的自动化事件接收 (techvanguards.com/products/eventsinkimp) 的优秀示例。
  • @Thorsten Engler:我将代码示例更改为具有 Delphi 风格。

标签: winapi com native-code idispatch


【解决方案1】:

传递给CreateStdDispatchITypeInfo 参数不是暴露了所有的方法信息吗?

因此,您首先要创建类型信息,调用CreateDispTypeInfo 并将其传递给CreateStdDispatch,然后它可以使用类型信息来确定调用哪个方法,因为CreateDispTypeInfo 需要包含所有这些信息的INTERFACEDATA

我可能错了,因为我没有时间研究它,但这对我来说很有意义。 我稍后会对此进行调查并更新答案。

【讨论】:

  • 就是这样。 Ian,CreateStdDispatch 知道关于您的对象的所有内容,因为您在向其提供 ITypeInfo 对象时告诉了该信息。如果你没有给它一个,那么你还没有完成你的项目。
  • @Ian 为什么你在 Delphi 自带 IDispatch 时尝试编写自己的实现?
  • @David Heffernan:德尔福对IDispatch的实现在哪里?
  • 就像我在对 Q 的评论中所说的那样,从 TAutoIntfObject 派生。我自己从未做过,但这是我对应该如何做的理解。我认为这是要走的路,但我不能 100% 确定我理解你的目标。
  • @Rob Kennedy:我现在看到了,ITypeInfo::AddressOfMember,在那里我返回了我班级方法的地址。虽然我不知道如何获取类方法的地址,但它肯定回答了我的问题,“CreateStdDispatch”如何知道如何调用我的方法?”答案是我给它函数的地址(大概相对于对象的开始)。
【解决方案2】:

您的问题的简短回答是:CreateStdDispatch() 和它创建的 IDispatch 实现都不知道任何事情关于要调用的方法。

您返回的对象只是存储您传递给CreateStdDispatch() 的参数,并且对于所有IDispatch 方法,它只会转身并在您提供给它的ITypeInfo 上进行相应的调用。就是这样。

如果您将 nil 传递给 ptinfo,如您的代码中所示,那么您只会得到 E_INVALIDARG,因为如果没有将所有工作委托给的 ITypeInfo,实现对象根本无法执行任何操作。

如果您检查 oleaut32.dll 中 CStdDisp 的代码,您会发现它调用 API 函数,如 DispInvoke()(也存在于该 DLL 中),而不是直接调用 ITypeInfo 方法,但这些函数都是调用ITypeInfo 方法的简单包装器,没有任何进一步的功能。

如果有人想知道:CreateStdDispatch()CStdDisp 都不会执行任何额外的魔法;他们所做的就是给你一个IDispatch,它可以做你传入的ITypeInfo 可以做的任何事情。将其视为一种适配器,可让您将ITypeInfo 插入IDispatch 套接字。

TAutoIntfObject.Create() 确实需要一个类型库。但是,构造函数所做的只是在其上调用GetTypeInfoOfGuid() 以获取类型信息指针,然后对象将与调度相关的大部分工作委托给该指针。

Borland 在他们的智慧中为类型信息指针 private 制作了成员变量,这意味着您确实需要将一些类型库或其他包含相关接口的构造函数交给构造函数,而不是简单地编写另一个构造函数或覆盖一些虚函数。另一方面,通过注册表加载类型库或将其部分转储到 TLB 文件应该不会太难。使用 OleView 检查 TLB 会为您提供实际可编译的 IDL,这通常也是 Borland 可编译的 RIDL。

CreateStdDispatch() 也对异常一无所知。捕获从 COM 方法抛出的异常并将它们转换为 HRESULT 和/或 IErrorInfo 是由 Delphi 在实现方法上的 safecall 关键字引起的编译器魔法。

在调用接口声明中指定为 safecall 的 COM 方法时,将 HRESULT 转换为异常也是如此。编译器只是在每次调用 safecall 方法后插入对@CheckAutoResult 的调用;此函数检查 HRESULT 并在适当时抛出 EOleSysError

只需将 Delphi 调试器切换到反汇编(“CPU 视图”),即可检查编译器为您执行的所有功能!

【讨论】:

    猜你喜欢
    • 2011-06-25
    • 2015-03-08
    • 2016-10-13
    • 1970-01-01
    • 2021-12-08
    • 2020-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多