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

В моем приложении я подключаю различные функции для создания 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.Мне может понадобиться отслеживать активные итераторы и, как только последний из них закончится, удалить все нулевые указатели из списка.Или что-то в этом роде.

Ответы [ 3 ]

4 голосов
/ 25 мая 2011

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

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

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

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

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

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


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

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

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

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

Это не гарантируется, однако: как MSDN состояния:

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

(выделение добавлено)

Но это, очевидно, то, что вы здесь делаете.

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

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

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

1 голос
/ 21 июля 2017

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

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

1 голос
/ 25 мая 2011

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

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

...