Позвольте мне начать с того, что я ОЧЕНЬ неопытен с работой COM, но мне было поручено отладить проблему для кого-то другого.
У меня есть два COM-проекта с именами pvTaskCOM и pvFormsCOM, и у каждого есть много интерфейсов, но я обеспокоен следующими двумя:
ITaskActPtr, который находится в pvTaskCOM
IChartingObjectPtr, который находится в pvFormsCOM
Строка кода, вызывающая мою проблему:
ITaskActPtr pTaskAct = m_pChartObj;
Где m_pChartObj — это IChartingObjectPtr. Проблема, с которой я столкнулся, состояла в том, что pTaskAct был НЕДЕЙСТВИТЕЛЕН после этого назначения в одном рабочем процессе, но хорошо в большинстве других рабочих процессов. Я погрузился в то, что здесь происходит, используя отладчик, и обнаружил, что он просматривает неправильные записи COM во время QueryInterface. В рабочих процессах, которые работают нормально, QueryInterface извлекает записи из pvTaskCOM / pvTaskAct.h:
BEGIN_COM_MAP(CTaskAct)
COM_INTERFACE_ENTRY(ITaskAct)
.
.
.
END_COM_MAP()
Который содержит интерфейс, который я пытаюсь привести, и QueryInterface возвращает S_OK.
Но в этом другом рабочем процессе m_pChartObj создается таким же образом, но QueryInterface по какой-то странной причине заглядывает внутрь pvFormsCOM / ChartingObject.h
BEGIN_COM_MAP(CChartingObject)
COM_INTERFACE_ENTRY(IChartingObject)
.
.
.
END_COM_MAP()
который НЕ содержит ITaskAct, к которому мы пытаемся привести, и поэтому QueryInterface возвращает E_NOINTERFACE.
Вопрос, который у меня возник, заключается в том, что может заставить его искать два разных COM-кода для одной и той же строки кода? Это какая-то проблема наследования? Мне просто нужен шаг в правильном направлении.
В рабочих процессах, которые работают нормально,
QueryInterface
получает записи из pvTaskCOM / pvTaskAct.h
Так не должно быть.
Эта строка:
ITaskActPtr pTaskAct = m_pChartObj;
Делает это под капотом:
ITaskAct *pTaskAct = NULL;
m_pChartObj->QueryInterface(IID_ITaskAct, (void*)&pTaskAct);
Это спрашивает IChartingObject
реализует объект, если он поддерживает ITaskAct
интерфейс, и если так, чтобы вернуть указатель на эту реализацию. Так что этот код должен смотреть только на записи COM_MAP
для CChartingObject
учебный класс. Не стоит смотреть на CTaskAct
класс вообще.
Но в этом другом рабочем процессе
m_pChartObj
создается таким же образом, ноQueryInterface
по какой-то странной причине заглядывает внутрь pvFormsCOM / ChartingObject.h
Это правильное поведение, так как именно здесь CChartingObject
на самом деле реализовано. Если нет записи для ITaskAct
в COM_MAP
из CChartingObject
тогда правильное поведение CChartingObject::QueryInterface()
потерпеть неудачу с E_NOINTERFACE
ошибка.
Итак, настоящая проблема заключается в том, что ваши «рабочие» рабочие процессы на самом деле несовершенны, а ваш «неработающий» рабочий процесс делает правильные вещи.
что может заставить его искать два разных COM-кода для одной и той же строки кода? Это какая-то проблема наследования?
Нет. «Рабочие» рабочие процессы повреждены, просты и понятны. призвание QueryInterface()
на IChartingObject
интерфейс должен вызывать CChartingObject::QueryInterface()
, но это явно зовет CTaskAct::QueryInterface()
вместо. Так что либо
IChartingObject*
указатель на самом деле указывает на CTaskAct
объект вместо CChartingObject
объект
что-то испортило память и IChartingObject
vtable является ничего не подозревающей жертвой.
Я бы заподозрил первое. Итак, в «рабочих» рабочих процессах убедитесь, что IChartingObject*
указатель на самом деле указывает на правильный объект. Похоже, кто-то взял ITaskAct*
и набрал его на IChartingObject*
без использования QueryInterface()
, Или они позвонили QueryInterface()
на каком-то объекте и попросил его IID_ITaskAct
вместо IID_IChartingObject
но затем сохранил возвращенный указатель в IChartingObject*
указатель вместо ITaskAct*
указатель.
Вы, вероятно, немного заблудились в сантехнике. Это код C ++, который должен был сделать COM немного менее драконовским. Важным аспектом COM является то, что клиентский код работает только с интерфейсы. Он ничего не знает о объекты. Интерфейс — это простой контракт, список функций, которые вы можете вызвать. Например, IChartingObject будет иметь функцию Paint (). У ITaskAct не было бы никакой реальной идеи, что-то «тасккое», функция Schedule ().
Обратите внимание, что m_pChartObj — довольно обманчивое имя. Он хранит указатель интерфейса, а не объект. Но нередко, указатель интерфейса легко представить как указатель объекта, если объект реализует только один интерфейс или имеет «доминирующий» интерфейс, который вы будете использовать все время. Сокрытие объекта внутри серверного кода является очень важной целью в COM, вы можете только когда-либо делать вызовы интерфейса.
Так что ITaskActPtr pTaskAct = m_pChartObj; в основном объявляет: «У меня есть график, я хочу сделать вызов функции задач дальше». Нравится Расписание (). Для этого COM должен спросить реализацию объекта диаграммы «знаете ли вы что-нибудь о контракте интерфейса задачи?». Неизбежно он должен обратиться обратно к серверу, в карте интерфейса для CChartingObject, откуда появился IChartingObject, чтобы увидеть, реализует ли он также ITaskAct.
То, что вы видите, происходит совершенно нормально. Ответ — нет».