У меня есть ручка, и мне нужно ее закрыть. В коде есть несколько мест, где дескриптор может быть закрыт. Итак, это правильный способ закрыть ручку?
HANDLE h;
....
if ( h != INVALID_HANDLE_VALUE ) {
::CloseHandle(h);
h = INVALID_HANDLE_VALUE;
}
Есть тот же вопрос о ручках растрового изображения:
HBITMAP hb;
....
if ( hb != INVALID_HANDLE_VALUE ) {
::DeleteObject(hb);
hb = INVALID_HANDLE_VALUE;
}
РЕДАКТИРОВАТЬ: Я думаю, что есть недоразумение. я знаю CloseHandle
для закрытия ручек. Я хотел бы знать, как правильно закрывать ручки. Аналогичная ситуация возникает при удалении указателей.
Foo *foo = new Foo();
// for example there is 2 functions that can delete foo
void bar() {
....
delete foo;
}
void duck() {
....
delete foo;
}
Итак, следующий код означает проблемы:
bar();
duck();
В этом случае есть обходной путь. Нам нужно определить bar
&duck
функции как это:
void bar() {
....
if (foo) {
delete foo;
foo = NULL;
}
}
void duck() {
....
if (foo) {
delete foo;
foo = NULL;
}
}
Поэтому мы избегаем повторного удаления foo. Вопрос в том, как правильно закрывать ручки? Я имею в виду, как избежать повторных проблем с ручками закрытия?
Не все функции, которые используют HANDLE
использование CloseHandle()
некоторые вместо этого используют другие закрывающие функции. Также не все HANDLE
значения используют INVALID_HANDLE_VALUE
, или. Некоторые используют NULL
вместо.
HBITMAP
никогда не использует INVALID_HANDLE_VALUE
всегда использует NULL
, И ты никогда не должен звонить DeleteObject()
для HBITMAP
ты не владеешь.
Итак, короткий ответ — если вы пытаетесь создать какое-то общее управление дескриптором, не беспокойтесь. Вы, вероятно, ошиблись. Если вы выделяете / открываете какой-то дескриптор, вы должны знать правильный способ его закрытия, вы не сможете догадаться об этом.
Если вы хотите, чтобы ручки управляли сами, то RAII — лучший выбор. Я предпочитаю использовать шаблонный класс со специализированными чертами, чтобы уменьшить дублирование кода для разных типов дескрипторов, например:
template< class traits >
class HandleWrapper
{
private:
traits::HandleType FHandle;
public:
HandleWrapper()
FHandle(traits::InvalidValue)
{
}
HandleWrapper(const traits::HandleType value)
FHandle(value)
{
}
~HandleWrapper()
{
Close();
}
void Close()
{
if (FHandle != traits::InvalidValue)
{
traits::Close(FHandle);
FHandle = traits::InvalidValue;
}
}
bool operator !() const {
return (FHandle == traits:::InvalidValue);
}
operator bool() const {
return (FHandle != traits:::InvalidValue);
}
operator traits::HandleType() {
return FHandle;
}
};
.
struct KernelHandleTraits
{
typedef HANDLE HandleType;
static const HANDLE InvalidValue = INVALID_HANDLE_VALUE;
static void Close(HANDLE value)
{
CloseHandle(value);
}
};
HandleWrapper<KernelHandleTraits> hFile(CreateFile(...));
.
struct NullKernelHandleTraits
{
typedef HANDLE HandleType;
static const HANDLE InvalidValue = NULL;
static void Close(HANDLE value)
{
CloseHandle(value);
}
};
HandleWrapper<NullKernelHandleTraits> hMapping(CreateFileMapping(...));
.
struct FileMapViewTraits
{
typedef void* HandleType;
static const void* InvalidValue = NULL;
static void Close(void *value)
{
UnmapViewOfFile(value);
}
};
HandleWrapper<FileMapViewTraits> hView(MapViewOfFile(...));
.
struct GDIBitmapHandleTraits
{
typedef HBITMAP HandleType;
static const HBITMAP InvalidValue = NULL;
static void Close(HBITMAP value)
{
DeleteObject(value);
}
};
HandleWrapper<GDIBitmapTraits> hBmp(CreateBitmap(...));
И т.п.
использование RAII шаблон.
Оберните дескриптор в класс, который размещает дескриптор в конструкторе и уничтожает его в деструкторе. Вы можете найти некоторые примеры в MFC, например, Класс CGdiObject для объектов GDI, таких как HBITMAP
,
Смотрите также этот вопрос: RAII и умные указатели в C ++
Да.
CloseHandle()
закрывает окна объект ядра ручки.DeleteObject()
удаляет объекты GDI.Я думаю, что ваше замешательство происходит от того, что их обоих называют «ручками», но это разные «классы» объектов. Термин дескриптор в HBITMAP
здесь используется больше как «непрозрачный идентификатор». Существует также много документации, которая предполагает «handle» == «дескриптор ядра Windows».
В общем, если вам интересно, как удалить что-то, вам следует обратиться к документации конструктора.
Следующий код может быть то, что вы после:
BOOL CloseValidHandle(HANDLE& handle)
{
if (handle != INVALID_HANDLE_VALUE && handle != 0)
{
if (CloseHandle(handle))
{
handle = INVALID_HANDLE_VALUE;
return TRUE;
}
else
{
return FALSE;
}
}
return TRUE;
}
Это не RAII, но это помогает удалить / закрыть обработчик.
class HandleDel : boost::notcopyable{
public:
HandleDel(HANDLE h, HANDLE invalid, BOOL(WINAPI *del)(HANDLE)):
h(h), invalid(invalid), del(del){
}
~HandleDel(){
if ( h != invalid ) del(h);
}
private:
HANDLE h;
HANDLE invalid;
BOOL(WINAPI *del)(HANDLE);
};