Сообщений 6    Оценка 180        Оценить  
Система Orphus

RSDN::CHandle

Обертка для HANDLE

Авторы: Павел Блудов
Владислав Чистяков

Версия текста: 1.1

Введение
Класс CHandle
Переменные класса
Конструкторы/деструкторы
Метод IsValid
Подробное описание
Заключение

Исходный текст (1.5kb)

Введение

Многие библиотеки, предназначенные для облегчения жизни программистов, предоставляют множество классов-оберток для упрощения работы с такими объектами, как HKEY, HWND, HMENU, объектами GDI. Тем не менее, такие популярные объекты как хендлы (handles) почему-то бывают забыты разработчиками библиотеки. В данной статье приведен простой пример такого класса.

Класс CHandle

Переменные класса

Представлены единственной

HANDLE m_h;

объявленной как public. Скорее всего, использовать ее непосредственно не понадобится. В классе реализованы операторы HANDLE и LPHANDLE, предоставляющие доступ к этой переменной.

Конструкторы/деструкторы

Самое узкое место этого (и ему подобных) классов. Дело в том, что будучи нормальным C++ классом, наш CHandle должен иметь конструктор копирования, позволяющий использовать одни HANDLE-объект несколькими CHandle-объектами. Но в таком случае, только деструктор последнего CHandle-объекта должен закрыть единственный HANDLE-объект. Т.е. необходимо реализовать механизм подсчета ссылок. В данном классе эта тяжкая работа поручена операционной системе; используется API ::DuplicateHandle(). Это не самое легковесное решение, и его использование сведено к минимуму. Конструктор копирования использует эту функцию, а обычный конструктор и переопределенный оператор присваивания нет.

RSDN::CHandle hRead, hWrite;
// Duplicate handle не используется
BOOL b = ::CreatePipe(&hRead, &hWrite, NULL, 0);

// Duplicate handle не используется
RSDN::CHandle hEvent = ::CreateEvent(NULL, false, false, NULL);

// Вызывается Duplicate handle
RSDN::CHandle hDup = hRead;

// ОШИБКА: НЕ ВЫЗЫВАЕТСЯ Duplicate handle,
// после уничтожения обного из объектов
// у второго остается недействительный HANDLE
RSDN::CHandle hDup = hRead.m_h;

Обратите внимание, что operator=(const CHandle&) и конструктор копирования CHandle(const CHandle&) неявно вызывают метод Duplicate(). Избегайте использования этих методов, заменяя их на явный вызов Duplicate() или Attach(other.Detach()):

RSDN::CHandle hPrev;
for(/*...*/)
{
    RSDN::CHandle hNext = ::CreateFile(/*...*/);
    // ...
    hPrev = hNext;
}

В этом примере все время происходит вызов функций Duplicate/Close, которых можно избежать, изменив код следующим образом:

RSDN::CHandle hPrev;
for(/*...*/)
{
    RSDN::CHandle hNext = ::CreateFile(/*...*/);
    // ...
    hPrev.Attach(hNext.Detach());
}

Эти методы были добавлены для поддержки вот такого (довольно экзотического) кода:

__STD::vector<CAdapt<RSDN::CHandle> > m_vector;

for(int i = 0; i < 10; i++)
    m_vector.push_back(::CreateThread(/*...*/));

Который, тем не менее, можно (и нужно) переделать в что-то вроде:

__STD::vector<CAdapt<RSDN::CHandle> > m_vector;
m_vector.resize(10);

for(int i = 0; i < 10; i++)
    m_vector[i].m_T = ::CreateThread(/*...*/);

Метод IsValid

Кроме проверки на NULL и INVALID_HANDLE_VALUE он также пытается проверить сам хендл, используя для этого API ::GetHandleInformation(). Но только если имеется директива _DEBUG. В Release версии хендл проверяется только на NULL и INVALID_HANDLE_VALUE. К сожалению, ::GetHandleInformation() не реализован в Win9x, и при отладке в этой операционной системе дополнительная проверка не производится.

Подробное описание

HANDLE m_hПеременная, хранящая оригинальный хендл

CHandle()Конструктор, инициализирует m_h нулем
CHandle(HANDLE h)Конструктор, Duplicate не вызывается
CHandle(const CHandle& other)Конструктор, Duplicate вызывается
~CHandle()Деструктор, вызывает Close

bool operator!() constОбратное к IsValid
operator HANDLE() constПозволяет использовать этот объект вместо HANDLE
LPHANDLE operator&()Возвращает адрес m_h, позволяет использовать этот объект вместо HANDLE*
CHandle& operator=(HANDLE h)m_h = h, Duplicate не вызывается
CHandle& operator=(const CHandle& other)m_h = other.m_h, Duplicate вызывается
void Attach(HANDLE h)Вызывает ::ClosеHandle если нужно, затем m_h = h, Duplicate не вызывается
HANDLE Detach()Возвращает m_h и выставляет ее в нуль

bool IsValid() constВозвращает false, если m_h равен нулю или INVALID_HANDLE_VALUE
void Close()Проверяет m_h и вызывает ::CloseHandle если нужно
bool Duplicate(HANDLE hSource, bool bInherit = false)m_h = ::DuplicateHandle(hSource)

DWORD Wait(DWORD dwTimeout = INFINITE) constОжидает события m_h
DWORD WaitWithMessageLoop(DWORD dwTimeout = INFINITE) constОжидает события m_h с обработкой сообщений

Заключение

Класс CHandle не зависит от других библиотек и может быть использован в любом ATL/MFC/API приложении.


Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав.
    Сообщений 6    Оценка 180        Оценить