Правильный способ закрыть РУЧКИ WinAPI (избегая повторного закрытия)

У меня есть ручка, и мне нужно ее закрыть. В коде есть несколько мест, где дескриптор может быть закрыт. Итак, это правильный способ закрыть ручку?

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. Вопрос в том, как правильно закрывать ручки? Я имею в виду, как избежать повторных проблем с ручками закрытия?

4

Решение

Не все функции, которые используют 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(...));

И т.п.

8

Другие решения

использование RAII шаблон.

Оберните дескриптор в класс, который размещает дескриптор в конструкторе и уничтожает его в деструкторе. Вы можете найти некоторые примеры в MFC, например, Класс CGdiObject для объектов GDI, таких как HBITMAP,

Смотрите также этот вопрос: RAII и умные указатели в C ++

3

Да.

Я думаю, что ваше замешательство происходит от того, что их обоих называют «ручками», но это разные «классы» объектов. Термин дескриптор в HBITMAP здесь используется больше как «непрозрачный идентификатор». Существует также много документации, которая предполагает «handle» == «дескриптор ядра Windows».

В общем, если вам интересно, как удалить что-то, вам следует обратиться к документации конструктора.

1

Следующий код может быть то, что вы после:

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;
}
1

Это не 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);
};
0
По вопросам рекламы [email protected]