У меня есть необходимость разработать специализированный 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>
, Затем я вызываю методы, не обращая внимания на фактический тип объекта, реализующего метод.
Это приводит меня к двум вопросам:
ICorProfilerInfo2
) а бросить это на такое?В тестировании пока # 1, как правило, кажется, все в порядке. Но я бы предпочел подтверждение или совет. Я гораздо более неуверен в пункте № 2. Например, ICorProfilerInfo
имеет SetEnterLeaveFunctionHooks
функция в то время как ICorProfilerInfo2
имеет SetEnterLeaveFunctionHooks2
функция. Я хотел бы сделать что-то вроде следующего псевдокода:
if (m_pICorProfilerInfo IS ICorProfilerInfo2)
{
((ICorProfilerInfo2) m_pICorProfilerInfo)->SetEnterLeaveFunctionHooks2(...)
}
else
{
m_pICorProfilerInfo->SetEnterLeaveFunctionHooks(...)
}
Любой совет о том, как это можно сделать, был бы очень признателен.
1) это нормально для этих типов интерфейса, они были созданы для того, чтобы всегда наследовать предыдущую версию. Таким образом, V-таблица ICorProfilerInfo4 включает в себя все из методов 3 предыдущих версий. Это, конечно, не всегда так для COM-интерфейсов, но работает здесь. Опасно, если вызов метода ICorProfilerInfo4 при получении интерфейса ICorProfilerInfo3 приведет к сбою вашей программы. Компилятор не поможет вам избежать неприятностей.
2) нет оператора IS в C ++, вы можете сделать это в COM, вызвав QueryInterface () снова. Или вы можете просто установить переменную, которая указывает, какую версию интерфейса вы получили. Использование QI позволяет избежать сбоя, когда вы неправильно проверяете версию и делает Позвольте компилятору помочь вам получить правильный код.
Я бы порекомендовал вам сначала каталогизировать функции профилировщика, которые вам действительно нужны. Вы рискуете добавить слишком большую гибкость, такую, которая заставляет вас писать код, который вы никогда не будете использовать, и отправлять программу, которая не была полностью протестирована. Различия между FunctionEnter2 и FunctionEnter3 невелики, однако оба будут работать просто отлично, и вы вряд ли заметите оптимизацию.
Других решений пока нет …