Учет ссылок на объекты COM в коде DirectShow
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 03.08.19 13:30
Оценка:
Накидал простенький фильтр, в котором многие методы пока не реализованы и возвращают E_NOTIMPL. Вставляю его в граф, запускаю граф — что-то шевелится, а при остановке графа и освобождении (Release) ссылок на IGraphBuilder или IMediaControl код из quartz.dll стабильно пытается освободить ссылки на уже удаленные объекты, или лезет в память по некорректным адресам.

Несколько раз проверил учет ссылок в коде фильтра — вроде все правильно. Ссылок на IGraphBuilder и IMediaEventSink, как и положено, не освобождаю.

Как там, в коде DirectShow и конкретно в quartz.dll, с восстановлением после ошибок, когда метод фильтра/пина/сампла и прочего неожиданно возвращает E_NOTIMPL? Такое впечатление, что в этом случае код путается в учете ссылок.

Или там с этим все идеально, и надо таки искать ошибки у себя?
directshow фильтр filter pin sample ссылка reference addref release
Re: Учет ссылок на объекты COM в коде DirectShow
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 03.08.19 21:23
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

EМ>Или там с этим все идеально


Таки увы, там с этим все очень плохо. Выяснилось, что quartz.dll в одних местах проверяет результат QueryInterface, а других проверяет вместо этого адрес интерфейса в переменной. Строго говоря, это допустимо (по правилам COM, при ошибках в переменную должен записываться нуль), но это не дает возможности идентифицировать и обращения по нулевым адресам после тех запросов, где он не проверяет ничего. Честно записываешь нуль — он лезет из разных мест кода по нулевому адресу, записываешь уникальный недоступный адрес для любого отсутствующего интерфейса — начинает лезть по ним и из других мест, поскольку адреса ненулевые. Приходится последовательно менять возврат нуля на возврат недоступного адреса для отдельных интерфейсов, чтобы поймать очевидные косяки.

Такая хрень наблюдается от семерки до десятки 1903 (более новой десятки под рукой нет).

Получается, что MS, предложив свои Base Classes для DirectShow (которых я у себя не использую), спрятал за ними кривую реализацию документированных интерфейсов.
Re[2]: Учет ссылок на объекты COM в коде DirectShow
От: Videoman Россия https://hts.tv/
Дата: 05.08.19 11:20
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

1ЕМ>Получается, что MS, предложив свои Base Classes для DirectShow (которых я у себя не использую), спрятал за ними кривую реализацию документированных интерфейсов.


Какой кошмар ! Я даже добавлю, кое-где они берут ссылки на интерфейс, релизят его и потом используют. На моей памяти такая кривизна только в самих BaseClasses, тут ничего не поделаешь. С интерфейсами стороннего фильтра они работают корректно. С другой стороны, я уже много лет использую BaseClasses как основу для своих фильтров и нет никаких проблем. Нужно просто принять, что Quarz и BaseClasse, кое-где, при работе между собой срезают углы и используют недокументированные протоколы. Не вижу в этом ничего страшного. Не вижу никакого смысла в реализации своей базовой библиотеки, всегда достаточно обертки над ней. А чем вас не устраивает стандартная библиотека DirectShow, если не секрет ?
Re[3]: Учет ссылок на объекты COM в коде DirectShow
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 05.08.19 12:46
Оценка: 6 (1)
Здравствуйте, Videoman, Вы писали:

V>А чем вас не устраивает стандартная библиотека DirectShow, если не секрет ?


Не люблю фреймворков, в которых нельзя взять управление в любой ключевой точке. Когда-то пытался использовать MFC — пришлось городить хаки для перехвата некоторых сообщений, которые он предпочитал обрабатывать сам. Делал звуковые драйверы под PortCls — на борьбу с его ограничениями и косяками ушло в несколько раз больше времени, чем ушло бы на реализацию всей его функциональности своими силами.

Возможно, Base Classes этим и не страдает, но перспектива упереться в ограничение, которое нельзя изящно обойти, в финале разработки, ни разу не привлекает.
Re[4]: Учет ссылок на объекты COM в коде DirectShow
От: Videoman Россия https://hts.tv/
Дата: 05.08.19 13:08
Оценка: 12 (1)
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Не люблю фреймворков, в которых нельзя взять управление в любой ключевой точке. Когда-то пытался использовать MFC — пришлось городить хаки для перехвата некоторых сообщений, которые он предпочитал обрабатывать сам. Делал звуковые драйверы под PortCls — на борьбу с его ограничениями и косяками ушло в несколько раз больше времени, чем ушло бы на реализацию всей его функциональности своими силами.


В MFC я что-то такого не припомню, т.к. под ним лежит WinAPI и всегда, если нужно, можно было засабкласить процедуру окна и перехватить все что нужно. Про драйверы не в курсе, но догадываюсь что таких особенностей там еще больше — делайте так и никак иначе, ибо ядро.

ЕМ>Возможно, Base Classes этим и не страдает, но перспектива упереться в ограничение, которое нельзя изящно обойти, в финале разработки, ни разу не привлекает.


Конкретно про Base Classes я бы не стал переживать. Там кругом виртуальные функции которые без проблем переопределяются и, вообще, это тонкая обертка для реализации массы интерфейсов которые встречаются в DirectShow. Вот с самим Quarz — да, там намертво зашита логика, которая не всегда понятна и которую иногда хотелось бы поменять. Но все же, если вас успокоит, то на практике я реализовывая свой аналог IFilterGraph и сторонние фильтры внутри такого "виртуального" uграфа без проблем работали, так что все мало-мальски практические задачи реализуемы.
Re[5]: Учет ссылок на объекты COM в коде DirectShow
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 05.08.19 16:34
Оценка:
Здравствуйте, Videoman, Вы писали:

V>В MFC я что-то такого не припомню, т.к. под ним лежит WinAPI и всегда, если нужно, можно было засабкласить процедуру окна и перехватить все что нужно.


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

V>Про драйверы не в курсе, но догадываюсь что таких особенностей там еще больше — делайте так и никак иначе, ибо ядро.


Если б "ибо ядро". Там просто сделана стандартная обработка внешних запросов для самых общих случаев. Для частных случаев потребовались обширные пляски с бубном, чтобы поймать запросы, которые PortCls считает "своими".

V>Конкретно про Base Classes я бы не стал переживать. Там кругом виртуальные функции которые без проблем переопределяются и, вообще, это тонкая обертка для реализации массы интерфейсов которые встречаются в DirectShow.


Я уже сделал основную часть функциональности с нуля — думаю, обойдусь и без Base Classes. У меня звуковые фильтры, они относительно просты. Вот с видео я бы, наверное, побоялся делать все с нуля.

V>Вот с самим Quarz — да, там намертво зашита логика, которая не всегда понятна и которую иногда хотелось бы поменять.


Ладно б только поменять — ее же порой еще и понять хочется. Вот падает оно на обращении к нулевому указателю глубоко внутри, и как искать причину?

Подсунул вместо стандартной DLL отладочную из Checked Build. Заглянул внутрь — там довольно много отладочных сообщений, но нигде не пишут, как их включить. Пришлось подключать Process Monitor и IDA, включил всю доступную отладку, но она больше по его собственным фильтрам, а конкретно по операциям Graph Manager там немного.
Re[6]: Учет ссылок на объекты COM в коде DirectShow
От: Videoman Россия https://hts.tv/
Дата: 06.08.19 08:55
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Я уже сделал основную часть функциональности с нуля — думаю, обойдусь и без Base Classes. У меня звуковые фильтры, они относительно просты. Вот с видео я бы, наверное, побоялся делать все с нуля.


Да я ж вам говорю, прослойка настолько тонкая что она и ничем не поможет, по сути. В видео — там только с разными аллокаторами, динамическим реконектом, а также Pitch-ем для разных форматов замучаешься. По сути, Base Classes помогает собрать лишь самые простейшие примеры из SDK. Дальше все нужно самому делать. Со звуком — да, проще, если не renderer.

ЕМ>Подсунул вместо стандартной DLL отладочную из Checked Build. Заглянул внутрь — там довольно много отладочных сообщений, но нигде не пишут, как их включить. Пришлось подключать Process Monitor и IDA, включил всю доступную отладку, но она больше по его собственным фильтрам, а конкретно по операциям Graph Manager там немного.


Раньше, давно, когда я только начинал разбираться, я как-то включал отладочные сообщения. По-моему через реестр.Не оно ?. Сейчас уже не требуется, все в голове сидит .
Re[7]: Учет ссылок на объекты COM в коде DirectShow
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 06.08.19 12:25
Оценка:
Здравствуйте, Videoman, Вы писали:

V>Раньше, давно, когда я только начинал разбираться, я как-то включал отладочные сообщения. По-моему через реестр.Не оно ?.


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

HKLM\SOFTWARE[\Wow6432Node]\Microsoft\DirectShow\Debug

Поскольку оно, хоть прочитав, хоть не прочитав, сразу же пишет обратно, то может создаваться экранирующий ключ в VirtualStore:

HKCU\Software\Classes\VirtualStore\MACHINE\SOFTWARE[\Wow6432Node]\Microsoft\DirectShow\Debug

Подключи в этих ветках — QUARTZ.dll или Global.

Значения:

TIMING
TRACE
MEMORY
LOCKING
ERROR
CUSTOM1
CUSTOM2
CUSTOM3
CUSTOM4
CUSTOM5
TIMEOUT

Все типа DWORD. И у QUARTZ.dll дополнительно LogToFile типа SZ.

Из дизассемблерного листинга я навскидку не понял, как именно задаются уровни сообщений — они там проверяются и по маске, и по величине, поэтому тупо забил везде ffffffff, и вроде выводятся все предусмотренные.

Но все это не помогло найти причину обращения по нулевому указателю (точнее, по 0xc из-за смещения vftbl). Еще пара часов копания в IDA натолкнула на мысль, что в проблемном поле объекта CWaveOutInputPin может лежать адрес IMemAllocator, и тут я вспомнил, что код обсуждения аллокаторов с другим пином я еще не добавил. Сделал код, все заработало.

Так что это еще один крупный косяк quartz.dll — не только не проверять наличие аллокатора, но и не иметь для него даже ассерта.

Вообще, глядя на код от MS, не могу понять, как они его отлаживают? Или тупо наняли миллион индусов, которые занимаются этим круглосуточно?
Re[3]: Учет ссылок на объекты COM в коде DirectShow
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 25.08.19 13:48
Оценка:
Здравствуйте, Videoman, Вы писали:

V>кое-где они берут ссылки на интерфейс, релизят его и потом используют. На моей памяти такая кривизна только в самих BaseClasses, тут ничего не поделаешь. С интерфейсами стороннего фильтра они работают корректно.


Выяснилось, что quartz.dll почему-то сперва освобождает последнюю ссылку на фильтр, а затем — на его пины. У меня учет был раздельный, поэтому и падало. Посмотрел в Base Classes — у них учет общий для фильтра и всех пинов. Сделал так же — падать перестало.

Кстати, нет ли там (кроме Base Classes, конечно) какого-нибудь хелпера для управления Advise-списками? Чтобы реализовать в своем IReferenceClock только фактическое отслеживание времени, а остальную рутинную работу отдать типовому хелперу. Или каждый фильтр, реализующий часы, должен всю эту байду делать заново?
Re[4]: Учет ссылок на объекты COM в коде DirectShow
От: Videoman Россия https://hts.tv/
Дата: 26.08.19 08:43
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Выяснилось, что quartz.dll почему-то сперва освобождает последнюю ссылку на фильтр, а затем — на его пины. У меня учет был раздельный, поэтому и падало. Посмотрел в Base Classes — у них учет общий для фильтра и всех пинов. Сделал так же — падать перестало.


Здесь вот тонкий момент, нужно еще раз перепроверить вашу логику. Вообще, в идеологии COM, порядок освобождения должен быть не важен. Также, имея интерфейс IPin, всегда можно получить IBaseFilter и обратно. Т.е., по суди, это все один завуалированный объект, скажем — такой большой "краб". Если счетчик один, то уже после первого AddRef мы не можем понять за что нас держат, за IPin или IBaseFilter и у нас нет выбора кроме как удалять все только когда ref_count == 0. Я в разных фильтрах делал и так и так и всегда все работало как и задумалось (без утечек). На основе подхода реализованного в базовых классах BaseClasses, возможен только вариант с общим счетчиком (советую делать также, т.к. удобнее для DirectShow).

ЕМ>Кстати, нет ли там (кроме Base Classes, конечно) какого-нибудь хелпера для управления Advise-списками? Чтобы реализовать в своем IReferenceClock только фактическое отслеживание времени, а остальную рутинную работу отдать типовому хелперу. Или каждый фильтр, реализующий часы, должен всю эту байду делать заново?


Еcть — класс CBaseReferenceClock. Но на моей долгой практике я ни разу не видел чтобы кто-то пользовался этим сервисом. Мне также было лень эту каку реализовывать и я всегда возвращаю E_NOTIMP Пока проблем не было (видимо лет 20 назад кто-то использовал из Visual Basic).
Отредактировано 26.08.2019 8:44 Videoman . Предыдущая версия . Еще …
Отредактировано 26.08.2019 8:43 Videoman . Предыдущая версия .
Re[5]: Учет ссылок на объекты COM в коде DirectShow
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 26.08.19 09:58
Оценка:
Здравствуйте, Videoman, Вы писали:

V>Yа основе подхода реализованного в базовых классах BaseClasses, возможен только вариант с общим счетчиком (советую делать также, т.к. удобнее для DirectShow).


Я так и сделал, теперь все работает надежно.

V>Еcть — класс CBaseReferenceClock.


Ну, это снова из набора Base Classes — то есть, левый костыль, а не стандартный метод, доступный через базовые системные DLL.

В Base Classes все вообще неимоверно криво. Например, в составе любого DDK, начиная с 2k3, идут strmbase.lib/strmbasd.lib, но нигде нет streams.h, который есть только в SDK Sources. А в SDK до восьмого этих библиотек нет.

И сами модули в библиотеке сделаны криво: например, для регистрации сервера можно было бы использовать AMovieSetupRegisterServer из dllsetup, но там же лежат функции, использующие CFactoryTemplate, и даже при определении пустышек g_cTemplates/g_Templates все это тянет за собой dllentry. То есть, даже не просто костыль, а кривой костыль.

Прямо изумляет такое убожество, на фоне масштабности DirectShow.
Re[6]: Учет ссылок на объекты COM в коде DirectShow
От: Videoman Россия https://hts.tv/
Дата: 26.08.19 13:49
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>...

ЕМ>Прямо изумляет такое убожество, на фоне масштабности DirectShow.

Не могу не согласится. У меня только одно объяснение, на которое наталкивает тот факт что BaseClasses всегда лежали в Samples — Microsoft купила технологию DirectShow целиком и не планировала что все тупа начнут использовать примеры как SDK, но получилось как получилось.

Я, например, не использую регистрацию через dllentry как у Microsoft. Вместо этого у меня есть класс:
template<typename Class, typename Unknown = Class>
class FactoryClass;

// Где-то

class MyFilter : public FactoryClass<MyFilter, IUnknown>
{
//...
static std::vector<guid_t> ClassIds();  // Возвращает все GUIDы под которыми регистрируется объект (несколько ID бывает нужно если фильтр представляет несколько устройств в системе)
static guid_t CategoryId();             // Возвращает GUID категории
static string_t Description();          // Описание при регистрации в реестре
//...
}

Любой наследник автоматически встраивается в статический лист и потом автоматом регистрируется. Удобно, т.к. если модуль содержит несколько фильтров или COM объектов все регистрируется автоматом. DllEntry и DllRegisterServer/DllUnregisterServer — свои и там просто вызывается метод который проходит по статическому списку фабрик и все что нужно регистрирует/разрегистрируется.
Re[7]: Учет ссылок на объекты COM в коде DirectShow
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 26.08.19 16:16
Оценка:
Здравствуйте, Videoman, Вы писали:

V>Microsoft купила технологию DirectShow целиком и не планировала что все тупа начнут использовать примеры как SDK


А чего еще можно было ожидать, при таком-то подходе? Многие интерфейсы слишком обширны, чтобы реализовывать их с нуля, удобных хелперов для типовых задач не предложили, документация крайне убогая (многие примитивные функции WinAPI описаны куда подробнее), многие частные случаи не упомянуты вообще, вот народ и повадился делать фильтры непосредственно из примеров. Впрочем, тенденция лепить софт непосредственно из примера, похоже, вездесуща...
Re[7]: Учет ссылок на объекты COM в коде DirectShow
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 26.08.19 17:09
Оценка:
Здравствуйте, Videoman, Вы писали:

V>Я, например, не использую регистрацию через dllentry как у Microsoft.


Кстати, регистрацию можно сделать разными способами, но результат один. А вот как вообще принято задавать параметры произвольного фильтра в графе, кроме как вручную через Property Pages? Например, в Kernel Streaming для этого есть универсальный механизм Property Sets, огрызок которого (IKsPropertySet) реализован в DS, но там не видно универсальных свойств, особенно для звуковых фильтров. Каждый придумывает свои свойства (или целиком интерфейсы), или есть какие-то рекомендации? И где принято хранить эти параметры — в реестре или пользовательском профиле? Документация на эту тему совсем невнятная.
Re[8]: Учет ссылок на объекты COM в коде DirectShow
От: Videoman Россия https://hts.tv/
Дата: 27.08.19 07:48
Оценка: 8 (1)
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Кстати, регистрацию можно сделать разными способами, но результат один. А вот как вообще принято задавать параметры произвольного фильтра в графе, кроме как вручную через Property Pages? Например, в Kernel Streaming для этого есть универсальный механизм Property Sets, огрызок которого (IKsPropertySet) реализован в DS, но там не видно универсальных свойств, особенно для звуковых фильтров. Каждый придумывает свои свойства (или целиком интерфейсы), или есть какие-то рекомендации? И где принято хранить эти параметры — в реестре или пользовательском профиле? Документация на эту тему совсем невнятная.


Стандартно принято через IPersistStream. Там всего 5 методов. Graph при создании запрашивает этот интерфейс и если он есть вызывает Load. При сохранении, соответственно, Save.
Re[9]: Учет ссылок на объекты COM в коде DirectShow
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 27.08.19 10:16
Оценка:
Здравствуйте, Videoman, Вы писали:

V>Стандартно принято через IPersistStream.


А на конкретное место хранения рекомендации есть? Или, как и с параметрами обычных приложений, полный разброд и анархия? В последнее время вроде как более популярно сохранение в файлы пользовательского профиля, хотя ветка HKCU все одно в нем лежит...
Re[10]: Учет ссылок на объекты COM в коде DirectShow
От: Videoman Россия https://hts.tv/
Дата: 27.08.19 10:56
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>А на конкретное место хранения рекомендации есть? Или, как и с параметрами обычных приложений, полный разброд и анархия? В последнее время вроде как более популярно сохранение в файлы пользовательского профиля, хотя ветка HKCU все одно в нем лежит...


Не уверен что правильно понял ваш вопрос. Фильтр как драйвер, полностью пассивная штука. Он сохраняет в абстрактный IPersistStream. Откуда или куда будет чтение/запись решает код снаружи. В Graph Edit-е, например, все будет сохраняться в файл .grf.
Re[11]: Учет ссылок на объекты COM в коде DirectShow
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 27.08.19 11:27
Оценка:
Здравствуйте, Videoman, Вы писали:

V>Фильтр как драйвер, полностью пассивная штука.


Ну как сказать — в большинстве драйверов таки есть настройки, сохраняемые в реестре (в собственной ветке драйвера или в HKLM\Software). Используется ли то же самое для фильтров для определения общих умолчаний?

V>Он сохраняет в абстрактный IPersistStream. Откуда или куда будет чтение/запись решает код снаружи. В Graph Edit-е, например, все будет сохраняться в файл .grf.


Это, фактически, уже настройки приложения, использующего фильтр.
Re[12]: Учет ссылок на объекты COM в коде DirectShow
От: Videoman Россия https://hts.tv/
Дата: 27.08.19 12:35
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Ну как сказать — в большинстве драйверов таки есть настройки, сохраняемые в реестре (в собственной ветке драйвера или в HKLM\Software). Используется ли то же самое для фильтров для определения общих умолчаний?


А, понял. Такого рода сетап можно размещать в реестре или конфигурационных файлах. Стандартного подхода я не видел.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.