CreateStdDispatch 如何知道要调用哪个方法?
我面临着实施 IDispatch
界面。有四种方法,幸运的是其中 3 个很简单:
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;
不想编写所有繁琐的样板代码,我确信会有错误,所以我去谷歌搜索 - 而不是做任何工作。
我在IDispatch.Invoke
的MSDN文档中找到了这个片段:
一般来说,您不应该直接实现Invoke。
出色的!反正我不想实施它!继续阅读:
相反,使用调度接口创建函数 CreateStdDispatch 和 < a href="http://msdn.microsoft.com/en-us/library/ms221366.aspx" rel="nofollow">DispInvoke。有关详细信息,请参阅 CreateStdDispatch、DispInvoke,创建 IDispatch 接口 和 公开 ActiveX 对象。
创建 IDispatch Interface 链接显示:
您可以通过以下任一方式实现 IDispatch:
- [剪断]
- 调用 CreateStdDispatch 函数。这种方法是最简单的,但它不提供丰富的错误处理或多种国家语言。
- [剪断]
非常好,CreateStdDispatch 它是:
通过单个函数调用创建 IDispatch 接口的标准实现。这简化了通过自动化公开对象的过程。
HRESULT CreateStdDispatch( IUnknown FAR* 朋克外在, void FAR* pvThis, ITypeInfo FAR* ptinfo, IUnknown 远* 远* 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
;我没有定义接口 。我希望它是一个简单的早期绑定 IUnknown
,但事实并非如此。
i'm faced with implementing an IDispatch
interface. There are four methods, and fortunately 3 of them are easy:
function TIEEventsSink.GetTypeInfoCount(...): HResult;
{
Result := E_NOTIMPL;
}
function TIEEventsSink.GetTypeInfo(...): HResult;
{
Result := E_NOTIMPL;
}
function TIEEventsSink.GetIDsOfNames(...): HResult;
{
Result := E_NOTIMPL;
}
It's the last method, Invoke
that is difficult. Here i am faced with having to actually case the DispID, and call my appropriate method; unmarhsalling parameters from a variant array.
function Invoke(
dispIdMember: DISPID;
riid: REFIID;
lcid: LCID;
wFlags: WORD;
var pDispParams: DISPPARAMS;
var pVarResult: VARIANT;
var pExcepInfo: EXCEPINFO;
var puArgErr: DWORD
): HRESULT;
Not wanting to have to write all the tedious boilerplate code, that i'm sure will have bugs, i went googling - rather than doing any work.
i found this snippit on the MSDN Documentation of IDispatch.Invoke
:
Generally, you should not implement Invoke directly.
Excellent! i didn't want to implement it anyway! Continuing reading:
Instead, use the dispatch interface to create functions CreateStdDispatch and DispInvoke. For details, refer to CreateStdDispatch, DispInvoke, Creating the IDispatch Interface and Exposing ActiveX Objects.
The Creating the IDispatch Interface link says:
You can implement IDispatch by any of the following means:
- [snip]
- Calling the CreateStdDispatch function. This approach is the simplest, but it does not provide for rich error handling or multiple national languages.
- [snip]
Excellent, CreateStdDispatch it is:
Creates a standard implementation of the IDispatch interface through a single function call. This simplifies exposing objects through Automation.
HRESULT CreateStdDispatch( IUnknown FAR* punkOuter, void FAR* pvThis, ITypeInfo FAR* ptinfo, IUnknown FAR* FAR* ppunkStdDisp );
i was going to call it as:
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
);
What i cannot figure out is how the Windows API implemention of CreateStdDispatch
knows what methods to call on my object - especially since CreateStdDispatch
doesn't know what object-oriented language i'm using, or its calling conventions.
How will CreateStdDispatch
know
- what method to call for a given
dispid
? - the calling convention of my language?
- how to handle exceptions from the language that my object oriented object is written in?
Note: i have no choice but to implement a dispinterface
; i didn't define the interface. i wish it was a simple early bound IUnknown
, but it tisn't.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
传递给
CreateStdDispatch
的ITypeInfo
参数是否公开了所有方法信息?因此,您需要首先调用
CreateDispTypeInfo
创建类型信息,并将其传递给CreateStdDispatch
,然后后者可以使用类型信息来确定自CreateDispTypeInfo< 以来要调用哪个方法。 /code> 需要 INTERFACEDATA ,其中包含所有这些信息,
我可能是错误的,因为我没有时间研究它,但这对我来说是有意义的。
我稍后会对此进行调查并更新答案。
Doesn't the
ITypeInfo
parameter passed intoCreateStdDispatch
expose all of the method information?So you'd create type info first calling
CreateDispTypeInfo
and pass that through toCreateStdDispatch
which can then use the type information to work out which method to call sinceCreateDispTypeInfo
requiresINTERFACEDATA
which contains all this informationI could be way wrong since I don't have time to look into it but that would make sense to me.
I'll investigate this later and update the answer.
您的问题的简短答案是:
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 到异常的转换也是如此。编译器只是在每次调用 safecall 方法之后插入对 @CheckAutoResult 的调用;该函数检查 HRESULT 并在适当的情况下抛出
EOleSysError
。只需将 Delphi 调试器切换到反汇编(“CPU 视图”)即可检查编译器为您所做的所有魔力!
The short answer to your question is: neither
CreateStdDispatch()
nor theIDispatch
implementation it creates knows anything at all about the methods to be called.The object that you get back simply stores the parameters that you passed to
CreateStdDispatch()
, and for allIDispatch
methods it only turns around and makes the corresponding calls on theITypeInfo
that you gave it. That is all.If you pass nil for
ptinfo
as shown in your code then you only getE_INVALIDARG
, since the implementing object cannot do anything at all without anITypeInfo
to which to delegate all the work.If you inspect the code for
CStdDisp
in oleaut32.dll then you will find that it calls API functions likeDispInvoke()
(which also live in that DLL) instead of invoking theITypeInfo
methods directly, but these functions are all simple wrappers for calls to theITypeInfo
methods, without any further functionality.In case anyone wonders: neither
CreateStdDispatch()
norCStdDisp
performs any additional magic; all they do is give you anIDispatch
that does whatever theITypeInfo
that you passed in can do. Think of it as a kind of an adapter that allows you to plug anITypeInfo
into anIDispatch
socket.It is true that
TAutoIntfObject.Create()
needs a type library. However, all that the constructor does is callGetTypeInfoOfGuid()
on it in order to get a type info pointer, to which the object then delegates most of the work related to dispatch things.Borland in their wisdom made the member variable for the type info pointer
private
, which means that you really need to hand the constructor some type library or other that contains the interface in question, instead of simply writing another constructor or overriding some virtual function. On the other hand it shouldn't be too hard to load the type library via the registry or to dump parts of it to a TLB file. Inspecting a TLB with OleView gives you actual compilable IDL which is often also Borland-compilable RIDL.CreateStdDispatch()
does not know anything about exceptions either. The catching of exceptions thrown from COM methods and their conversion to HRESULT and/orIErrorInfo
is compiler magic induced by Delphi'ssafecall
keyword on the implementing method.The same goes for the translation of HRESULTs to exceptions when calling COM methods specified as safecall in their interface declarations. The compiler simply inserts a call to
@CheckAutoResult
after every invocation of a safecall method; this function checks the HRESULT and throwsEOleSysError
if appropriate.Simply switch the Delphi debugger to disassembly ('CPU view') to inspect all the magic that the compiler does for you!