我面临着实施IDispatch
界面的问题。有四种方法,幸运的是其中有三种方法很简单:
function TIEEventsSink.GetTypeInfoCount(...): HResult;
{
Result := E_NOTIMPL;
}
function TIEEventsSink.GetTypeInfo(...): HResult;
{
Result := E_NOTIMPL;
}
function TIEEventsSink.GetIDsOfNames(...): HResult;
{
Result := E_NOTIMPL;
}
这是最后一种方法,Invoke
很难。在这里,我面临着必须实际处理 DispID ,并调用我的适当方法;来自变量数组的unmarhsalling参数。
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
上找到了这个snippit:
通常,您不应直接实施调用。
出色!我还是不想实现它!继续阅读:
相反,使用调度接口创建函数CreateStdDispatch和DispInvoke。有关详细信息,请参阅CreateStdDispatch,DispInvoke,Creating the IDispatch Interface和Exposing ActiveX Objects。
Creating the IDispatch Interface链接说:
您可以通过以下任何方式实施IDispatch:
- [剪断]
- 调用CreateStdDispatch函数。这种方法最简单,但它不提供丰富的错误处理或多种国家语言。
- [剪断]
很好,CreateStdDispatch是:
通过单个函数调用创建IDispatch接口的标准实现。这简化了通过Automation公开对象。
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
,但它不是。
答案 0 :(得分:4)
传递到ITypeInfo
的{{1}}参数不会公开所有方法信息吗?
因此,您首先调用CreateStdDispatch
创建类型信息并将其传递给CreateDispTypeInfo
,然后可以使用类型信息来确定调用哪个方法,因为CreateStdDispatch
需要CreateDispTypeInfo
1}}包含所有这些信息
我可能错了,因为我没有时间去研究它,但这对我来说是有道理的。 我稍后会对此进行调查并更新答案。
答案 1 :(得分:1)
对您的问题的简短回答是:CreateStdDispatch()
和它创建的 IDispatch
实现都对要调用的方法一无所知。
您返回的对象只是存储您传递给 CreateStdDispatch()
的参数,并且对于所有 IDispatch
方法,它只会翻转并在您调用的 ITypeInfo
上进行相应的调用给它。仅此而已。
如果您为 ptinfo
传递 nil,如您的代码所示,那么您只会得到 E_INVALIDARG
,因为如果没有 ITypeInfo
来委托所有工作。
如果您检查 oleaut32.dll 中 CStdDisp
的代码,您会发现它调用了 DispInvoke()
之类的 API 函数(也存在于该 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 转换为异常也是如此。编译器只是在每次调用安全调用方法后插入对 @CheckAutoResult
的调用;此函数检查 HRESULT 并在适当时抛出 EOleSysError
。
只需将 Delphi 调试器切换到反汇编(“CPU 视图”),即可检查编译器为您所做的所有魔法!