Правильный способ закрыть РУЧКИ 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);
};
Другие вопросы по тегам