CreateStdDispatch 如何知道调用什么方法?

Ian*_*oyd 5 com winapi idispatch native-code

我面临着实现一个IDispatch接口。有四种方法,幸运的是其中 3 种方法很简单:

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

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

function TIEEventsSink.GetIDsOfNames(...): HResult;
{
   Result := E_NOTIMPL;
}
Run Code Online (Sandbox Code Playgroud)

这是最后一种方法,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;
Run Code Online (Sandbox Code Playgroud)

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

我在以下的 MSDN 文档中IDispatch.Invoke找到了这个片段:

通常,您不应直接实现Invoke

优秀!我无论如何都不想实施它!继续阅读:

相反,使用调度接口来创建函数CreateStdDispatchDispInvoke。有关详细信息,请参阅CreateStdDispatchDispInvoke创建 IDispatch 接口公开 ActiveX 对象

创建IDispatch接口链接说:

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

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

很好,CreateStdDispatch是:

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

HRESULT CreateStdDispatch(  
  IUnknown FAR*  punkOuter,        
  void FAR*  pvThis,               
  ITypeInfo FAR*  ptinfo,          
  IUnknown FAR* FAR* ppunkStdDisp  
);
Run Code Online (Sandbox Code Playgroud)

我打算把它称为:

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
);
Run Code Online (Sandbox Code Playgroud)

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

怎么会CreateStdDispatch知道

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

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

Jam*_*ass 5

ITypeInfo传入的参数不是CreateStdDispatch暴露了所有的方法信息吗?

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

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


Dar*_*zka 2

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

您返回的对象仅存储您传递给 的参数CreateStdDispatch(),并且对于所有IDispatch方法,它仅返回并对ITypeInfo您提供的 进行相应的调用。就这些。

ptinfo如果您像代码中所示传递 nil ,那么您只能得到 ,因为如果没有将所有工作委托给其,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由 Delphisafecall在实现方法上的关键字引发的编译器魔法。

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

只需将 Delphi 调试器切换到反汇编(“CPU 视图”)即可检查编译器为您所做的所有魔力!