сразу хочу отметить, что вопрос сюда пришел постепенно, по пути msdn -> microsoft.public.vc.atl -> wasm -> rsdn. К сожалению, ответ нигде получить не получилось, поэтому дублирую здесь, прошу прощения у тех, если кто его читает не в первый раз.
Ситуация следующая: в процессе работы над проектом мы натолкнулись на то, что при определенных условиях нам удается загрузить одну и ту же dll в один процесс 2 раза. Фактический mapping type в обоих случаях Image, пути загрузки — идентичны. Чтобы показать проблему был сделан простенький пример (solution из 3-х native win32 C++ проектов для Visual Studio 2005).
В нем мы грузим проблемную длл вначале с помощью SxS COM, создавая объект, живущий в ней, а воторой раз — как статически прилинкованную к загружаемой через LoadLibrary длл (т.е. делаем LoadLibrary "промежуточной" длл, в таблице импорта которой находится "проблемная" первоначальная дублирующаяся длл (та, в которой живут созданные раньше через SxS com объекты)). Порядок загрузки изображен тут.
В результате смотрим, например, Process Explorer'ом и видим 2 копии одной и той же длл.
Меня бы может и устроил ответ, что, типа, в одном случае у нас com грузит, а в другом — LoadLibrary, да и то опосредованно через таблицу импорта и все такое (хотя это, имхо, бред, какая разница как оно грузится, если образ один и тот-же, да и поискать в ctnb если, то везде говорят, что "dll can't be loaded twice" с различным вариациями), если бы не еще несколько НО с нашей точки зрения:
1. Если в указанном примере использовать обычный com, а не SxS, то копия длл оказывается одна.
2. Если изменить порядок загрузки и вначале грузить через LoadLibrary, а потом создавать объект через SxS com — то опять-же, копия одна.
И пускай уже, пункт 1 я готов принять как некий by design, но вот пункт 2 ставит большой знак вопроса. Какими бы ни были разными механизмы загрузки, но порядок их использования не должен влиять на конечный результат.
Более того, если поставить бряки в DllMain дублирующейся длл, то студия дуреет: при первоначальной загрузке через SxS все красиво брякается, а вот при повторной непонятной загрузке студийные бряки не срабатывают. Если поусердствовать и вручную всунуть int3, то это приводит студию в шок и она показывает в стеке вызовов полную ерунду + асм код вместо исходников. Т.е. она видит, что длл загружена по одному базовому адресу и никак не хочет принимать тот факт, что та же длл есть и еще по другому адресу в том-же процессе.
В результате мы подозреваем баг в загрузчике WinXP SP2 / Vista (воспроизводится аналогично), неким образом очевидно связанный с Side by Side COM.
Очень буду вам благодарен за опровержение и любые иные аргументированные соображения.
TRG>Очень буду вам благодарен за опровержение и любые иные аргументированные соображения.
А ты уверен, что Dll не выгружена COM-ом? Попробуй протрейсить события в dllmain-е.
Здравствуйте, Tom, Вы писали:
TRG>>Очень буду вам благодарен за опровержение и любые иные аргументированные соображения. Tom>А ты уверен, что Dll не выгружена COM-ом? Попробуй протрейсить события в dllmain-е.
Большое спасибо за Ваш ответ.
Я абсолютно уверен, что не выгружена . Ведь мы держим указатель на объект, который создаем.
В случае описаного сценария (когда длл грузится именно 2 раза) мы имеем такие события (ставим int 3 в DllMain и в конструкторе глобального объекта, объявленного в длл):
--- Создаем SxS com объект ---
1. Конструктор глобального объекта.
2. DllMain: ATTACH
--- Делаем LoadLibrary "промежуточной" dll ---
3. Конструктор глобального объекта, но студия не может с этим разобраться и показывает асм код + безымянный стек trace
4. DllMain: ATTACH, асм код + безымянный стек trace
--- Закрываем программу ---
5. DllMain: DETACH, нормальный, т.е. загруженой com'ом dll'ки.
6. DllMain: DETACH, асм код + безымянный стек trace, т.е. выгружается загруженная ранее "опосредовано" через LoadLibrary dll'ка
Деструктор объекта просто не объявлен, но он тут сути не играет. К моменту закрытия уже и трейсить особо ничего больше не надо (шаги 5,6) — т.к. проблема налицо.
TRG>Я абсолютно уверен, что не выгружена . Ведь мы держим указатель на объект, который создаем.
Это ничего не значит.
TRG>В случае описаного сценария (когда длл грузится именно 2 раза) мы имеем такие события (ставим int 3 в DllMain и в конструкторе глобального объекта, объявленного в длл):
Не надо int 3, хватит простого outputDebugString-а.
TRG>--- Создаем SxS com объект --- TRG>1. Конструктор глобального объекта. TRG>2. DllMain: ATTACH
Который attach?
ПС:
Запустить не удалось, у меня 2008-я студия не стоит, только 2005.
TRG>>Я абсолютно уверен, что не выгружена . Ведь мы держим указатель на объект, который создаем. Tom>Это ничего не значит.
TRG>>В случае описаного сценария (когда длл грузится именно 2 раза) мы имеем такие события (ставим int 3 в DllMain и в конструкторе глобального объекта, объявленного в длл): Tom>Не надо int 3, хватит простого outputDebugString-а.
TRG>>--- Создаем SxS com объект --- TRG>>1. Конструктор глобального объекта. TRG>>2. DllMain: ATTACH Tom>Который attach?
Tom>ПС: Tom>Запустить не удалось, у меня 2008-я студия не стоит, только 2005.
2008 не нужна, у меня тоже 2005. Собственно, о 2005 и шла речь в оригинальном сообщении, почему вы решили, что нужна 2008?
Аттач — тот, который DLL_PROCESS_ATTACH, оба раза.
Второй раз надо смотреть в дизасме, так как студия не соображает, что есть код, как я и писал. Тем не менее, оба раза DLL_PROCESS_ATTACH.
TRG>2008 не нужна, у меня тоже 2005. Собственно, о 2005 и шла речь в оригинальном сообщении, почему вы решили, что нужна 2008?
Потому, что при запуске требует CRT от 2008-й
Здравствуйте, Tom, Вы писали:
TRG>>2008 не нужна, у меня тоже 2005. Собственно, о 2005 и шла речь в оригинальном сообщении, почему вы решили, что нужна 2008? Tom>Потому, что при запуске требует CRT от 2008-й
Коллега, ошибаетесь. 2008 студии не видел в глаза ни один из моих рабочих PC, 9-й CRT там быть неоткуда. Тем более в зависимостях студийного (2005!) проекта. Требуется CRT версий 8.хххх.
Не знаю, обновляллось ли CRT с выходом SP1 для студии 2005. Подозреваю, что как раз обновлялось, и что у Вас, возможно, это обновление не стоит.
В любом случае, это имеет мало отношения к сути проблемы.
После Нового Года будем контактировать с MS в рамках Gold Partner программы, должны уделить внимание, по идее. Больше ничего не остается.
Оказыается, есть не так много людей, которые используют SxS COM и еще меньше людей, которые при этом вызывают LoadLibrary тех же библиотек
Здравствуйте, TheRawGod, Вы писали:
TRG>В результате мы подозреваем баг в загрузчике WinXP SP2 / Vista (воспроизводится аналогично), неким образом очевидно связанный с Side by Side COM. TRG>Очень буду вам благодарен за опровержение и любые иные аргументированные соображения.
посмотрел пример в отладчике — выяснилось следующее:
— нужно ли загружать dll определяет фукнция LdrpCheckForLoadedDll, при повторной загрузке SideBySide.dll она возвращает false и поэтому dll загружается повторно, при этом взводится флаг 0x10000000 в LDR_MODULE.flags
(в WinDbg это можно увидеть с помощью команды !dlls -c <адрес модуля>)
описание LDR_MODULE http://undocumented.ntinternals.net/UserMode/Structures/LDR_MODULE.html
— эта логика как-то связана с тем, что предыдущая загрузка была в некотором activation context'е (т.е. с использованием манифеста), а текущая загрузка происходит через импорт другой dll.
— в последующих загрузках (через таблицу импорта) LdrpCheckForLoadedDll уже будет проверять флаг 0x10000000)
является ли это багом или фичей — непонятно. механизм загрузки модулей через LoadLibrary и таблицы импорта разный. как минимум тем, что в первом случае передаются пути для поиска dll, а во втором — нет.
могу предположить, что манифесты предназначены для динамической загрузки модулей, в случае статической загрузки манифест никак не влияет на поиск подходящего модуля, с этим возможно и связан тот факт, что при статической загрузке ранее загруженного с помощью манифеста модуля будет создана отдельная копия. наличие специального флага 0x10000000 и логики по его проверке подтверждает версию, что все-таки это фича, а не баг
Подобный эффект должен наблюдаться, если набор прав у загружающих dll моделей, разный. У Рихтера эта ситуация подробно описана. Однако обычной ситуация является, когда dll грузят разные процессы, как могут быть разные права в пределах одного процесса, мне не понятно. К сожалению, не знаю, что скрывается за сочетанием "SxS", может оно и обеспечивает секьюрити?
Еще одна возможность — com-объект создается как внепроцессный.