Pergunta

I have a need to develop a specialized CLR profiler. CLR profilers must be implemented as a COM server implementing ICorProfilerCallback or a newer version currently up through 5. Profiler initialization occurs in a callback method Initialize(IUnknown* pICorProfilerInfoUnk). This gives one an opportunity to do a QueryInterface on the provided IUnknown object and obtain pointers to the ICorProfilerInfo interfaces. As of .NET 4.5, there are ICorProfilerInfo, ICorProfilerInfo2, ICorProfilerInfo3, and ICorProfilerInfo4, with each new version providing additional functionality. Ideally I would like to obtain a pointer to the latest available version and let the vtables figure out what the real object is.

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

Notice that all cases, the pointer to the returned interface is the same variable m_pICorProfilerInfo, which is of type CComQIPtr<ICorProfilerInfo>. I then call methods on it oblivious of the actual type of the object implementing the method.

This leads me to two questions:

  1. In COM / ATL context, is it safe to retrieve derived interfaces, store them in a parent interface such as above, and then call functions from it?
  2. A parent interface obviously has no knowledge of functions in derived interfaces. How can I check if the pointer IS A derived interface (such as ICorProfilerInfo2) and cast it to such?

In testing so far, #1 generally seems okay. But I would would prefer confirmation or advise. I am much more uncertain about point #2. For example, ICorProfilerInfo has a SetEnterLeaveFunctionHooks function while ICorProfilerInfo2 has a SetEnterLeaveFunctionHooks2 function. I would like to do something like the following pseudo code:

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

Any advice on how can this be accomplished would be most appreciated.

Foi útil?

Solução

1) is okay for these interface types, they were crafted to always inherit the previous version. So the v-table of ICorProfilerInfo4 includes all of the methods of the 3 previous versions. This is certainly not necessarily always the case for COM interfaces but works here. It is dangerous, calling an ICorProfilerInfo4 method when you obtained an ICorProfilerInfo3 interface will crash your program. You get no help from the compiler to keep you out of trouble.

2) there's no IS operator in C++, you can do it in COM by calling QueryInterface() again. Or you could just set a variable that indicates which version of the interface you obtained. Using QI avoids the crash when you get the version checking wrong and does allow the compiler to help you get the code right.

I would recommend you first catalogue the profiler functions you actually need. You're in danger of adding too much flexibility, the kind that make you write code you'll never use and ship a program that isn't fully tested. The differences between FunctionEnter2 and FunctionEnter3 are not subtle, both will however work just fine and you're pretty unlikely to notice the optimization.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top