Бага в CoUninitialize
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 18.09.06 08:11
Оценка: 54 (7)
#Имя: FAQ.com.counitialize.bug
Доброго всем понедельника.

У меня от тоже добрый. На протяжении последнего времени меня, время от времени, долбило сообщение об AV (чур меня, чур меня) при деинициализации COM-а.

Вчера меня оно стало уже конкретно напрягать и сегодня, приготовив ведро с вазелином, решил ... багу до смерти.

Что выяснил.
1. В COM-объекте, используемом в программе, происходила ошибка. Которую он честно возвращал клиенту через SetErrorInfo. Клиент, так уж получалось, на это дело ложил и оставлял объект ошибки висеть в памяти.
2. Объект с описанием ошибки был нестандартный и реализовывался не стандартным комом (в ole32.dll?), а в oledb32.dll. Естественно, интерфейс IErrorInfo у него был.
3. При вызове CoUninitialize, сначала выгружаются все библиотеки с COM-объектами (в том числе моя и oledb32.dll), а потом освобождается объект ошибки.
Но объекта ошибки, ясный пень, в памяти уже нет (DLL-то уже тю-тю) — так что появляется злополучный AV

Бага проявляется как на Win2000 так и на XP со всеми обновлениями.

Я требую пустить кровь тому кто реализовывал CoUninitialize или, что хуже, израсходовать на него остатки вазелина!

Пока что решил перед вызовом CoUninitialize самому вызывать SetErrorInfo(0,NULL) — это реально помогает
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re: Бага в CoUninitialize
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 18.09.06 11:58
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Доброго всем понедельника.


Наверное, надо пояснить, где подобная ошибка может произойти.

Ну во-первых, если вы, для своего ActiveX-объекта, самостоятельно реализуете объект ошибки с интерфейсом IErrorInfo.

Во-вторых, если вы юзаете такой объект и забиваете на получение объектов ошибок.

В третьих, если вы прямо или косвенно работаете с OLEDB-провайдерами. Провайдеры, как правило, используют объекты ошибок из oledb32.dll. Это был как раз мой случай

--------------
И, кстати говоря, тут сформулировалось очередное правило компоненто-писателям. При разрушении вашего COM-объекта не стоит обрабатывать внутренние ошибки и пытаться сообщить о них клиенту через пресловутый объект-ошибки. Я про реализацию деструкторов, если вы пишете на C++.

Во-первых, вы можете затереть действительно полезную ошибку другого COM-объекта (который юзал и инициировал разрушение вашего COM-объекта)
Во-вторых, вы потенциально нарываетесь на ранее описанную ошибку с CoUninitialize. Клиенту, как правило, по-барабану ошибки освобождения COM-объектов.

--------------
Вот. Обожаю COM и все его грабли
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[2]: Бага в CoUninitialize
От: Vi2 Удмуртия http://www.adem.ru
Дата: 18.09.06 13:17
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Ну во-первых, если вы, для своего ActiveX-объекта, самостоятельно реализуете объект ошибки с интерфейсом IErrorInfo.


КД>>2. Объект с описанием ошибки был нестандартный и реализовывался не стандартным комом (в ole32.dll?), а в oledb32.dll. Естественно, интерфейс IErrorInfo у него был.


А можно поподробнее о нестандартном объекте ошибок. Что это за объект, кто написал, кто создает и т.п.

Видишь ли, тут не предъявить претензии именно к CoUninitialize: она работает как умеет. Если кто-то не закрыл счетчик ссылок объекта и позволил серверу этого объекта уйти, вот этот кто-то — злобный буратино. Так что решение SetErrorInfo(0,NULL) уничтожает зловредное влияние этого "кто-то", а не CoUninitialize. Аналог этому действию — End в VB, когда приложение не думает завершаться по Unload.
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! © КВН НГУ
Re[3]: Бага в CoUninitialize
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 18.09.06 13:38
Оценка:
Здравствуйте, Vi2, Вы писали:

Vi2>Здравствуйте, Коваленко Дмитрий, Вы писали:


КД>>Ну во-первых, если вы, для своего ActiveX-объекта, самостоятельно реализуете объект ошибки с интерфейсом IErrorInfo.


КД>>>2. Объект с описанием ошибки был нестандартный и реализовывался не стандартным комом (в ole32.dll?), а в oledb32.dll. Естественно, интерфейс IErrorInfo у него был.


Vi2>А можно поподробнее о нестандартном объекте ошибок. Что это за объект, кто написал, кто создает и т.п.


В OLEDB есть собственная реализация объекта ошибки. Помимо IErrorInfo она реализует интерфейс IErrorRecords, что позволяет создавать коллекции ошибок. Создается этот объект через CoCreateInstance(CLSID_EXTENDEDERRORINFO,...). Создается OLEDB-провайдером, когда он хочет вернуть клиенту "правильный" объект OLEDB-ошибки. Устанавливается как обычная ошибка автоматизации через SetErrorInfo. Живет в oledb32.dll.

Помимо коллекции ошибок, этот объект еще хранит параметры ошибки, которые потом используются при формировании сообщения об ошибки (с учетом языка пользователя). То есть, создаешь такой объект-ошибки, запихиваешь в него параметры, а он потом передает эти параметры и lcid твоему другому "спец-объекту" OLEDB-провайдера с интерфейсом IErrorLookup, чтобы последний сформировал текст ошибки.

Vi2>Видишь ли, тут не предъявить претензии именно к CoUninitialize: она работает как умеет. Если кто-то не закрыл счетчик ссылок объекта и позволил серверу этого объекта уйти, вот этот кто-то — злобный буратино. Так что решение SetErrorInfo(0,NULL) уничтожает зловредное влияние этого "кто-то", а не CoUninitialize. Аналог этому действию — End в VB, когда приложение не думает завершаться по Unload.


Ха. Извините меня — я зарегистрировал в окружении COM-а объект-ошибку и он вызвал для нее AddRef. Все — он за неё отвечает. Клиент и, тем более, сам COM-объект (зарегистрировавший эту ошибку) за этот объект-ошибку не отвечают. За него отвечает окружение. Оно же вызывает, в конечном итоге, для него Relеase и соответственно нарывается на AV — так что оно (окружение) это понимает, но разработчик CoUninitialize тестировал только с тем объектом ошибки, который живет в ole32.dll.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[4]: Бага в CoUninitialize
От: Константин Л. Франция  
Дата: 18.09.06 13:43
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Ха. Извините меня — я зарегистрировал в окружении COM-а объект-ошибку и он вызвал для нее AddRef. Все — он за неё отвечает. Клиент и, тем более, сам COM-объект (зарегистрировавший эту ошибку) за этот объект-ошибку не отвечают. За него отвечает окружение. Оно же вызывает, в конечном итоге, для него Relеase и соответственно нарывается на AV — так что оно (окружение) это понимает, но разработчик CoUninitialize тестировал только с тем объектом ошибки, который живет в ole32.dll.


Вот тут позволю себе не согласиться. Кто-то должен управлять временем жизни этой ошибки. Этот "кто-то" либо клиент, либо сервер, тк среда, похоже by design, за нее не отвечает. Так что тут банальный interface leak
Re[5]: Бага в CoUninitialize
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 18.09.06 14:11
Оценка:
Здравствуйте, Константин Л., Вы писали:

КЛ>Вот тут позволю себе не согласиться. Кто-то должен управлять временем жизни этой ошибки. Этот "кто-то" либо клиент, либо сервер, тк среда, похоже by design, за нее не отвечает. Так что тут банальный interface leak


Пожалуйста, это дело такое. Никто не заставляет.

Но рекомендую бросать молится на тех, кто сидит в MS. Я встречал у них баги и пострашнее, чем этот несчастный CoUnitialize.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[6]: Бага в CoUninitialize
От: Константин Л. Франция  
Дата: 18.09.06 14:15
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Здравствуйте, Константин Л., Вы писали:


КЛ>>Вот тут позволю себе не согласиться. Кто-то должен управлять временем жизни этой ошибки. Этот "кто-то" либо клиент, либо сервер, тк среда, похоже by design, за нее не отвечает. Так что тут банальный interface leak


КД>Пожалуйста, это дело такое. Никто не заставляет.


КД>Но рекомендую бросать молится на тех, кто сидит в MS. Я встречал у них баги и пострашнее, чем этот несчастный CoUnitialize.

дык, никто и не молится
Re[5]: Бага в CoUninitialize
От: Лазар Бешкенадзе СССР  
Дата: 18.09.06 15:18
Оценка: +1
Здравствуйте, Константин Л., Вы писали:

КЛ>Здравствуйте, Коваленко Дмитрий, Вы писали:


КД>>Ха. Извините меня — я зарегистрировал в окружении COM-а объект-ошибку и он вызвал для нее AddRef. Все — он за неё отвечает. Клиент и, тем более, сам COM-объект (зарегистрировавший эту ошибку) за этот объект-ошибку не отвечают. За него отвечает окружение. Оно же вызывает, в конечном итоге, для него Relеase и соответственно нарывается на AV — так что оно (окружение) это понимает, но разработчик CoUninitialize тестировал только с тем объектом ошибки, который живет в ole32.dll.


КЛ>Вот тут позволю себе не согласиться. Кто-то должен управлять временем жизни этой ошибки. Этот "кто-то" либо клиент, либо сервер, тк среда, похоже by design, за нее не отвечает. Так что тут банальный interface leak


В данном случае именно "среда" этим управляет потому как от сервера
предполагается только вызов SetErrorInfo, а от клиента ожидают вызова
GetErrorInfo, причем ни от одного этого не требуют. Ошибка живет в
потоке и именно "среда" освобождает не съеденную клиентом ошибку, если
сервер пытается установить новую. Так что и в недрах CoUninitialize
разумно ожидать сначала освобождения объекта ошибки, а потом уже
выгрузки DLL, которая этот объект реализует.

Я проэксперементировал с custom error object и все происходит именно
так, как описывает Дмитрий. Я с ним согласен, здесь MS явно ошиблась.

Лазар
Re[3]: Бага в CoUninitialize
От: Аноним  
Дата: 18.09.06 17:08
Оценка:
Здравствуйте, Vi2, Вы писали:

Vi2>Видишь ли, тут не предъявить претензии именно к CoUninitialize: она работает как умеет. Если кто-то не закрыл счетчик ссылок объекта и позволил серверу этого объекта уйти, вот этот кто-то — злобный буратино. Так что решение SetErrorInfo(0,NULL) уничтожает зловредное влияние этого "кто-то", а не CoUninitialize. Аналог этому действию — End в VB, когда приложение не думает завершаться по Unload.


Не согласен. Если вы намекаете на неправильные module locks — правильные не помогут в случае вызова CoUninitialize — ему на DllCanUnloadNow плевать.
ИМХО: Интерфейс отдаётся в распоряжение системы, она принимает на себя ответственность за его своевременное уничтожение перед до вызова CoUninitialize, или же такое требование к интерфейсу (перехват вызова CoUninitialize и самоуничтожение) должно быть документировано. ИМХО — это баг.
Re[5]: Бага в CoUninitialize
От: Аноним  
Дата: 18.09.06 17:10
Оценка: +1
Здравствуйте, Константин Л., Вы писали:

КЛ>Вот тут позволю себе не согласиться. Кто-то должен управлять временем жизни этой ошибки. Этот "кто-то" либо клиент, либо сервер, тк среда, похоже by design, за нее не отвечает. Так что тут банальный interface leak

Да ну? И что же, если я пишу код пользующий СОМ сервер я должен после каждого вызова дергать GetErrorInfo, дабы этот ErrorInfo не утёк?
ИМХО: Интерфейс отдаётся в распоряжение системы, она принимает на себя ответственность за его своевременное уничтожение перед до вызова CoUninitialize, или же такое требование к интерфейсу (перехват вызова CoUninitialize и самоуничтожение) должно быть документировано. ИМХО — это баг.
Re[7]: Бага в CoUninitialize
От: Аноним  
Дата: 18.09.06 17:27
Оценка: :)
Здравствуйте, Константин Л., Вы писали:

КД>>Но рекомендую бросать молится на тех, кто сидит в MS. Я встречал у них баги и пострашнее, чем этот несчастный CoUnitialize.

КЛ>дык, никто и не молится
Не правда ваша, многие как раз. А почему — действительно непонятно. Когда-то — да, каждый из нескольких десятков программеров работавших в МС был профи, теперь это тысячи индусов и русских закончивших институт год-два-три назад, ещё ничему не научившихся и способных делать любые глупейшие ошибки. Хорошо хоть тестеров там больше чем кодеров.
ИМХО — БГ от этого и бежит. ИМХО: они ошиблись в том, что подумали что набрав тысячи програмеров они смогут свернуть горы. Годах в 96-98 набрали — результат плачевный — больше никогда и ничего не смогли сделать в срок даже близко (Vista — апофеоз) и хотя бы с 50% запланированного функционала. НТ тим был вроде 50 человек и сделали его "играючи". 2к тим был вроде 2000 програмеров + 3000 тестеров — еле-еле сделали не уложившись в сроки в 2-3 раза (он же НТ 5 должен был зваться и выйти в 97-98). ИМХО: оригинальный НТ тим легко сделал бы в срок. Сверхприбылей уже нет — всё явно съедает эта толпа програмеров. Права на бизнес-ошибку (коих в истории майкрософта навалом) больше нет — нет денежного резерва как раньше, когда доходы сильно превышали расходы. И выгнать всю эту толпу никак — не простит биржа, трудовое законодательство, да и самим обидно.
Re[4]: Бага в CoUninitialize
От: Vi2 Удмуртия http://www.adem.ru
Дата: 19.09.06 05:39
Оценка: 8 (1)
Здравствуйте, Аноним, Вы писали:

А>Не согласен. Если вы намекаете на неправильные module locks — правильные не помогут в случае вызова CoUninitialize — ему на DllCanUnloadNow плевать.

А>ИМХО: Интерфейс отдаётся в распоряжение системы, она принимает на себя ответственность за его своевременное уничтожение перед до вызова CoUninitialize, или же такое требование к интерфейсу (перехват вызова CoUninitialize и самоуничтожение) должно быть документировано.

У-у, как страшно! А ты читал про DllCanUnloadNow? При правильном module locks сервер для объекта никогда не выгрузится, пока есть активные ссылки на его объекты. Наверное, дело в том, что нестандартный объект-ошибка не блокирует выгрузку своего сервера. Но это уже не проблема для CoUninitialize. Вот то, что я хотел сказать.

А>ИМХО — это баг.


Да, очевидно. Но чей? И если без разницы, то SetErrorInfo(0,NULL) — решение. За что и были поставлены баллы.
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! © КВН НГУ
Re[4]: Бага в CoUninitialize
От: Vi2 Удмуртия http://www.adem.ru
Дата: 19.09.06 05:45
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Извините меня — я зарегистрировал в окружении COM-а объект-ошибку и он вызвал для нее AddRef. Все — он за неё отвечает. Клиент и, тем более, сам COM-объект (зарегистрировавший эту ошибку) за этот объект-ошибку не отвечают. За него отвечает окружение. Оно же вызывает, в конечном итоге, для него Relеase и соответственно нарывается на AV — так что оно (окружение) это понимает, но разработчик CoUninitialize тестировал только с тем объектом ошибки, который живет в ole32.dll.


Естественно. После вызова SetErrorInfo объект-ошибку можно релизить — ее счетчик на совести СОМ системы. Но почему ушла oledb32.dll, если, ты говоришь, в ней реализован объект-ошибка? Наверное, из-за тог, что приложение завершается. Значит, что-то не так с самим выходом.
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! © КВН НГУ
Re[5]: Бага в CoUninitialize
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 19.09.06 05:55
Оценка:
Здравствуйте, Vi2, Вы писали:

Vi2>Здравствуйте, Аноним, Вы писали:


А>>Не согласен. Если вы намекаете на неправильные module locks — правильные не помогут в случае вызова CoUninitialize — ему на DllCanUnloadNow плевать.

А>>ИМХО: Интерфейс отдаётся в распоряжение системы, она принимает на себя ответственность за его своевременное уничтожение перед до вызова CoUninitialize, или же такое требование к интерфейсу (перехват вызова CoUninitialize и самоуничтожение) должно быть документировано.

Vi2>У-у, как страшно! А ты читал про DllCanUnloadNow? При правильном module locks сервер для объекта никогда не выгрузится, пока есть активные ссылки на его объекты. Наверное, дело в том, что нестандартный объект-ошибка не блокирует выгрузку своего сервера. Но это уже не проблема для CoUninitialize. Вот то, что я хотел сказать.


Дык это, того — CoUninitialize это завершение работы. Это его основное назначение. То что он, извините, во время деинициализации сам себе наступает на ..., то это его проблема. Причем очевидно, что он выполняет деинициализацию в неправильном порядке — сначало нужно освободить COM-объекты, потом выгружать DLL.

Кстати говоря, ведь COM может держать не только объект ошибки, но и другие COM-объекты. Есть же функции регистрации активных ActiveX объектов (их названия сходу я не скажу). И что? Я более чем уверен — освобождение этих ActiveX-ов выполняется перед выгрузкой DLL-ей. Потому что иначе вообще любая более менее сложная программа основанная на COM завершалась бы по AV.

А>>ИМХО — это баг.


Vi2>Да, очевидно. Но чей? И если без разницы, то SetErrorInfo(0,NULL) — решение.


Это да. Удивляет то, что этот баг, по всей видимости, живет с самого рождения COM-а
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[5]: Бага в CoUninitialize
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 19.09.06 06:09
Оценка:
Здравствуйте, Vi2, Вы писали:

Vi2>Здравствуйте, Коваленко Дмитрий, Вы писали:


Vi2>Естественно. После вызова SetErrorInfo объект-ошибку можно релизить — ее счетчик на совести СОМ системы. Но почему ушла oledb32.dll, если, ты говоришь, в ней реализован объект-ошибка? Наверное, из-за тог, что приложение завершается. Значит, что-то не так с самим выходом.


Вообще это было злополучное стечение багов. Коротко говоря, у меня при последнем Release внутри компонента возникала ошибка и я радостно пытался сообщить о ней клиенту через объект-ошибку из oledb32.dll. Ясный пень, клиент не будет проверять результаты работы Release, поэтому объект-ошибки продолжал висеть в памяти до деинициализции COM-а.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[6]: Бага в CoUninitialize
От: Vi2 Удмуртия http://www.adem.ru
Дата: 19.09.06 06:36
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>Дык это, того — CoUninitialize это завершение работы. Это его основное назначение. То что он, извините, во время деинициализации сам себе наступает на ..., то это его проблема. Причем очевидно, что он выполняет деинициализацию в неправильном порядке — сначало нужно освободить COM-объекты, потом выгружать DLL.


Это заблуждение: CoUninitialize — это не завершение работы.

CoUninitialize
Closes the COM library on the current thread, unloads all DLLs loaded by the thread, frees any other resources that the thread maintains, and forces all RPC connections on the thread to close.

Это не мешает заново в потоке вызвать CoInitialize и потом снова CoUninitialize. Так, конечно, никто не делает, но этот путь возможен.

А фраза "forces all RPC connections on the thread to close" может трактоваться как освобождение памяти под внутренние буфера для прокси/стабов. Но это освобождение никак не зависит от соответствия AddRef и Release, т.е. от наличия или отсутствия сервера поддержки.

КД>Кстати говоря, ведь COM может держать не только объект ошибки, но и другие COM-объекты. Есть же функции регистрации активных ActiveX объектов (их названия сходу я не скажу). И что? Я более чем уверен — освобождение этих ActiveX-ов выполняется перед выгрузкой DLL-ей. Потому что иначе вообще любая более менее сложная программа основанная на COM завершалась бы по AV.


В том-то и дело, что "объект ошибки и другие COM-объекты" ничем не отличаются. Вернее, отличаются только тем, кто реализовал код поддержки. И как криво. Поэтому это может быть не баг в CoUninitialize, а баг в обработчике ошибок DB и тому подобное. И может быть продулирован в их, DB, разделе для борьбы с надоедливым AV.
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! © КВН НГУ
Re[7]: Бага в CoUninitialize
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 19.09.06 06:52
Оценка:
Здравствуйте, Vi2, Вы писали:

КД>>Дык это, того — CoUninitialize это завершение работы. Это его основное назначение. То что он, извините, во время деинициализации сам себе наступает на ..., то это его проблема. Причем очевидно, что он выполняет деинициализацию в неправильном порядке — сначало нужно освободить COM-объекты, потом выгружать DLL.


Vi2>Это заблуждение: CoUninitialize — это не завершение работы.


Я просто не стал распинаться по этому поводу

КД>>Кстати говоря, ведь COM может держать не только объект ошибки, но и другие COM-объекты. Есть же функции регистрации активных ActiveX объектов (их названия сходу я не скажу). И что? Я более чем уверен — освобождение этих ActiveX-ов выполняется перед выгрузкой DLL-ей. Потому что иначе вообще любая более менее сложная программа основанная на COM завершалась бы по AV.


Vi2>В том-то и дело, что "объект ошибки и другие COM-объекты" ничем не отличаются. Вернее, отличаются только тем, кто реализовал код поддержки. И как криво. Поэтому это может быть не баг в CoUninitialize, а баг в обработчике ошибок DB и тому подобное. И может быть продулирован в их, DB, разделе для борьбы с надоедливым AV.


Чего-то ты сам себе противоречишь. И я прям удивляюсь твоему упорству признать, что клоун, реализовываший деинициализацию не подумал, что объект ошибки может быть реализован в другой DLL.

Предлагаю написать баг-репорт в микрософт и посмотреть что они скажут по этому поводу. Я правда не знаю в какую дверь надо стучаться
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[8]: Бага в CoUninitialize
От: Vi2 Удмуртия http://www.adem.ru
Дата: 19.09.06 07:36
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>И я прям удивляюсь твоему упорству признать, что клоун, реализовываший деинициализацию не подумал, что объект ошибки может быть реализован в другой DLL.


Хорошо. Предположим, этот клоун все же подумал об этом. Какие действия функция CoUninitialize должна совершить в свете этих новых знаний/дум? Простой Release на указателе. Но это ясно и БЕЗ информации о том, где расположен сервер. Ах вылетает, а я тут причем? Библиотека ушла? Хм, ну и придурок ее писал.

КД>Предлагаю написать баг-репорт в микрософт и посмотреть что они скажут по этому поводу. Я правда не знаю в какую дверь надо стучаться


А оно надо? Они же уже в НЕТе все. А там сборщик мусора, с ним не забалуешь.
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! © КВН НГУ
Re[7]: Бага в CoUninitialize
От: Лазар Бешкенадзе СССР  
Дата: 19.09.06 07:39
Оценка: 1 (1)
Здравствуйте, Vi2, Вы писали:

Vi2>Здравствуйте, Коваленко Дмитрий, Вы писали:


КД>>Дык это, того — CoUninitialize это завершение работы. Это его основное назначение. То что он, извините, во время деинициализации сам себе наступает на ..., то это его проблема. Причем очевидно, что он выполняет деинициализацию в неправильном порядке — сначало нужно освободить COM-объекты, потом выгружать DLL.


Vi2>Это заблуждение: CoUninitialize — это не завершение работы.


Очевидно, что имелось ввиду завершение работы с библиотекой COM

Vi2>CoUninitialize

Vi2>Closes the COM library on the current thread, unloads all DLLs loaded by the thread, frees any other resources that the thread maintains,

Я бы подвел освобождение объекта ошибки под этот пункт если, конечно, не возражают уважаемые присяжные.
Красивое обязательство — frees any other resources that the thread maintains

Vi2> and forces all RPC connections on the thread to close.


Vi2>Это не мешает заново в потоке вызвать CoInitialize и потом снова CoUninitialize. Так, конечно, никто не делает, но этот путь возможен.


Действительно никто не мешает после завершения работы снова начать работу.

Vi2>А фраза "forces all RPC connections on the thread to close" может трактоваться как освобождение памяти под внутренние буфера для прокси/стабов. Но это освобождение никак не зависит от соответствия AddRef и Release, т.е. от наличия или отсутствия сервера поддержки.


КД>>Кстати говоря, ведь COM может держать не только объект ошибки, но и другие COM-объекты. Есть же функции регистрации активных ActiveX объектов (их названия сходу я не скажу). И что? Я более чем уверен — освобождение этих ActiveX-ов выполняется перед выгрузкой DLL-ей. Потому что иначе вообще любая более менее сложная программа основанная на COM завершалась бы по AV.


Vi2>В том-то и дело, что "объект ошибки и другие COM-объекты" ничем не отличаются. Вернее, отличаются только тем, кто реализовал код поддержки. И как криво. Поэтому это может быть не баг в CoUninitialize, а баг в обработчике ошибок DB и тому подобное. И может быть продулирован в их, DB, разделе для борьбы с надоедливым AV.


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

#define _WIN32_WINNT 0x0400

#include <msxml2.h>

class ErrorObject : IErrorInfo
{
    ULONG m_cRef;
    IXMLDOMDocument *m_pXmlDoc;

    ErrorObject() : m_cRef(1) {
        CoCreateInstance(__uuidof(DOMDocument), 0, CLSCTX_INPROC_SERVER, __uuidof(IXMLDOMDocument), (void **) &m_pXmlDoc);
    }
    ~ErrorObject() {
        if (m_pXmlDoc != 0)
            m_pXmlDoc->Release();
    }

    HRESULT __stdcall QueryInterface(REFIID iid, void **ppvObject) {
        if (iid == __uuidof(IErrorInfo) || iid == __uuidof(IUnknown)) {
            *ppvObject = this;
            return S_OK;
        }
        else {
            *ppvObject = 0;
            return E_NOINTERFACE;
        }
    }
    ULONG __stdcall AddRef(void) { return ::InterlockedIncrement((LONG *) &m_cRef); }
    ULONG __stdcall Release(void) {
        ULONG cRef = ::InterlockedDecrement((LONG *) &m_cRef);
        if (cRef == 0)
            delete this;
        return cRef;
    }

    HRESULT __stdcall GetGUID(GUID *pGUID) {
        *pGUID = GUID_NULL;
        return S_OK;
    }
    HRESULT __stdcall GetSource(BSTR *pBstrSource) {
        *pBstrSource = 0;
        return S_OK;
    }
    HRESULT __stdcall GetDescription(BSTR *pBstrDescription) {
        *pBstrDescription = 0;
        return S_OK;
    }
    HRESULT __stdcall GetHelpFile(BSTR *pBstrHelpFile) {
        *pBstrHelpFile = 0;
        return S_OK;
    }
    HRESULT __stdcall GetHelpContext(DWORD *pdwHelpContext) {
        *pdwHelpContext = 0;
        return S_OK;
    }

public:
    static HRESULT CreateInstance( IErrorInfo **ppErrorInfo) {
        *ppErrorInfo = new ErrorObject();
        return S_OK;
    }
};

int main()
{
    ::CoInitializeEx(0, COINIT_MULTITHREADED);

    IErrorInfo *pError;
    ErrorObject::CreateInstance(&pError);
    SetErrorInfo(0, pError);
    pError->Release();

    ::CoUninitialize();
}
Re[9]: Бага в CoUninitialize
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 19.09.06 08:03
Оценка:
Здравствуйте, Vi2, Вы писали:

Vi2>Здравствуйте, Коваленко Дмитрий, Вы писали:


КД>>И я прям удивляюсь твоему упорству признать, что клоун, реализовываший деинициализацию не подумал, что объект ошибки может быть реализован в другой DLL.


Vi2>Хорошо. Предположим, этот клоун все же подумал об этом. Какие действия функция CoUninitialize должна совершить в свете этих новых знаний/дум? Простой Release на указателе. Но это ясно и БЕЗ информации о том, где расположен сервер.


Я не догнал — какую информацию ты имеешь в виду.

Vi2>Ах вылетает, а я тут причем? Библиотека ушла? Хм, ну и придурок ее писал.


Куда именно она уйдет? С какого перепугу? Еще раз

[правильный порядок деинициализации]

1. Освобождаем COM-объекты удерживаемые системой. Ясный пень, удерживаемые в текущем потоке. В том числе и объект-ошибки, который был зарегистрирован в текущем потоке.

Здесь никто и никуда не уходит. DLL-и продолжают жить в памяти, потому что COM еще не вызывал для них FreeLibrary.

2. Вызываем FreeLibrary для всех DLL-ей которые были загружены в качестве COM-серверов. Если у DLL-и счетчик ссылок обнулился — система её выгрузит. Повторю — счетчик ссылок DLL-и, а не тех COM-объектов, которые ею обслуживаются.

И все. Я в упор не вижу где я тут и чему противоречу.

КД>>Предлагаю написать баг-репорт в микрософт и посмотреть что они скажут по этому поводу. Я правда не знаю в какую дверь надо стучаться


Vi2>А оно надо? Они же уже в НЕТе все. А там сборщик мусора, с ним не забалуешь.


Кто это все? Вообще говоря, это не правда и это тема для другого форума

Я как-то совсем недавно заглядывал на форум MSSQL-я (сообщал им о другом баге, гы-гы) так там висело объявление — кто хочет познакомиться с новейшими возможностями ODBC-драйвера и OLEDB-провайдера для MSSQL 2005, типа добро пожаловать к нам на семинар. И первое, и второе, как впрочем и третье (сам MSSQL) явно не на .NET написаны. Аналогичную вещь я наблюдал в обвязке VirtualServer, там тоже предоставляются COM-интерфейсы для взаимодейстивия с этим сервисом. Так что не будем так категоричны.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.