Вашему вниманию предлагается небольшая библиотека классов, реализующих обертки (wrappers) над хендлами (handles) таких объектов, как файлы, объекты синхронизации, процессы и нити (threads).
Идея такой библиотеки не нова, на данный момент существует несколько подобных реализаций (например ATL::CHandle, ATL::CEvent и другие из ATL 7.0). Достоинствами предлагаемой библиотеки является ее полная независимость от других библиотек, ее можно использовать в MFC, ATL проектах и просто проектах, написанных на "чистом" API. Библиотека может использоваться в уникодных (Unicode), ANSI и смешанных проектах. Кроме того, в библиотеку включены несколько вспомогательных функций, упрощающих работу с API.
У каждого класса есть пара: класс, с таким же именем, но с суффиксом Handle. Например CProcessHandle. От парного класса, handle-класс отличается тем, что не закрывает свой хеднл в деструкторе. Их удобно использовать в качестве входных параметров процедур:
Если вместо RSDN::CProcessHandle использовать RSDN::CProcess в качестве входного параметра, произойдет неявный вызов ::DuplicateHandle() перед вызовом процедуры и ::CloseHandle() при выходе из нее.
Все классы содержат одну переменную m_h, в которой находится хендл, ассоциированный с данным объектом. Таким образом, выполняется равенство
sizeof(HANDLE) == sizeof(CHandle) == sizeof(CEvent) == sizeof(CEventHandle); // и т.д.
Порядок следования аргументов в методах классов, вызывающих функции API совпадает с порядком в вызываемых функциях. Кроме тех случаев, если в начале идут параметры практически никогда не используемые. Например, параметр LPSECURITY_ATTRIBUTES у метода RSDN::CEvent::Create() идет последним и равен по умолчанию нулю.
ПРИМЕЧАНИЕ
Если Вы уже использовали класс RSDN::CHandle предыдущих версий, обратите внимание на изменения в функции IsValid(). Проверка на INVALID_HANDLE_VALUE больше не производится. Вместо этого, метод RSDN::CFile::Create() и ему подобные сами проверяют возвращаемое значение и подменяют его с INVALID_HANDLE_VALUE на NULL. Сделано это в связи с тем, что с точки зрения таких функций API, как ::DuplicateHandle(), ::CloseHandle(), ::GetHandleInformation() и им подобных, INVALID_HANDLE_VALUE является допустимым параметром, и функции отрабатывают корректно. Дело в том, что INVALID_HANDLE_VALUE – это значение, возвращаемое функцией API ::GetCurrentProcess(). Таким образом, RSDN::CHandle::Close(), RSDN::IsValidHandle() и RSDN::DuplicateHandle() не должны трактовать INVALID_HANDLE_VALUE как неправильное значение. С другой стороны, для RSDN::CFile такой хендл никак не может быть правильным. Чтобы разрешить это противоречие, все методы, для которых INVALID_HANDLE_VALUE означает ошибку, сразу заменяют его на NULL. К сожалению, INVALID_HANDLE_VALUE все-таки может "прокрасться" через оператор RSDN::CHandle::operator&() или при непосредственном обращении к RSDN::CHandle::m_h. Будьте внимательны.
Еще одно важное замечание: про конструкторы. Если конструктор вызывает какой-либо метод, например Open() или Create(), а тот не отрабатывает успешно, то код ошибки может быть утерян. Если код ошибки важен, лучше явно вызвать соответствующий метод:
RSDN::CProcess hProcess;
if (FALSE == hProcess.Open(dwProcessId, PROCESS_ALL_ACCESS))
ReportErrorToUser(::GetLastError());
Краткое описание
Далее приведены списки методов классов, в левой колонке название метода, в правой описание или просто функции, вызываемые из этого метода.
CHandle
Базовый класс. Абстрактная обертка над любым хендлом, нуждающемся в вызове ::CloseHandle().
Самое узкое место этого класса – его конструкторы. Дело в том, что будучи нормальным C++ классом, наш RSDN::CHandle должен иметь конструктор копирования, позволяющий использовать одни HANDLE-объект несколькими RSDN::CHandle-объектами. Но с таком случае, только деструктор последнего RSDN::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&) неявно вызывают метод DuplicateFrom(). Избегайте использования этих методов, заменяя их на явный вызов DuplicateFrom() или Attach(other.Detach()):
Ожидает события hEvent не долее чем nTimeout миллисекунд с обработкой сообщений.
При bProcessAPC = TRUE также обрабатываются вызовы асинхронных процедур (APC).
Если во время ожидания события из очереди было изъято сообщение WM_QUIT (что, как правило, говорит об архитектурной ошибке), то на выходе из процедуры это сообщение возвращается обратно в очередь. Если такое поведение не требуется, можно определить макрос RSDN_CHANDLE_NO_WMQUITCHECK перед включением файла <rsdn/chandle.h>.
inlinebool IsValidHandle
( HANDLE h
);
Просто проверяет значение h на неравенство нулю. Кроме того, если определен макрос _DEBUG, то "правильность" хендла проверяется через вызов API ::GetHandleInformation().
Аналог стандартной ::CreateRemoteThread(), с тем отличаем, что эти функции сначала копируют в указанный процесс саму процедуру для выполнения в "чужом" процессе и ее параметры.
Освобождает память, выделенную в RSDN::CreateRemoteThreadEx(). Эту функцию следует вызывать только после того, как созданная нить будет завершена. Поскольку выделение памяти производится в другом процессе и без его ведома, то ее следует освободить явно. В противном случае, по завершении нити, память, выделенная в том процессе так и останется не освобожденной до его завершения.
Вспомогательные функции (cmodule.h)
inlinebool IsValidModule
( HMODULE h
);
Версия IsValidHandle(), специально предназначенная для проверки хендлов модулей.
inline BOOL DuplicateModule
( IN HMODULE hSource
, OUT HMODULE *lpDest
);
То же, что и DuplicateHandle, только для модулей.
inline HMODULE LoadLibraryRelativeToModule
( IN HMODULE hSource
, IN LPCSTR lpLibFileName
);
Загружает .dll по относительному пути, задаваемом хендлом модуля. Может пригодиться, если нужно загрузить .dll из другой .dll, расположенной в другом каталоге, чем .exe.
Что нового
Апрель 2004
Поддержка асинхронных процедур (APC) в Wait() функциях.
Новый класс-обертка для HINSTANCE (HMODULE).
Типизированные Read()/Write()/IoControl()/т.п.
Улучшены CCompletionPortT, CFileMappingT
Исправлены незначительные ошибки и опечатки.
Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы
то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских
прав.