为什么我可以将 COM 对象转换为错误的接口?
Why can I cast a COM object to a wrong interface?
我有一个 .NET 程序,它与来自另一个进程的 mshtml 对象进行交互。我从头写了一个小 sample project 来说明这个问题。在此示例中,我直接使用 COM 引用来实现 mshtml 互操作。
HTMLDocument document = Document;
IHTMLElement activeElement = document.activeElement;
Log.Verbose(activeElement.tagName);
bool isHtmlFrameElement = activeElement is HTMLFrameElement;
Log.Verbose("active Element is " + (isHtmlFrameElement ? "" : "NOT ") + "a frame element");
我引用了一个自定义的 mshtml,它是通过以下调用生成的:
tlbimp c:\Windows\System32\mshtml.tlb /out:c:\tmp\Interop.mshtml.dll
在我的开发机器(安装了 Office)上,我得到了这个日志,这是预期的行为:
INPUT
active Element is NOT a frame element
但是在 naked 机器上,没有安装 office(也没有 mshtml interop),我得到以下日志:
INPUT
active Element is a frame element
当然它不是 HTMLFrameElement
并且任何对其成员之一的访问都会导致 找不到成员 异常。
为什么 COM 在第二种情况下允许这种无效的转换?我可以使用我的互操作(在构建目录中)还是必须将它安装到 GAC(就像 MS Office 一样)?
转换没有失败,因为返回的对象是 NULL。详细说明here
简而言之,它说在转换不当时没有异常。
老实说,使用 COM 对象并不容易。
首先要正确注册dll,其次要正确描述对象。
mshtml 的 HTMLFrameElement
是由 .NET 生成的 COM coclass。实际上,简而言之,除了工具(如 tlbimp 等)之外,这并不存在。通常我们只使用它的 GUID(CLSID)来“共同创建”它。
这个 coclass 声明它实现了 DispHTMLFrameElement
,这是一个 dispinterface。同样,这是更多用于工具的元数据,它对您的情况没有实际用处。您可以在 Windows SDK 的 mshtml.h:
中看到它的声明
EXTERN_C const IID DIID_DispHTMLFrameElement;
#if defined(__cplusplus) && !defined(CINTERFACE)
MIDL_INTERFACE("3050f513-98b5-11cf-bb82-00aa00bdce0b")
DispHTMLFrameElement : public IDispatch
{
};
#else /* C style interface */
因此 .NET 在这些基础上构建了一些奇特的 类 但它们不应该用于检测您的情况下的 COM 接口支持。为什么行为因上下文而异只是意味着这些 类 的实现会有所不同。
在你的情况下,你应该检查 IHTMLFrameElement,它也在 MsHTML.h:
中定义
EXTERN_C const IID IID_IHTMLFrameElement;
#if defined(__cplusplus) && !defined(CINTERFACE)
MIDL_INTERFACE("3050f313-98b5-11cf-bb82-00aa00bdce0b")
IHTMLFrameElement : public IDispatch
{
public:
virtual /* [id][propput] */ HRESULT STDMETHODCALLTYPE put_borderColor(
/* [in] */ VARIANT v) = 0;
virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_borderColor(
/* [out][retval] */ __RPC__out VARIANT *p) = 0;
};
#else /* C style interface */
请注意 IID 相似但实际上不同,用这个进行测试确实是您想要做的:
bool isHtmlFrameElement = activeElement is IHTMLFrameElement;
我有一个 .NET 程序,它与来自另一个进程的 mshtml 对象进行交互。我从头写了一个小 sample project 来说明这个问题。在此示例中,我直接使用 COM 引用来实现 mshtml 互操作。
HTMLDocument document = Document;
IHTMLElement activeElement = document.activeElement;
Log.Verbose(activeElement.tagName);
bool isHtmlFrameElement = activeElement is HTMLFrameElement;
Log.Verbose("active Element is " + (isHtmlFrameElement ? "" : "NOT ") + "a frame element");
我引用了一个自定义的 mshtml,它是通过以下调用生成的:
tlbimp c:\Windows\System32\mshtml.tlb /out:c:\tmp\Interop.mshtml.dll
在我的开发机器(安装了 Office)上,我得到了这个日志,这是预期的行为:
INPUT
active Element is NOT a frame element
但是在 naked 机器上,没有安装 office(也没有 mshtml interop),我得到以下日志:
INPUT
active Element is a frame element
当然它不是 HTMLFrameElement
并且任何对其成员之一的访问都会导致 找不到成员 异常。
为什么 COM 在第二种情况下允许这种无效的转换?我可以使用我的互操作(在构建目录中)还是必须将它安装到 GAC(就像 MS Office 一样)?
转换没有失败,因为返回的对象是 NULL。详细说明here
简而言之,它说在转换不当时没有异常。 老实说,使用 COM 对象并不容易。 首先要正确注册dll,其次要正确描述对象。
mshtml 的 HTMLFrameElement
是由 .NET 生成的 COM coclass。实际上,简而言之,除了工具(如 tlbimp 等)之外,这并不存在。通常我们只使用它的 GUID(CLSID)来“共同创建”它。
这个 coclass 声明它实现了 DispHTMLFrameElement
,这是一个 dispinterface。同样,这是更多用于工具的元数据,它对您的情况没有实际用处。您可以在 Windows SDK 的 mshtml.h:
EXTERN_C const IID DIID_DispHTMLFrameElement;
#if defined(__cplusplus) && !defined(CINTERFACE)
MIDL_INTERFACE("3050f513-98b5-11cf-bb82-00aa00bdce0b") DispHTMLFrameElement : public IDispatch { };
#else /* C style interface */
因此 .NET 在这些基础上构建了一些奇特的 类 但它们不应该用于检测您的情况下的 COM 接口支持。为什么行为因上下文而异只是意味着这些 类 的实现会有所不同。
在你的情况下,你应该检查 IHTMLFrameElement,它也在 MsHTML.h:
中定义EXTERN_C const IID IID_IHTMLFrameElement;
#if defined(__cplusplus) && !defined(CINTERFACE)
MIDL_INTERFACE("3050f313-98b5-11cf-bb82-00aa00bdce0b") IHTMLFrameElement : public IDispatch { public: virtual /* [id][propput] */ HRESULT STDMETHODCALLTYPE put_borderColor( /* [in] */ VARIANT v) = 0; virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_borderColor( /* [out][retval] */ __RPC__out VARIANT *p) = 0; };
#else /* C style interface */
请注意 IID 相似但实际上不同,用这个进行测试确实是您想要做的:
bool isHtmlFrameElement = activeElement is IHTMLFrameElement;