Правильный способ закрыть РУЧКИ 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. Вопрос в том, как правильно закрывать ручки? Я имею в виду, как избежать повторных проблем с ручками закрытия?
5 ответов
Не все функции, которые используют 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()
закрывает дескрипторы объектов ядра Windows.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);
};