Запрет выгрузки dll до момента освобождения последнего объекта из dll
От: Aniskin  
Дата: 25.10.18 18:12
Оценка: 3 (1)
Что бы изучить получше оконную систему Windows решил я написать более-менее сложный контрол на чистом WinApi. Для одного из проектов мне нужно было дерево, отображающее себя несколько иначе по сравнению со стандартным SysTreeView32, его и решил реализовать. Контрол я успешно написал, оформил по взрослому, но осталась у меня одна не решенная проблема.

Приличный контрол должен уметь UI Automation. Его я тоже реализовал. Но возникло одно но. Сам контрол реализован в dll (для использования в проекте на любом языке). Когда какое-нибудь приложение типа Narrator желает поработать с моим контролом через UI Automation, то в контрол начинают посылаться сообщения WM_GETOBJECT (OBJID_CLIENT), в ответ на которое я возвращаю экземпляр объекта, реализующего IAccessible. И как бы все работает, Narrator видит отдельные ноды дерева, произносит человеческим голосом текст ноды, и т.д. Проблема возникает при завершении хост приложения. Хост приложение ничего не знает о том, что были запросы WM_GETOBJECT и есть живые объекты из dll, и может выгрузить dll, а лишь затем вызвать OleUninitialize. Соответственно при OleUninitialize, когда вызывается последний Release объекта, происходит AV.

Вопрос – как то можно это исправить в самой dll?

(Я нашел временный workaround – в ответ на WM_GETOBJECT (OBJID_QUERYCLASSNAMEIDX) я возвращаю 65536+25, это позволяет системе создавать собственный внутренний прокси, реализующий IAccessible, который общается с моим деревом на чистом WinApi, но мне нужна имена моя реализация.)
Re[2]: Запрет выгрузки dll до момента освобождения последнего о
От: Aniskin  
Дата: 26.10.18 18:48
Оценка: 1 (1)
Здравствуйте, Alexander G, Вы писали:

AG>Я не думал, что такое кто-то делает в 2018.


Ключевое — "Что бы изучить получше оконную систему Windows".

AG>FreeLibrary для последнего Release


В этом и состоит вся проблема. Если я вызываю в последнем Release FreeLibrary, то FreeLibrary, отработав свою работу, вернется обратно в мой Release, который находится в адресном пространстве моей выгруженной dll.
Re[11]: Запрет выгрузки dll до момента освобождения последнег
От: ononim  
Дата: 26.10.18 18:15
Оценка: +1
O>>Но ведь ктото же загрузил ту длл в процесс. Вот та сущность которая длл загрузила, и должна заниматься ее безопасной выгрузкой.
A>Вот этого я и пытаюсь всячески избежать. Процессу незачем вникать в проблемы dll, он просто должен сделать LoadLib, поработать с ней, FreeLib.
У решения с LoadLib/FreeLib с внутренне-отложенной обработкой выгрузки есть неявное усложнение: FreeLib _не_ гарантирует реальной выгрузки либы сейчас. Соответственно если вызывателю FreeLib реально нужна (чтоб удалить файл?) то таким образом рождается фундаментальный гейзенбаг. А если FreeLib ему реально не нужна — почему бы не заморозить либу в АП процесса до его завершения?
Как много веселых ребят, и все делают велосипед...
Re: Запрет выгрузки dll до момента освобождения последнего объекта из dll
От: bnk СССР http://unmanagedvisio.com/
Дата: 25.10.18 21:16
Оценка:
Здравствуйте, Aniskin, Вы писали:

A>Что бы изучить получше оконную систему Windows решил я написать более-менее сложный контрол на чистом WinApi. Для одного из проектов мне нужно было дерево, отображающее себя несколько иначе по сравнению со стандартным SysTreeView32, его и решил реализовать. Контрол я успешно написал, оформил по взрослому, но осталась у меня одна не решенная проблема.


A>Приличный контрол должен уметь UI Automation. Его я тоже реализовал. Но возникло одно но. Сам контрол реализован в dll (для использования в проекте на любом языке). Когда какое-нибудь приложение типа Narrator желает поработать с моим контролом через UI Automation, то в контрол начинают посылаться сообщения WM_GETOBJECT (OBJID_CLIENT), в ответ на которое я возвращаю экземпляр объекта, реализующего IAccessible. И как бы все работает, Narrator видит отдельные ноды дерева, произносит человеческим голосом текст ноды, и т.д. Проблема возникает при завершении хост приложения. Хост приложение ничего не знает о том, что были запросы WM_GETOBJECT и есть живые объекты из dll, и может выгрузить dll, а лишь затем вызвать OleUninitialize. Соответственно при OleUninitialize, когда вызывается последний Release объекта, происходит AV.


A>Вопрос – как то можно это исправить в самой dll?


Так не должно быть. Похоже на какой-то косяк при подсчете ссылок (как именно ты реализовал WM_GETOBJECT/IAccessible)
Когда система хочет выгрузить DLL, она ее предварительно спрашивает, "можно ли"? (DllCanUnloadNow)?
Твоя DLL похоже отвечает "да, можно", хотя у нее есть живые объекты.

К сожалению, я в Delphi не в зуб ногой, как починить, посоветовать не смогу.
Re[2]: Запрет выгрузки dll до момента освобождения последнег
От: Aniskin  
Дата: 25.10.18 21:26
Оценка:
Здравствуйте, bnk, Вы писали:

bnk>Похоже на какой-то косяк при подсчете ссылок


Если бы был косяк с подсчетом ссылок на моей стороне, то объект просто бы терялся и была бы утечка памяти. Проблема возникает при OleUninitialize, т.е. (предполагаю что) COM освобождает ссылку на мой объект в то время как dll уже выгружена.

bnk>как именно ты реализовал WM_GETOBJECT/IAccessible


Стандартно:

Accessible := TTreeViewAccessible.Create(Self);
Result := LresultFromObject(IID_IAccessible, WParam, Accessible);

bnk>Когда система хочет выгрузить DLL, она ее предварительно спрашивает, "можно ли"? (DllCanUnloadNow)?


У меня же не COM сервер, и dll загружена не COM системой. COM вообще ни чего не знает о моей dll.

bnk>К сожалению, я в Delphi не в зуб ногой, как починить, посоветовать не смогу.


Дело в общем то не в Delphi. То, на чем написана dll, к моему вопросу имеет весьма косвенное отношение.
Отредактировано 25.10.2018 21:40 Aniskin . Предыдущая версия .
Re[3]: Запрет выгрузки dll до момента освобождения последнег
От: bnk СССР http://unmanagedvisio.com/
Дата: 25.10.18 21:58
Оценка:
Здравствуйте, Aniskin, Вы писали:

bnk>>Когда система хочет выгрузить DLL, она ее предварительно спрашивает, "можно ли"? (DllCanUnloadNow)?


A>У меня же не COM сервер, и dll загружена не COM системой. COM вообще ни чего не знает о моей dll.


Это windows спрашивает. DLL, которые нельзя выгружать, обычно отвечают, что их выгружать нельзя
Для случая COM, реализация обычно считает все созданные COM объекты (в твоем случае, все IAccessible), и если кто-то еще жив, возвращает FALSE.
Если не хочешь считать, можешь попробовать всегда возвращать FALSE из этой функции,
тогда система выгрузит DLL только когда будет завершать программу.
Re[4]: Запрет выгрузки dll до момента освобождения последнег
От: Aniskin  
Дата: 25.10.18 22:53
Оценка:
Здравствуйте, bnk, Вы писали:

bnk>>>Когда система хочет выгрузить DLL, она ее предварительно спрашивает, "можно ли"? (DllCanUnloadNow)?


A>>У меня же не COM сервер, и dll загружена не COM системой. COM вообще ни чего не знает о моей dll.


bnk>Это windows спрашивает.


Не могу подтвердить это утверждение. Мои познания говорят, что DllCanUnloadNow вызывается COM подсистемой для dll, которые были загружены вследствие вызова CoCreateInstanceEx (или других связанных функций). Для обычных dll она не вызывается, для них есть DllMain. Эксперимент подтвердил, DllCanUnloadNow не вызывается при простом FreeLibrary.
Re: Запрет выгрузки dll до момента освобождения последнего объекта из dll
От: Мёртвый Даун Россия  
Дата: 26.10.18 02:36
Оценка:
Здравствуйте, Aniskin, Вы писали:

A>Что бы изучить получше оконную систему Windows решил я написать более-менее сложный контрол на чистом WinApi. Для одного из проектов мне нужно было дерево, отображающее себя несколько иначе по сравнению со стандартным SysTreeView32, его и решил реализовать. Контрол я успешно написал, оформил по взрослому, но осталась у меня одна не решенная проблема.


А не на Delphi есть?
Только Путин, и никого кроме Путина! О Великий и Могучий Путин — царь на веки веков, навсегда!
Смотрю только Соловьева и Михеева, для меня это самые авторитетные эксперты.
КРЫМ НАШ! СКОРО И ВСЯ УКРАИНА БУДЕТ НАШЕЙ!
Re[5]: Запрет выгрузки dll до момента освобождения последнег
От: Mystic Artifact  
Дата: 26.10.18 05:21
Оценка:
Здравствуйте, Aniskin, Вы писали:

Если вам нужен способ предотвратить выгрузку... то можете вызвать LoadLibrary своей dll внутри своей dll.
Re: Запрет выгрузки dll до момента освобождения последнего о
От: ononim  
Дата: 26.10.18 06:49
Оценка:
1) Ну так тот кто загрузил длл — пускай спрашивает ее перед выгрузкой, можно ли ее выгрущить. С помощью той же DllCanUnloadNow.
2) Если 1 невозможен (например длл загружается изза SetWindowsHookEx и нежелательная выгрузка происходит изза того что процесс который поставил хук случайно сдыхает), то dll может вызвать LoadLibrary сама на себя чтобы самозалочиться, а когда решит саморазлочиться — запустит рабочий поток, который вызовет FreeLibraryAndExitThread на результат той LoadLibrary, при этом потребуется хитрый трюк, чтобы исключить race condition когда поток-разлочиватель будет выгружать длл, а тот кто его запустил — все еще исполняет ее код.
Как много веселых ребят, и все делают велосипед...
Отредактировано 26.10.2018 7:01 ononim . Предыдущая версия .
Re[2]: Запрет выгрузки dll до момента освобождения последнего объекта из dll
От: Aniskin  
Дата: 26.10.18 12:07
Оценка:
Здравствуйте, Мёртвый Даун, Вы писали:

МД>А не на Delphi есть?


В смысле есть ли этот же код, но с перламутровыми пуговицами написанный на C++? Нет. Для C++ есть dll, которая используется аналогично ComCtl32.dll.
Re[6]: Запрет выгрузки dll до момента освобождения последнег
От: Aniskin  
Дата: 26.10.18 12:11
Оценка:
Здравствуйте, Mystic Artifact, Вы писали:

MA>можете вызвать LoadLibrary своей dll внутри своей dll.


Ок, я вызываю LoadLibrary. Это приводит к тому, что dll будут висеть до конца жизни процесса. Соответственно я должен когда-нибудь из dll вызвать FreeLibrary. Но FreeLibrary вызовет выгрузку dll, и попытка возврата из вызова FreeLibrary в выгруженную dll может иметь трагические последствия.
Re[2]: Запрет выгрузки dll до момента освобождения последнего о
От: Aniskin  
Дата: 26.10.18 12:27
Оценка:
Здравствуйте, ononim, Вы писали:

O>1) Ну так тот кто загрузил длл — пускай спрашивает ее перед выгрузкой, можно ли ее выгрущить. С помощью той же DllCanUnloadNow.


Очень не хочется ставить никаких дополнительных условий на использование dll (типа можете загрузить ее когда угодно, но выгружать только по вторникам).

O>2) dll может вызвать LoadLibrary сама на себя чтобы самозалочиться, а когда решит саморазлочиться — запустит рабочий поток, который вызовет FreeLibraryAndExitThread на результат той LoadLibrary, при этом потребуется хитрый трюк, чтобы исключить race condition когда поток-разлочиватель будет выгружать длл, а тот кто его запустил — все еще исполняет ее код.


Вот о чем то таком я и мечтал. Но не могу до конца понять, в какой момент мне можно вызвать FreeLibraryAndExitThread из доп потока. Это должен быть такой момент, когда последний вызов IAccessible.Release отработал и произошел выход из процедуры. Вопрос переформулируется на "Как исполнить этот хитрый трюк".
Re[7]: Запрет выгрузки dll до момента освобождения последнег
От: ononim  
Дата: 26.10.18 13:27
Оценка:
A>Ок, я вызываю LoadLibrary. Это приводит к тому, что dll будут висеть до конца жизни процесса. Соответственно я должен когда-нибудь из dll вызвать FreeLibrary. Но FreeLibrary вызовет выгрузку dll, и попытка возврата из вызова FreeLibrary в выгруженную dll может иметь трагические последствия.
Вариант 1 — tail call (псевдоасм):
x86-32:
void __fastcall CallbackAndOptionallyFreeLibrary(void *arg, void *(__fastcall *pCallback)(void *arg))
{
    call edx ; вызовем pCallback, arg уже в ecx
    cmp eax, 0 ; если pCallback вернул не 0 - значит это hmod, которому надо сделать FreeLibrary
    jne need_free_library
    ret

need_free_library:
    pop rcx ; адрес возврата
    push eax ; dwExitCode - не заморачиваемся
    push eax ; hLibModule
    push rcx ; адрес возврата положим туда где ему место
    jmp FreeLibrary
}

x86-64:
void CallbackAndOptionallyFreeLibrary(void *arg, void *(*pCallback)(void *arg))
{
    call rdx
    cmp rax, 0
    jne need_free_library
    ret

need_free_library:
    mov rcx, rax
    jmp FreeLibrary
}

..но это лишь малая часть необходимого — надо же еще синхронизировать повторное создание объекта, чтобы в процессе создания не произошел выход.

Вариант 2:
Тот кто снаружи (управляющий процесс) должен в правильный момент вызвать CreateRemoteThread(RemoteProcess, ..., &FreeLibrary, RemoteLibraryHandle,...); или CreateRemoteThread, который вызовет функцию, которая сделает деинициализацию и выйдет через FreeLibraryAndExitThread.
Как много веселых ребят, и все делают велосипед...
Отредактировано 26.10.2018 13:29 ononim . Предыдущая версия .
Re[8]: Запрет выгрузки dll до момента освобождения последнег
От: Aniskin  
Дата: 26.10.18 14:59
Оценка:
Здравствуйте, ononim, Вы писали:

O>Вариант 1 — tail call


Правильно ли я понял идею, что ее реализация должна выглядеть как то так:

0) В IAccessible.AddRef увеличиваю счетчик.
1) Если счетчик == 1 то вызываю LoadLibrary(Self)
2) В IAccessible.Release уменьшаю счетчик.
3.1) Если счетчик == 0 то вызываю CallbackAndFreeLibrary, которая вызывает деструктор моего объекта, производит манипуляции со стеком и передает управление в реальную FreeLibrary.
3.2) Иначе просто выхожу из IAccessible.Release

При этом стек вызовов внутри CallbackAndFreeLibrary будет примерно таким:
CallbackAndFreeLibrary
IAccessible.Release
SomeOleInternalFunction

И идея состоит в том, что бы выход из реальной FreeLibrary произошел сразу в SomeOleInternalFunction.

O>Вариант 2:

O>управляющий процесс

Вот ну очень не хочется плодить сущности без надобности.
Re[9]: Запрет выгрузки dll до момента освобождения последнег
От: ononim  
Дата: 26.10.18 16:29
Оценка:
A>При этом стек вызовов внутри CallbackAndFreeLibrary будет примерно таким:
A> CallbackAndFreeLibrary
A> IAccessible.Release
A> SomeOleInternalFunction

A>И идея состоит в том, что бы выход из реальной FreeLibrary произошел сразу в SomeOleInternalFunction.

Неа. CallbackAndFreeLibrary должен быть самым первым на стеке, кто находится в выгружаемой длл. То есть это он должен быть прописан в VTBL и вызывать реальную IAccessible.Release.

O>>Вариант 2:

O>>управляющий процесс
A>Вот ну очень не хочется плодить сущности без надобности.
Но ведь ктото же загрузил ту длл в процесс. Вот та сущность которая длл загрузила, и должна заниматься ее безопасной выгрузкой.
Как много веселых ребят, и все делают велосипед...
Re[10]: Запрет выгрузки dll до момента освобождения последнег
От: Aniskin  
Дата: 26.10.18 17:00
Оценка:
Здравствуйте, ononim, Вы писали:

A>>При этом стек вызовов внутри CallbackAndFreeLibrary будет примерно таким:

A>> CallbackAndFreeLibrary
A>> IAccessible.Release
A>> SomeOleInternalFunction

A>>И идея состоит в том, что бы выход из реальной FreeLibrary произошел сразу в SomeOleInternalFunction.

O>Неа. CallbackAndFreeLibrary должен быть самым первым на стеке, кто находится в выгружаемой длл. То есть это он должен быть прописан в VTBL и вызывать реальную IAccessible.Release.

(Возможно я путаю понятия первый/последний в стеке.) Сейчас имеем ситуацию, в которой SomeOleInternalFunction вызывает мою IAccessible.Release. Если я правильно понял, то SomeOleInternalFunction должна вызвать CallbackAndFreeLibrary, которая в свою должна вызывать IAccessible.Release. Т.е. CallbackAndFreeLibrary должна иметь прототип аналогичный IAccessible.Release. Но в этом случае мне не совсем понятно, зачем нужна отдельная функция CallbackAndFreeLibrary, ее тело может быть прямо в IAccessible.Release.

O>Но ведь ктото же загрузил ту длл в процесс. Вот та сущность которая длл загрузила, и должна заниматься ее безопасной выгрузкой.

Вот этого я и пытаюсь всячески избежать. Процессу незачем вникать в проблемы dll, он просто должен сделать LoadLib, поработать с ней, FreeLib.
Re[11]: Запрет выгрузки dll до момента освобождения последнег
От: ononim  
Дата: 26.10.18 17:13
Оценка:
A>(Возможно я путаю понятия первый/последний в стеке.) Сейчас имеем ситуацию, в которой SomeOleInternalFunction вызывает мою IAccessible.Release. Если я правильно понял, то SomeOleInternalFunction должна вызвать CallbackAndFreeLibrary, которая в свою должна вызывать IAccessible.Release. Т.е. CallbackAndFreeLibrary должна иметь прототип аналогичный IAccessible.Release.
Ага.

A>Но в этом случае мне не совсем понятно, зачем нужна отдельная функция CallbackAndFreeLibrary, ее тело может быть прямо в IAccessible.Release.

Чтоб писать тяжелый код на нормальном человеческом ЯП.
O>>Но ведь ктото же загрузил ту длл в процесс. Вот та сущность которая длл загрузила, и должна заниматься ее безопасной выгрузкой.
A>Вот этого я и пытаюсь всячески избежать. Процессу незачем вникать в проблемы dll, он просто должен сделать LoadLib, поработать с ней, FreeLib.
Дык это.. Может таки воспользоваться COMом, зарегистрировать свою дллку, и вместо LoadLib/FreeLib процесс будет делать CoCreateInstance/Release? Тогда по идее заработает DllCanUnloadNow.
Как много веселых ребят, и все делают велосипед...
Re[12]: Запрет выгрузки dll до момента освобождения последнег
От: Aniskin  
Дата: 26.10.18 17:39
Оценка:
O>Может таки воспользоваться COMом
Убивает портабельность.
Re[5]: Запрет выгрузки dll до момента освобождения последнег
От: bnk СССР http://unmanagedvisio.com/
Дата: 26.10.18 18:10
Оценка:
Здравствуйте, Aniskin, Вы писали:

A>Не могу подтвердить это утверждение. Мои познания говорят, что DllCanUnloadNow вызывается COM подсистемой для dll, которые были загружены вследствие вызова CoCreateInstanceEx (или других связанных функций). Для обычных dll она не вызывается, для них есть DllMain. Эксперимент подтвердил, DllCanUnloadNow не вызывается при простом FreeLibrary.


То есть ты говоришь, что когда Narrator использует твою DLL, то DllCanUnloadNow не вызывается? Это было бы странно.
Да, COM у тебя похоже все же есть (но возможно что ты пока этого не осознал :), потому как IAccessible — это COM.
Уверен, что функция с названием "DllCanUnloadNow" в твоей DLL (1) существует и (2) экспортируется (т.е. может быть вызвана) и (3) возвращает FALSE при вызове?
Re[6]: Запрет выгрузки dll до момента освобождения последнег
От: Aniskin  
Дата: 26.10.18 18:20
Оценка:
Здравствуйте, bnk, Вы писали:

bnk>То есть ты говоришь, что когда Narrator использует твою DLL, то DllCanUnloadNow не вызывается? Это было бы странно.


Все что делает Narrator (в лице COM системы) — это посылает WM_GETOBJECT оконному элементу. Он в принципе не знает, что окно создано с использованием dll.

bnk>IAccessible — это COM.


Обратного я и не утверждал.

bnk>Уверен, что функция с названием "DllCanUnloadNow" в твоей DLL (1) существует и (2) экспортируется (т.е. может быть вызвана)


Уверен.

bnk> и (3) возвращает FALSE при вызове?


Не вызывается.
Re[12]: Запрет выгрузки dll до момента освобождения последнег
От: Aniskin  
Дата: 26.10.18 18:27
Оценка:
O>У решения с LoadLib/FreeLib с внутренне-отложенной обработкой выгрузки есть неявное усложнение: FreeLib _не_ гарантирует реальной выгрузки либы сейчас. Соответственно если вызывателю FreeLib реально нужна (чтоб удалить файл?) то таким образом рождается фундаментальный гейзенбаг.

Мне в принципе не нужна гарантия того, что FreeLib точно выгрузит dll. Мне нужно в конечном итоге только то, что бы у dll был простой контракт на использование — 1) LoadLib, 2) UseLib, 3) FreeLib. Без всяких "FreeLib можно только по вторникам".

O>А если FreeLib ему реально не нужна — почему бы не заморозить либу в АП процесса до его завершения?


Можно и так, но этому противится мой внутренний перфекционист. Если можно сделать по нормальному, то почему бы и не сделать.
Re: Запрет выгрузки dll до момента освобождения последнего о
От: Alexander G Украина  
Дата: 26.10.18 18:41
Оценка:
Здравствуйте, Aniskin, Вы писали:

A>Что бы изучить получше оконную систему Windows решил я написать более-менее сложный контрол на чистом WinApi. Для одного из проектов мне нужно было дерево, отображающее себя несколько иначе по сравнению со стандартным SysTreeView32, его и решил реализовать. Контрол я успешно написал, оформил по взрослому, но осталась у меня одна не решенная проблема.


Я не думал, что такое кто-то делает в 2018.

A>Проблема возникает при завершении хост приложения. Хост приложение ничего не знает о том, что были запросы WM_GETOBJECT и есть живые объекты из dll, и может выгрузить dll, а лишь затем вызвать OleUninitialize. Соответственно при OleUninitialize, когда вызывается последний Release объекта, происходит AV.


A>Вопрос – как то можно это исправить в самой dll?


Может хранить референс на DLL в объекте?

GetModuleHandleEx со своим адресом с GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS и без GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT для первого AddRef
FreeLibrary для последнего Release
Русский военный корабль идёт ко дну!
Отредактировано 26.10.2018 18:44 Alexander G . Предыдущая версия .
Re: Запрет выгрузки dll до момента освобождения последнего о
От: Mystic Artifact  
Дата: 26.10.18 18:47
Оценка:
Здравствуйте, Aniskin, Вы писали:

А вот в мсдн пишут, что можно вроде просто вызвать
UiaReturnRawElementProvider(hwnd, 0, 0, NULL)
— врут? (Где-нибудь в WM_DESTROY).
Отредактировано 26.10.2018 18:50 Mystic Artifact . Предыдущая версия .
Re[2]: Запрет выгрузки dll до момента освобождения последнего о
От: Aniskin  
Дата: 26.10.18 18:58
Оценка:
Здравствуйте, Mystic Artifact, Вы писали:

MA>можно вроде просто вызвать UiaReturnRawElementProvider(hwnd, 0, 0, NULL)


Надо проверить.
Re[2]: Запрет выгрузки dll до момента освобождения последнего о
От: Aniskin  
Дата: 26.10.18 19:58
Оценка:
MA>А вот в мсдн пишут, что можно вроде просто вызвать UiaReturnRawElementProvider

Имею AV внутри UiaReturnRawElementProvider. Возможно, эта функция предназначена для тех, кто реализует полноценный UI Automation, я же реализую только Microsoft Active Accessibility.
Re[3]: Запрет выгрузки dll до момента освобождения последнего о
От: Mystic Artifact  
Дата: 26.10.18 20:25
Оценка:
Здравствуйте, Aniskin, Вы писали:

A>Имею AV внутри UiaReturnRawElementProvider. Возможно, эта функция предназначена для тех, кто реализует полноценный UI Automation, я же реализую только Microsoft Active Accessibility.


Понятно.

Тогда я бы смотрел в сторону NotifyWinEvent EVENT_OBJECT_DESTROY или как-то убедится, что это событие приходит. В любом случае трюки с FreeLibrary кажутся здесь неуместными, т.к. оно как-то должно делаться и без них. (Подозрение)
Re[4]: Запрет выгрузки dll до момента освобождения последнего о
От: Aniskin  
Дата: 26.10.18 20:30
Оценка:
MA>Тогда я бы смотрел в сторону NotifyWinEvent EVENT_OBJECT_DESTROY или как-то убедится, что это событие приходит.

Про EVENT_OBJECT_DESTROY пишут следующее: An object has been destroyed. The system sends this event for the following user interface elements: ... and window object. Т.е. система самостоятельно посылает это сообщение при уничтожении окна.
Re[5]: Запрет выгрузки dll до момента освобождения последнего о
От: Mystic Artifact  
Дата: 26.10.18 20:41
Оценка:
Здравствуйте, Aniskin, Вы писали:

A>Про EVENT_OBJECT_DESTROY пишут следующее: An object has been destroyed. The system sends this event for the following user interface elements: ... and window object. Т.е. система самостоятельно посылает это сообщение при уничтожении окна.


А посылает? Я так понимаю, она его посылает из DefWindowProc, который можно и не вызывать в своем контроле. Я не знаю. Просто как идея.

Там где-то между буквами у них пишут, что это обязанность сервера.
Re[6]: Запрет выгрузки dll до момента освобождения последнего о
От: Aniskin  
Дата: 26.10.18 21:07
Оценка:
MA>А посылает?

Я не знаю, я игрался с AccEvent.exe, но не смог добиться того, что бы он хотя бы для обычных окон показывал create/destroy события.

MA>Я так понимаю, она его посылает из DefWindowProc


WM_DESTROY/WM_NCDESTROY я посылаю в DefWindowProc. Ну, и не исключено, что EVENT_OBJECT_DESTROY может посылаться и из DestroyWindow.
Re: Запрет выгрузки dll до момента освобождения последнего объекта из dll
От: Mystic Artifact  
Дата: 27.10.18 05:55
Оценка:
Здравствуйте, Aniskin, Вы писали:

Другая идея — вызывать CoDisconnectObject тоже на WM_DESTROY.
Re[3]: Запрет выгрузки dll до момента освобождения последнего о
От: Alexander G Украина  
Дата: 27.10.18 16:05
Оценка:
Здравствуйте, Aniskin, Вы писали:

A>В этом и состоит вся проблема. Если я вызываю в последнем Release FreeLibrary, то FreeLibrary, отработав свою работу, вернется обратно в мой Release, который находится в адресном пространстве моей выгруженной dll.


Тогда классический костыль с запуском потока, который вызовет FreeLibraryAndExitThread?
Русский военный корабль идёт ко дну!
Re[2]: Запрет выгрузки dll до момента освобождения последнего объекта из dll
От: Aniskin  
Дата: 27.10.18 16:25
Оценка:
MA>Другая идея — вызывать CoDisconnectObject тоже на WM_DESTROY.

Проблема в том, что после LresultFromObject я освобождаю свой объект и более не владею ссылкой на него. Т.е. на каждый WM_GETOBJECT я создаю отдельный экземпляр IAccessible. Причине в том, что помимо IAccessible я еще реализую IEnumVARIANT, которую система использует для перечисления детей. Если я буду отдавать кэшированный IAccessible, т.е. существует вероятность, что два клиента будут одновременно перечислять детей, и внутренний счетчик будут некорректен.
Re[4]: Запрет выгрузки dll до момента освобождения последнего о
От: Aniskin  
Дата: 27.10.18 16:26
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>Здравствуйте, Aniskin, Вы писали:


A>>В этом и состоит вся проблема. Если я вызываю в последнем Release FreeLibrary, то FreeLibrary, отработав свою работу, вернется обратно в мой Release, который находится в адресном пространстве моей выгруженной dll.


AG>Тогда классический костыль с запуском потока, который вызовет FreeLibraryAndExitThread?


Та же проблема. FreeLibraryAndExitThread должен быть гарантированно вызван после того, как последний Release отработал и произошел выход из него.
Re[5]: Запрет выгрузки dll до момента освобождения последнего о
От: Alexander G Украина  
Дата: 27.10.18 16:42
Оценка:
Здравствуйте, Aniskin, Вы писали:

A>Та же проблема. FreeLibraryAndExitThread должен быть гарантированно вызван после того, как последний Release отработал и произошел выход из него.


Ох, да.
Кажется, есть идея с ассемблерной вставкой. Не то, чтобы чистое решение, но может, прокатит.
Сделать, чтобы вызов FreeLibrary был хвостовым.
(Нам всё равно, что вернёт IUnknown::Release(), это ведь чисто отладочная информация)

ULONG UiAutomationImpl::Release()
{
  ...
  PUSH module_handle
  JMP FreeLibrary // вернёт уже вызвавшему Release
}
Русский военный корабль идёт ко дну!
Re: Запрет выгрузки dll до момента освобождения последнего объекта из dll
От: Vi2 Удмуртия http://www.adem.ru
Дата: 27.10.18 16:55
Оценка:
Здравствуйте, Aniskin, Вы писали:

A>... Проблема возникает при завершении хост приложения. Хост приложение ничего не знает о том, что были запросы WM_GETOBJECT и есть живые объекты из dll, и может выгрузить dll, а лишь затем вызвать OleUninitialize. Соответственно при OleUninitialize, когда вызывается последний Release объекта, происходит AV.


A>Вопрос – как то можно это исправить в самой dll?


Посмотри тут Бага в CoUninitialize, может это твой случай.
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! © КВН НГУ
Re[6]: Запрет выгрузки dll до момента освобождения последнего о
От: Aniskin  
Дата: 27.10.18 17:23
Оценка:
AG>Кажется, есть идея с ассемблерной вставкой.

Как то вы с ononim мыслите одинаково Оба прошли в этом топике практически один и тот же путь (просто FreeLibrary в Release/поток с FreeLibraryAndExitThread/асм) и оба в конечном итоге пришли к одному и тому же решению на ассемблере

Идея мне нравиться, планирую ее реализовать (правда, последний раз писал на ассемблере 20 лет назад, и это был ассемблер Z80).
Re[2]: Запрет выгрузки dll до момента освобождения последнего объекта из dll
От: Aniskin  
Дата: 27.10.18 17:33
Оценка:
Здравствуйте, Vi2, Вы писали:

Vi2>Посмотри тут Бага в CoUninitialize, может это твой случай.


Нет, к сожалению (к счастью?), не мой.
Re[7]: Запрет выгрузки dll до момента освобождения последнего о
От: ononim  
Дата: 27.10.18 18:42
Оценка:
A>Как то вы с ononim мыслите одинаково Оба прошли в этом топике практически один и тот же путь (просто FreeLibrary в Release/поток с FreeLibraryAndExitThread/асм) и оба в конечном итоге пришли к одному и тому же решению на ассемблере
Я не в этом топике прошел этот путь. Я это реализовывал в продакшене
Как много веселых ребят, и все делают велосипед...
Re: Запрет выгрузки dll до момента освобождения последнего объекта из dll
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 06.11.18 18:18
Оценка:
Здравствуйте, Aniskin, Вы писали:

A>Вопрос – как то можно это исправить в самой dll?


Я так понял, что DLL загружается минуя COM и тебе нужно чтобы COM начал опрашивать DllCanUnloadNow после того как ты из неё вернул COM-объект.

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

Я несколько раз видел упоминание (или даже описание) такой функции. Так что покопайся в этом направлении. Может найдешь её

Если тут уже такое предложили, то звиняйте
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Re[2]: Запрет выгрузки dll до момента освобождения последнего объекта из dll
От: Aniskin  
Дата: 07.11.18 05:01
Оценка:
Здравствуйте, Коваленко Дмитрий, Вы писали:

КД>тебе нужно чтобы COM начал опрашивать DllCanUnloadNow после того как ты из неё вернул COM-объект.


Это было бы неплохим решением.

КД>по-моему в COM таки есть функция, которой подсовывают HINSTANCE уже загруженной DLL, чтобы он включил её в список загруженных COM-серверов процесса.


Никогда о такой функции не слышал, но попробую поискать.
Re[3]: Запрет выгрузки dll до момента освобождения последнего объекта из dll
От: Коваленко Дмитрий Россия http://www.ibprovider.com
Дата: 07.11.18 06:14
Оценка:
Здравствуйте, Aniskin, Вы писали:

КД>>по-моему в COM таки есть функция, которой подсовывают HINSTANCE уже загруженной DLL, чтобы он включил её в список загруженных COM-серверов процесса.


A>Никогда о такой функции не слышал, но попробую поискать.


https://docs.microsoft.com/ru-ru/windows/desktop/api/objbase/nf-objbase-coloadlibrary

HINSTANCE CoLoadLibrary(
  LPOLESTR lpszLibName,
  BOOL     bAutoFree
);

Я почему-то думал, что можно было HINSTANCE передавать.

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