5

我面临着实现一个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;

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

在 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  
);

我打算称它为:

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
);

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

怎么会CreateStdDispatch知道

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

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

4

2 回答 2

5

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

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

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

于 2011-06-28T16:43:50.203 回答
1

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

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

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

如果您检查CStdDispoleaut32.dll 中的代码,那么您会发现它调用 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 视图”)即可检查编译器为您所做的所有魔法!

于 2021-04-21T19:04:39.030 回答