CLR 探查器:COM 样式的强制转换和从派生接口调用函数

CLR profiler: COM-style casting and calling functions from derived interfaces

本文关键字:转换 派生 接口 函数 调用 探查 COM 样式 CLR      更新时间:2023-10-16

我需要开发一个专门的CLR探查器。CLR 探查器必须实现为实现 ICorProfilerCallback 的 COM 服务器或当前到 5 的较新版本。探查器初始化发生在回调方法Initialize(IUnknown* pICorProfilerInfoUnk) 中。这使人们有机会对提供的IUnknown对象执行QueryInterface并获取指向ICorProfilerInfo接口的指针。从.NET 4.5开始,有ICorProfilerInfoICorProfilerInfo2ICorProfilerInfo3ICorProfilerInfo4,每个新版本都提供了额外的功能。理想情况下,我想获得指向最新可用版本的指针,并让 vtables 弄清楚真正的对象是什么。

if (FAILED(pICorProfilerInfoUnk->QueryInterface(IID_ICorProfilerInfo4, (LPVOID*)&m_pICorProfilerInfo)))
{
    if (FAILED(pICorProfilerInfoUnk->QueryInterface(IID_ICorProfilerInfo3, (LPVOID*)&m_pICorProfilerInfo)))
    {
        if (FAILED(pICorProfilerInfoUnk->QueryInterface(IID_ICorProfilerInfo2, (LPVOID*)&m_pICorProfilerInfo)))
        {
            if (FAILED(pICorProfilerInfoUnk->QueryInterface(IID_ICorProfilerInfo, (LPVOID*)&m_pICorProfilerInfo)))
            {
                AtlTrace(TEXT("[Initialize] Failed to retrieve any ICorProfilerInfo~ interface."));
                return S_FALSE;
            }
        }
    }
}

请注意,在所有情况下,指向返回接口的指针都是同一个变量 m_pICorProfilerInfo ,其类型为 CComQIPtr<ICorProfilerInfo> 。然后,我调用它的方法,而不考虑实现该方法的对象的实际类型。

这让我想到两个问题:

  1. 在 COM/ATL 上下文中,检索派生接口,将它们存储在父接口中(如上所示),然后从中调用函数是否安全?
  2. 父接口显然不了解派生接口中的函数。如何检查指针是否是派生接口(例如ICorProfilerInfo2)并将其转换为派生接口?

在到目前为止的测试中,#1 通常看起来还可以。但我更喜欢确认或建议。我对#2点更加不确定。例如,ICorProfilerInfo 具有SetEnterLeaveFunctionHooks函数,而ICorProfilerInfo2具有SetEnterLeaveFunctionHooks2函数。我想做一些类似于以下伪代码的事情:

if (m_pICorProfilerInfo IS ICorProfilerInfo2)
{
    ((ICorProfilerInfo2) m_pICorProfilerInfo)->SetEnterLeaveFunctionHooks2(...)
}
else
{
    m_pICorProfilerInfo->SetEnterLeaveFunctionHooks(...)
}

关于如何做到这一点的任何建议将不胜感激。

1) 对于这些接口类型是可以的,它们被精心设计为始终继承以前的版本。 因此,ICorProfilerInfo4 的向量表包括了 3 个先前版本的所有方法。 对于COM接口来说,这当然不一定总是如此,但在这里有效。 这是危险的,当您获得ICorProfilerInfo3接口时调用ICorProfilerInfo4方法会使程序崩溃。 你不会从编译器那里得到任何帮助来让你远离麻烦。

2)C++中没有IS运算符,您可以通过再次调用QueryInterface()在COM中执行此操作。 或者,您可以设置一个变量来指示您获得的接口版本。 使用 QI 可以避免在版本检查错误时崩溃,并允许编译器帮助您正确获取代码。

我建议您首先对实际需要的分析器功能进行编目。 你面临着增加太多灵活性的危险,这种灵活性会让你编写你永远不会使用的代码,并发布一个未经充分测试的程序。 FunctionEnter2 和 FunctionEnter3 之间的差异并不微妙,但是两者都可以正常工作,您不太可能注意到优化。