Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Корректная работа с хендлами

602
08 сентября 2008 года
KPI Student
265 / / 16.12.2006
Есть класс управления плагином. Плагин сделан в виде DLL.
Естественно, класс переопределяет стандартный конструктор копирования по умолчанию, поскольку простое копирование числовых значений хендлов некорректно, т.к. при удалении экземпляра класса будет вызван соответствующий деструктор, закрывающий хендлы, в результате чего хендл перестанет быть валидным. Вывод - нужно дублировать хендлы.

 
Код:
CPlugin::CPlugin(const CPlugin & src)
{
    bool rc = DuplicateHandle(GetCurrentProcess(),src.m_hModule,GetCurrentProcess(),&m_hModule,0,false,DUPLICATE_SAME_ACCESS);
    bool rct= DuplicateHandle(GetCurrentProcess(),src.m_hThread,GetCurrentProcess(),&m_hThread,0,false,DUPLICATE_SAME_ACCESS);
    if ((rc && rct) == false)
    {
        MessageBox(NULL,TEXT("Error in CPlugin(const CPlugin & src)"),TEXT("Core error"),MB_ICONERROR);
    }
}


При этом rc всегда равен false, соответственно после попытки дублирования хендла модуля библиотеки последняя ошибка устанавливается в ERROR_INVALID_HANDLE.

Хендл потока копируется корректно.

Вопрос - можно ли, и если да, каким образом копировать хендлы библиотек, а именно - что "необходимо и достаточно" для того, чтобы счетчик пользователей хендла этого типа увеличился/уменьшился на 1.
3
09 сентября 2008 года
Green
4.8K / / 20.01.2000
А зачем перекладывать архитектурную проблему на ОС ?
Не вижу оснований дублировать хендлы.
Создай класс-обертку над хендлом с подсчетом ссылок.
602
09 сентября 2008 года
KPI Student
265 / / 16.12.2006
Цитата:
А зачем перекладывать архитектурную проблему на ОС ?
Не вижу оснований дублировать хендлы.
Создай класс-обертку над хендлом с подсчетом ссылок.


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

Цитата:
Создай класс-обертку над хендлом с подсчетом ссылок.


Другими словами: "Продублируй то, что уже реализовано системой для всех хендлов, своими руками"

Цитата:
А зачем ...

Ребят, вчитывайтесь пожалуйста, вопрос стоял отнюдь не такой: "Как избежать дублирования хендла модуля", и не такой: "А как выдумаете, зачем я..."
Вопрос стоял так: "Как корректно скопировать хендл модуля, чтобы при его закрытии система не сочла модуль не нужным и не выгрузила его, а также для того, чтобы не произошло ситуации с хранением и использованием уже закрытых хенлов". Разжевал.

Цитата: Green
А зачем ...

Все же объясняю, зачем.
Еще раз:

Код:
void f()
{[INDENT]std::list<CPlugin> lst;
if ( . . . )
{[INDENT]CPlugin newp( . . . ); // тут создался экземпляр класса, он содержит хендлы молуля своей ДЛЛ плагина, и хендл созданного ему отдельного замороженного потока
lst.push( newp ); // тут создается дополнительное звено списка (включающее экземпляр CPlugin), а затем вызывается конструктор копирования или операция присваивания (суть одна). Которая по умолчанию копирует числовые значения хендлов (предположим, хендл модуля == 5)[/INDENT]}[INDENT]//конец блока. Удаление всех его локальных переменных. В том числе вызов деструктора для newp. Чтобы не возникало утечки памяти во время работы программы, он должен освободить хендл. Что он и делает. Теперь хендл с числовым значением 5 закрыт (не валидный)[/INDENT]
. . .

CPlugin next = lst.pop();[INDENT]// Тут снова (упрощено) идет вызов конструктора копирования, который снова копирует числовое значение хендла модуля (5), уже невалидного, в next, далее удаляется последнее звено списка, в том числе, вызывается деструктор для экземпляра СPlugin, который в этом звене хранится, и который опять же, содержит неверное значение хендла модуля (5). Деструктор закрывает этот хенл. Это недопустимо - хендл неверный.[/INDENT]
/* а далее, каким-либо образом происходят попытки использовать next (который содержит "дважды закрытый" хендл модуля, с числовым значением 5) */

}
[/INDENT]


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

Очень надеюсь все же постичь, каким образом корректно скопировать хендл модуля, чтобы при его закрытии система не сочла модуль не нужным и не выгрузила его, а также для того, чтобы не произошло ситуации с хранением и использованием уже закрытых хендлов, два простейших примера которой приведено выше.
288
09 сентября 2008 года
nikitozz
1.2K / / 09.03.2007
Вопрос к автору. Правильно ли я понял вас, что src.m_hModule имеет тип HMODULE и получен в результате приблизительно такого вызова
 
Код:
m_hModule = LoadLibrary("SomeModule.dll");

?
3
09 сентября 2008 года
Green
4.8K / / 20.01.2000
Цитата: KPI Student
... и вследствие того, что обращаться к этому хендлу нужно из нескольких потоков, используй глобалыные переменные (прим:там, где можно этого избежать)


Зачем?!

Цитата: KPI Student
, со всеми прелестями синхронизации потоков, его использующих...


Да, но эту часть можно упростить. А возможно, можно и не реализовывать. Все зависит от архитектуры.
Кстати, ты уверен, что при твоем решении с DuplicateHandle, тебе не нужна будет синхронизация? Точно уверен?

Цитата: KPI Student

Другими словами: "Продублируй то, что уже реализовано системой для всех хендлов, своими руками"


Вся проблема в том, что ты используешь "тяжелую артиллерию" для относительно простой операции.
Подробнее, смотри здесь:
http://blog.not-a-kernel-guy.com/2006/10/31/93

Цитата: KPI Student

Ребят, вчитывайтесь пожалуйста, вопрос стоял отнюдь не такой: "Как избежать дублирования хендла модуля", и не такой: "А как выдумаете, зачем я..."


Задумайся, пожалуйста над тем, что иногда на вопрос "как мне сделать глупость", отвечают "как её не делать". И над этим стоит подумать, а не идти напролом.

P.S. Кстати, а ты уверен, что тебе вообще нужно копировать хендлы? Думаю, что можно обойтись и без копирования и без подсчета ссылок и без прочих усложнений.

602
09 сентября 2008 года
KPI Student
265 / / 16.12.2006
Цитата: nikitozz
Вопрос к автору. Правильно ли я понял вас,

Вы меня правильно поняли. Конечно же, хендл модуля имеет этот тип и получен подобным образом.
Виноват, не уточнил.

 
Код:
class CPlugin
{
private:
...
HMODULE  hModule;
HANDLE   hThread;
602
09 сентября 2008 года
KPI Student
265 / / 16.12.2006
Цитата:
Вся проблема в том, что ты используешь "тяжелую артиллерию" для относительно простой операции.
Подробнее, смотри здесь:
http://blog.not-a-kernel-guy.com/2006/10/31/93

Спасибо, просмотрел, добавил в закладки. интересно, доработаю и буду использовать, однако в моем случае это неприменимо по той причине, что ни использованный в классе CloseHandle ни DuplicateHandle неприменимы (проверено экспериментально) к хендлам модулей (HMODULE).

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

Цитата:
Задумайся, пожалуйста над тем, что иногда на вопрос "как мне сделать глупость", отвечают "как её не делать". И над этим стоит подумать, а не идти напролом.

Согласен. Предложите, пожалуйста, вариант корректного копирования.

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

"обычный" конструктор - выделяет память под приватную структуру, инициаизирует хендл заданным значением, и его счетчик использования единицей.

конструктор копирования - присваивает экземпляру-копии указатель на ту же (уже выделенную) структуру. инкрементирует счетчик ссылок.

деструктор - выполняет декремент счетчика ссылок. Если счетчик ссылок стал равен 0, то закрывает хендл модуля (FreeLibrary(..)) и удаляет выделенную структуру.


Перечитав то, что надумал, задаюсь вопросом -- а не реализовываю ли я заново механизм работы с хендлами, уже реализованный в Винде? Думаю, что да. Возможно, я, конечно неправильно понял ваш совет, в таком случае скажите, как понять правильно.

288
09 сентября 2008 года
nikitozz
1.2K / / 09.03.2007
Цитата: KPI Student
Вы меня правильно поняли. Конечно же, хендл модуля имеет этот тип и получен подобным образом.
Виноват, не уточнил.
 
Код:
class CPlugin
{
private:
...
HMODULE  hModule;
HANDLE   hThread;



Ok. Все понятно. Тогда, собственно, проблема заключается в том, что HMODULE и HANDLE это абсолютно разные типы. HMODULE - это просто базовый адрес, по-которому загружена DLL в адресное пространство процесса. HANDLE - это индекс (или байтовое смещение) строки в таблице описателей объектов ядра процесса. Короче говоря код

 
Код:
DuplicateHandle(GetCurrentProcess(),src.m_hModule,GetCurrentProcess(),&m_hModule,0,false,DUPLICATE_SAME_ACCESS);

не выполнится успешно, так как src.m_hModule - это не HANDLE.
Решение - не париться с копированием описателей. Модуль выгрузится либо только при явном вызове FreeLibrary либо при завершении процесса.
14
09 сентября 2008 года
Phodopus
3.3K / / 19.06.2008
Решение - LoadLibrary() вызывать в конструкторе (/копирования). Вторая копия библиотеки никогда не подгрузится, а винда ведет внутренний счетчик ссылок увеличивающийся при Load и освобождающийся при FreeLibrary()
602
09 сентября 2008 года
KPI Student
265 / / 16.12.2006
Цитата:
Решение - не париться с копированием описателей. Модуль выгрузится либо только при явном вызове FreeLibrary либо при завершении процесса.

Вот в том-то и пробема, что FreeLibrary вызывается в деструкторе... отсюда если не увеличить счетчик ссылок на описатель то будет беда...
Кстати:

Цитата:
The FreeLibrary function decrements the reference count of the loaded dynamic-link library (DLL). When the reference count reaches zero, the module is unmapped from the address space of the calling process and the handle is no longer valid.


Цитата:
Each process maintains a reference count for each loaded library module. This reference count is incremented each time LoadLibrary is called and is decremented each time FreeLibrary is called. A DLL module loaded at process initialization due to load-time dynamic linking has a reference count of one. This count is incremented if the same module is loaded by a call to LoadLibrary.


Цитата:
The GetModuleHandle function returns a handle to a mapped module without incrementing its reference count. Therefore, use care when passing the handle to the FreeLibrary function, because doing so can cause a DLL module to be unmapped prematurely.

Буду копать в сторону LoadLibrary. Правда, там могут быть подводные камни насчет неожиданных вызовов DllEntryРoint и получения по хендлу имени модуля, но думаю, это решабельно. Еще думаю над архитектурой всей этой системы - плагины будут писаться кроме меня еще одним-двумя прогерами, возможно на дельфях, надо интерфейс сделать максимально прозрачно.

З.Ы. А я, дурак, еще на асме это собирался кодить... )))

602
09 сентября 2008 года
KPI Student
265 / / 16.12.2006
Цитата: Phodopus
Решение - LoadLibrary() вызывать в конструкторе (/копирования). Вторая копия библиотеки никогда не подгрузится, а винда ведет внутренний счетчик ссылок увеличивающийся при Load и освобождающийся при FreeLibrary()



Не видел, пока писал ответ. Спасибо, именно туда и копаю. Или LoadLibrary, или LoadLibraryEx

14
10 сентября 2008 года
Phodopus
3.3K / / 19.06.2008
[QUOTE=MSDN]
If the module is a DLL not already mapped for the calling process, the system calls the DLL's DllMain function with the DLL_PROCESS_ATTACH value.
[/QUOTE]
Так что нормально все
3
12 сентября 2008 года
Green
4.8K / / 20.01.2000
Цитата: KPI Student
Спасибо, просмотрел, добавил в закладки. интересно, доработаю и буду использовать, однако в моем случае это неприменимо по той причине, что ни использованный в классе CloseHandle ни DuplicateHandle неприменимы (проверено экспериментально) к хендлам модулей (HMODULE).


Про HMODULE уже проговорили в теме. Это, действительно , не хендл объекта и т.о. не копируется. Кстати, что копирует DuplicateHandle перечислено в таблице на странице с его описанием в MSDN.

Цитата: KPI Student

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


Обертка призвана сделать так, чтоб не копировать хендл. Если "в определенных ситуаиях все же придется копировать", значит обертка не справляется со своими обязанностями, а сл-но написана неправильно.

Цитата: KPI Student

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

"обычный" конструктор - выделяет память под приватную структуру, инициаизирует хендл заданным значением, и его счетчик использования единицей.

конструктор копирования - присваивает экземпляру-копии указатель на ту же (уже выделенную) структуру. инкрементирует счетчик ссылок.

деструктор - выполняет декремент счетчика ссылок. Если счетчик ссылок стал равен 0, то закрывает хендл модуля (FreeLibrary(..)) и удаляет выделенную структуру.


Ну в общем схема правильная. Есть обертка агрегирующая хендл и производящая подсчет ссылок, есть вспомогательный объект для автоматического приращения счетчика ссылок. Совсем несложная схема.

Цитата: KPI Student

Перечитав то, что надумал, задаюсь вопросом -- а не реализовываю ли я заново механизм работы с хендлами, уже реализованный в Винде? Думаю, что да. Возможно, я, конечно неправильно понял ваш совет, в таком случае скажите, как понять правильно.


Механизм хендлов в системе занимается (в т.ч.) подсчетом ссылок. Вышеописанный механизм тоже (и только) занимается подстчетом ссылок. Но вопрос в уровне на котором они это делают. Товой механизм локальный и более высокоуровневый.

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

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог