Подтвердить что ты не робот

Как я могу поддерживать слабую ссылку на COM-объект в С++?

В моем приложении я подключаю различные функции для создания COM-объектов (таких как CoCreateInstanceEx), чтобы получать уведомления, когда какой-либо объект создано. Я отслеживаю все созданные объекты в std::list, и я повторяю этот список, чтобы делать разные вещи (например, проверить, какой OLE объекты были активированы).

Проблема с этим заключается в том, что прямо сейчас, добавляя указатель IUnknown к моему списку, я вызываю IUnknown::AddRef, чтобы убедиться, что он не будет уничтожен, пока я его отслеживаю. Это не то, что я действительно хочу; время жизни объекта должно быть таким же длинным (или коротким), как и без моего кода трассировки, поэтому я бы предпочел сохранить слабую ссылку на объектах. Всякий раз, когда последняя ссылка на какой-то отслеживаемый COM-объект удаляется (и, следовательно, объект уничтожается), я хотел бы получить уведомление, чтобы я мог обновить мою учетную запись (например, установив указатель в моем списке на NULL). *

Какой лучший способ сделать это? Прямо сейчас я исправляю (первый) VTable всех созданных объектов, чтобы уведомления о IUnknown::Release через первый vtable получили уведомление. Однако это не будет работать для интерфейсов COM, которые наследуют от нескольких интерфейсов (и, следовательно, имеют несколько vtables), но я не уверен, действительно ли это проблема: с учетом Правила реализации QueryInterface, всегда должен быть только один IUnknown, возвращенный IUnknown::QueryInterface, правильно? Поэтому я мог бы это сделать, а затем исправить эту таблицу vtable.

Кроме того, этот подход также немного волосатый, поскольку он включает создание thunks, которые генерируют некоторый код. Я только реализовал это для 32 бит до сих пор. Не большая проблема, но все же.

Мне действительно интересно, нет ли более элегантного способа иметь слабую ссылку на COM-объект. Кто-нибудь знает?

*: Следующее, что мне нужно решить, - это правильно выполнить эту работу, если у меня есть активные итераторы (я использую пользовательские объекты итератора), проходящие по списку объектов COM. Мне может потребоваться отслеживать активные итераторы, и как только последний закончен, удалите все нулевые указатели из списка. Или что-то в этом роде.

4b9b3361

Ответ 1

Это не ответ, как набор проблем, почему это очень сложно сделать - я помещаю его в качестве ответа, поскольку здесь слишком много информации, чем подходит в комментарии:)

Я понимаю, что понятие слабой ссылки просто не существует в COM-периоде. У вас есть подсчет ссылок через IUnknown и что общая сумма того, как COM имеет дело с управлением жизненным циклом объекта. Все, кроме этого, строго говоря, не COM.

(Net поддерживает концепцию, но у нее есть фактический менеджер памяти на основе GC для обеспечения соответствующей поддержки и может обрабатывать объекты WeakRef по-другому, чем обычные ссылки в памяти. Но это не так с очень простым миром, что COM предполагает, что это мир простой памяти и указателей, и немного больше.)

COM указывает, что подсчет ссылок для каждого интерфейса; любой COM-объект может свободно выполнять подсчет ссылок на объект в качестве удобства, но результатом является то, что если вы обертываете объект, вы должны принять наиболее ограничительный случай. Поэтому вы не можете предположить, что любой данный IUnknown будет использоваться для всех addrefs/release на этом объекте: вам действительно нужно будет отслеживать каждый интерфейс отдельно.

Канонический IUnknown - тот, который вы возвращаете QI'ing для IUnknown - может быть любым интерфейсом вообще - даже выделенный IUnknown, который используется только для того, чтобы действовать как личность! - до тех пор, пока одно и то же значение двоичного указателя возвращается каждый раз. Все остальные интерфейсы могут быть реализованы любым способом; обычно одно и то же значение возвращается каждый раз, но COM-объект может законно возвращать новый IFoo каждый раз, когда кто-то QI для IFoo. Или даже держите кеш в IFoos и возвращайте случайным образом.

... и тогда у вас есть агрегация, с которой нужно иметь дело - в принципе, у COM нет четкой концепции объекта вообще, все о интерфейсах. Объекты в COM - это всего лишь набор интерфейсов, которые используют один и тот же канонический IUnknown: они могут быть реализованы как один объект C/С++ за кулисами или как семейство связанных объектов C/С++, представляющих собой фасад "один COM-объект".


Сказав все это, учитывая, что:

Я отслеживаю состояние различных компонентов (включая все COM-объекты) этого программного обеспечения ради отладки

Здесь альтернативный подход, который может дать некоторые полезные данные для отладки с помощью.

Идея здесь заключается в том, что многие реализации COM-объектов возвращают значение ref в качестве возвращаемого значения Release() - поэтому, если они возвращают 0, то это означает, что интерфейс, возможно, был выпущен.

Однако это не гарантируется: MSDN:

Метод возвращает новый счетчик ссылок. Это значение предназначено для использования только в целях тестирования.

(выделено мной)

Но это, по-видимому, то, что вы здесь делаете.

Итак, одна вещь, которую вы могли бы сделать, предполагая, что у вас есть код вызова, заключается в замене вызовов с помощью Release() с помощью строки MyRelease() или аналогичной, которая будет вызывать выпуск, и если она замечает, что возвращаемое значение равно 0, затем замечает, что теперь указатель интерфейса освобожден - удаляет его из таблицы, записывает в файл и т.д.

Одно важное предостережение: имейте в виду, что у COM нет понятия слабого ref, даже если вы пытаетесь что-то взломать. Использование указателя COM-интерфейса, который не был AddRef() 'd, является незаконным в отношении COM; поэтому, если вы сберете значения указателя интерфейса в любом списке, единственное, что вы должны сделать с ними, - рассматривать их как непрозрачные числа для целей отладки (например, записывать их в файл, чтобы вы могли коррелировать создание с помощью деструкций или отслеживать сколько у вас выдающихся), но не пытайтесь использовать их в качестве фактических указателей интерфейса.

Опять же, имейте в виду, что ничто не требует, чтобы объект COM выполнял соглашение о возврате refcount; поэтому имейте в виду, что вы можете видеть что-то похожее на ошибку, но на самом деле просто реализация Release просто происходит, всегда возвращается 0 (или rand(), если вам особенно не повезло!)

Ответ 2

Во-первых, вы правы, что QueryInterface для IUnknown всегда должен возвращать тот же указатель; IUnknown рассматривается как идентификатор объекта IIRC, поэтому он должен быть стабильным.

Что касается слабых указателей, от верхней части головы, возможно, вы могли бы дать CoMarshalInterThreadInterfaceInStream вихрь? Он предназначен для того, чтобы вы могли сериализовать ссылку на COM-объект в поток, а затем создать новую ссылку на объект в другом потоке с использованием потока. Тем не менее, если вы сериализуете поток и сохраняете поток как своего рода слабый указатель, то позже, чтобы восстановить указатель, вы можете проверить, не сработает ли unmarshalling; Если это так, объект ушел.

Ответ 3

С WinRT IWeakReference был добавлен, чтобы включить слабые ссылки на COM-объекты. Объекты, созданные с помощью WRL RuntimeClass, поддерживают IWeakReference по умолчанию (можно отключить с помощью опции).

вы можете использовать IWeakReference в своих проектах, но это означает, что вам нужно будет использовать хотя бы некоторые концепции WinRT, интерфейс на основе Inspectable.