Проблема с хранением COM-указателей в глобальном одноэлементном объекте

StackOverflow https://stackoverflow.com/questions/1885021

Вопрос

Предыстория

Приложение, с которым я работаю, имеет несколько COM-библиотек DLL.

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

(Побочное примечание: Это особенно важно при регистрации отдельной библиотеки DLL, поскольку глобальные объекты будут создаваться внутри regsvr32 процесс, и я не хочу, чтобы DLL пыталась получить интерфейс к другой DLL во время этого процесса.)

Например, мой метод ленивой инициализации будет делать что-то вроде этого:

CComPtr<IMyOtherObject>&
CGlobalSingleton::
GetMyOtherObject()
{
    // SNIP: Other code removed for clarity...

    if (! m_pMyOtherObject)
    {
        hr = pUnknown->QueryInterface(IID_IMyOtherObject,
            (void**) &m_pMyOtherObject);
    }

    return m_pMyOtherObject;
}

ПРИМЕЧАНИЕ: m_pMyOtherObject является переменной -членом CComPtr Тип.

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

Проблема

Что я заметил, так это то, что при некоторых обстоятельствах я получаю неудачные утверждения, когда мое приложение завершает работу.Однако, если я изменю свой код на вызов QueryInterface() каждый время, которое мне нужно для доступа IID_IMyOtherOBject (вместо того, чтобы хранить его как переменную-член) это предотвращает выполнение утверждений.

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

Мое понимание CComPtr класс (который я использую) заключается в том, что он избавляет от многих головных болей, связанных с решением пожизненных проблем (т.е.зовущий AddRef() и Release()).Но, похоже, в моем случае это не работает.

Кто-нибудь может определить, что я, возможно, делаю неправильно?

Это было полезно?

Решение

Вместо того чтобы внедрять свой собственный глобальный синглтон, посмотрите на использование Глобальный интерфейс , доступный вместо этого используйте интерфейс.Это синглтон, который предоставляется операционной системой на уровне процесса.Любая из ваших библиотек DLL может помещать свои COM-объекты в таблицу, а другие библиотеки DLL могут восстанавливать их при необходимости.Все, что вам нужно было бы реализовать со своей стороны, - это способ для библиотек DLL обмениваться файлами cookie таблицы DWORD друг с другом.

Другие советы

Вы возвращаете ссылку на интеллектуальный указатель, который, возможно, не увеличивает количество ссылок.Извините, я бы проверил, но здесь уже поздно.Это моя догадка, и она соответствует тому, что вы описываете - загляните в конструкторы копирования для CComPtr.

Надеюсь, это поможет,

K

Дикий удар ножом в темноте:Возможно ли , что CGlobalSingleton может быть уничтожен после CoUninitialize() называется, при каких обстоятельствах?Если бы это было так, и m_pMyOtherObject следовательно, был также уничтожен после неинициализации COM, это был бы еще один способ вызвать нарушение доступа, о котором упоминал Игорь.

Я подозреваю, что проблема заключается в вашем понимании семантики копирования / присваивания класса CComPtr;Я не особенно знаком с CComPtr, но, по моему опыту, умные указатели, как правило, работают не так, как вы могли бы от них ожидать.Сначала вам следует прочитать документацию для CComPtr и убедиться, что вы понимаете, как это работает (также не помешало бы взглянуть на исходный код).Вы также могли бы попробовать установить некоторые точки останова в элементах AddRef() и Release() CComPtr, чтобы посмотреть, что происходит во время и после вызова GetMyOtherObject(), особенно если вы временно сохраняете возвращаемое значение и оно выходит за пределы области видимости.

Звучит как m_pMyOtherObject все еще работает, когда вы закрываете свое приложение.В дополнение к проблемам с конструктором копирования m_pMyOtherObject должен либо быть CComPtr или CGlobalSingleton должен позвонить m_pMyOtherObject's Release способ уничтожения.

Отредактировано для наглядности.

Редактировать Просто провел быстрый тест и не столкнулся с какими-либо проблемами при использовании функции, возвращающей ссылку на CComPtr.Хотя это немного необычно, это не вызвало никаких проблем с подсчетом ссылок.

Однако я хотел бы подробнее рассказать о том, что произойдет, если m_pMyOtherObject это не умный указатель.В этом случае он никогда не будет выпущен.Позвольте мне показать вам, почему:

  1. Вы вызываете QueryInterface по некоторому указателю.Он вызовет AddRef для этого объекта.
  2. Вы возвращаете либо CComPtr& CComPtr&, либо голый указатель интерфейса.Это в значительной степени не имеет значения.Операции подсчета ссылок не выполняются (если только вы не присвоите возвращаемое значение другому CComPtr, который добавит к нему ссылку.Но поскольку этот CComPtr уравновесит его вызовом Release, это не имеет значения).
  3. В итоге вы получаете либо 1 вызов AddRef и 0 для Release, либо 2 вызова AddRef и 1 для Release.Другими словами, они несбалансированы, и у вас утечка ссылок.

Чтобы избежать этого, вам нужно структурировать свою программу следующим образом:

class CGlobalSingleton{

CComPtr<IMyOtherObject> m_spMyOtherObject;

IMyOtherObject* GetMyOtherObject()
{
    // SNIP: Other code removed for clarity...

    if (! m_spMyOtherObject)
    {
        //pUnknown gets AddRef'ed, but that's OK, m_spMyOtherObject will call release when CGlobalSingleton goes out of scope
        hr = pUnknown->QueryInterface(IID_IMyOtherObject,
            (void**) &m_spMyOtherObject);
    }

    return m_pMyOtherObject;
}
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top