Второй вызов GetStdHandle Возвращает «неверный» дескриптор - PullRequest
2 голосов
/ 26 ноября 2011

Я пытаюсь установить цвет текста консоли на заданный цвет, напечатать одну строку (или более), а затем изменить цветовую схему на прежнюю. Вот что у меня есть:

Function SetConsoleTextColor(NewColor As UInt16) As UInt16
    Declare Function SetConsoleTextAttribute Lib "Kernel32" (hConsole As Integer, attribs As UInt16) As Boolean
    Declare Function GetStdHandle Lib "Kernel32" (hIOStreamType As Integer) As Integer
    Declare Function GetConsoleScreenBufferInfo Lib "Kernel32" (hConsole As Integer, ByRef buffinfo As CONSOLE_SCREEN_BUFFER_INFO) As Boolean
    Declare Sub CloseHandle Lib "Kernel32" (HWND As Integer)

    Const STD_OUTPUT_HANDLE = -12

    Dim conHandle As Integer = GetStdHandle(STD_OUTPUT_HANDLE)
    Dim buffInfo As CONSOLE_SCREEN_BUFFER_INFO  //A structure defined elsewhere
    If GetConsoleScreenBufferInfo(conHandle, buffInfo) Then
      Call SetConsoleTextAttribute(conHandle, NewColor)
      CloseHandle(conHandle)
      Return buffInfo.Attribute
    Else
      Return 0
    End If
End Function

Это отлично работает при первом вызове. Цвет текста для нового вывода в консоли изменяется, и возвращаются предыдущие атрибуты. Однако, когда я вызываю это во второй раз для сброса атрибутов, GetStdHandle возвращает дескриптор, идентичный предыдущему вызову, но который теперь недействителен (так как я его закрыл).

Конечно, это вызывает ошибку, когда я пытаюсь использовать дескриптор. Он работает правильно, если я сделаю conHandle статической переменной и вызову GetStdHandle только если conHandle равен нулю (значение по умолчанию для новой числовой переменной в RealBasic.)

Мне всегда говорили убирать за собой. Я должен оставить эту ручку открытой?

Ответы [ 2 ]

3 голосов
/ 26 ноября 2011

Да, вы должны оставить ручку открытой.

Эта ручка автоматически закрывается при выходе из процесса.

0 голосов
/ 11 января 2016

Из-за исследований на разных сайтах может случиться так, что при использовании CloseHandle для дескриптора, возвращаемого GetStdHandle, могут произойти неприятности. Часто кажется, что ответом людей является то, что, поскольку он получает дескриптор, а не создает его (поскольку слово Get, а не Create входит в имя функции), должно быть очевидно, что он получает дескриптор, созданный системой, и что закрывать это плохая идея. Тем не менее, фактический ответ не так очевиден (к сожалению). Хотя это верно для GetStdHandle, не каждая функция Get, связанная с дескрипторами, фактически получает существующий дескриптор. Некоторые создают новые ручки. Например, GetDC фактически создает новый дескриптор контекста устройства, и, поскольку он является новым дескриптором, он должен быть надлежащим образом закрыт с помощью CloseHandle. В отличие от ответов некоторых людей, не существует правила, согласно которому, если функция содержит слово Create, она создает новый дескриптор, а если она содержит слово Get, она ссылается только на дескриптор, уже созданный системой. Нет такого правила вообще. Как вы узнаете, когда ручку нужно закрыть? Один из способов заключается в том, что если MSDN специально не указывает, что CloseHandle необходимо использовать для дескриптора, возвращаемого такой-то функцией, то можно с уверенностью предположить, что вам НЕ следует использовать CloseHandle для этого дескриптора. Еще один способ понять это методом проб и ошибок. Если использование CloseHandle приводит к тому, что в вашей программе появляется больше ошибок, НЕ используйте CloseHandle. Если НЕ использование CloseHandle приводит к тому, что ваша программа имеет больше ошибок, используйте USE CloseHandle. Я часто использую комбинацию этих подходов. Если после следования документации MSDN моя программа, похоже, содержит ошибки, но выполнение ее не так, как указано в MSDN, имеет тенденцию уменьшать количество ошибок в моей программе, я выбираю то, что, как я определил, работает методом проб и ошибок.

...