CLR profiler: преобразование в COM-стиле и вызов функций из производных интерфейсов

У меня есть необходимость разработать специализированный CLR-профилировщик. Профилировщики CLR должны быть реализованы как реализация COM-сервера ICorProfilerCallback или более новая версия в настоящее время до 5. Инициализация Profiler происходит в методе обратного вызова Initialize(IUnknown* pICorProfilerInfoUnk), Это дает возможность сделать QueryInterface на предоставленной IUnknown возражать и получать указатели на ICorProfilerInfo интерфейсы. Начиная с .NET 4.5, есть ICorProfilerInfo, ICorProfilerInfo2, ICorProfilerInfo3, а также ICorProfilerInfo4, с каждой новой версией, обеспечивающей дополнительную функциональность. В идеале я хотел бы получить указатель на последнюю доступную версию и позволить виртуальным таблицам выяснить, что такое реальный объект.

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(...)
}

Любой совет о том, как это можно сделать, был бы очень признателен.

2

Решение

1) это нормально для этих типов интерфейса, они были созданы для того, чтобы всегда наследовать предыдущую версию. Таким образом, V-таблица ICorProfilerInfo4 включает в себя все из методов 3 предыдущих версий. Это, конечно, не всегда так для COM-интерфейсов, но работает здесь. Опасно, если вызов метода ICorProfilerInfo4 при получении интерфейса ICorProfilerInfo3 приведет к сбою вашей программы. Компилятор не поможет вам избежать неприятностей.

2) нет оператора IS в C ++, вы можете сделать это в COM, вызвав QueryInterface () снова. Или вы можете просто установить переменную, которая указывает, какую версию интерфейса вы получили. Использование QI позволяет избежать сбоя, когда вы неправильно проверяете версию и делает Позвольте компилятору помочь вам получить правильный код.

Я бы порекомендовал вам сначала каталогизировать функции профилировщика, которые вам действительно нужны. Вы рискуете добавить слишком большую гибкость, такую, которая заставляет вас писать код, который вы никогда не будете использовать, и отправлять программу, которая не была полностью протестирована. Различия между FunctionEnter2 и FunctionEnter3 невелики, однако оба будут работать просто отлично, и вы вряд ли заметите оптимизацию.

2

Другие решения

Других решений пока нет …