2

I'm trying to set the text color of a console to a given color, print one line (or more) and then change the color scheme back to what it was. Here's what I have:

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

This works just fine on the first call. The text color for new output on the console is changed and the previous attributes are returned. However, when I call this a second time to reset the attributes GetStdHandle returns a handle identical to the previous call, but which is now invalid (since I closed it.)

This causes an error, of course, when I try to use the handle. It works properly if I make conHandle a static variable and only call GetStdHandle if conHandle is equal to zero (the default value for new numeric variable in RealBasic.)

I was always told to clean up after myself. Am I supposed to leave this handle open?

Andrew Lambert
  • 1,869
  • 1
  • 17
  • 31
  • 3
    Yes, you should clean up after yourself, but you should also have been told to respect other people's property. In this case, you didn't create the console handle, so you shouldn't destroy it either. – Raymond Chen Nov 26 '11 at 14:44
  • I thought I did create the handle, or at least that it belonged to me. – Andrew Lambert Nov 26 '11 at 17:39
  • 1
    GetStdHandle does not create a handle, it just fetches the existing one. Handle creation functions are CreateFile, CreateMutex, etc. – Raymond Chen Nov 26 '11 at 17:43
  • Oh, I see! That does make sense (sort of.) – Andrew Lambert Nov 26 '11 at 17:45
  • 1
    @Raymond Chen Should I simply call GetStdHandle each time I need the handle or should I cache the handle and only call GetStdHandle if the cached handle is invalid (<=0)? – Andrew Lambert Dec 03 '11 at 22:36
  • Um, it depends on what you want your function to do. If somebody changes the standard output handle, do you want your function to change the color of the original console or the new one? The answer to that will answer your question. – Raymond Chen Dec 03 '11 at 23:00
  • 2
    Raymond Chen goes into depth about managing standard handles (in response to this question) at http://blogs.msdn.com/b/oldnewthing/archive/2013/03/07/10399690.aspx – zastrowm Mar 08 '13 at 14:18
  • And fails to mention what a mess WinAPI is when you cant even know if handle returned is created or fetched. There is simply no way to know and if MSDN docs didnt specify you are out of luck. Have fun guessing or the next year when random bugs pop up. – Digika Apr 22 '21 at 05:50

2 Answers2

5

Yes you are supposed to leave the handle open.

This handle is closed automatically when your process exits.

Norbert Willhelm
  • 2,579
  • 1
  • 23
  • 33
1

From doing research on various sites, it appears bad things can happen when you use CloseHandle on a handle returned by GetStdHandle. Often times, it seems that people's reply is that because it gets a handle rather than creating one (as the word Get, not Create, is in the function name) it should be obvious that it gets a handle created by the system, and that closing this is a bad idea. However, the actual answer is NOT this obvious (sadly). While it is true with GetStdHandle, not every Get function relating to handles actually gets an existing handle. Some create new handles. For example, GetDC actually creates a new handle to a device context, and as it is a new handle it must be properly closed with CloseHandle. Unlike some people's replies, there is no rule that if the function contains the word Create it creates a new handle, while if it contains the word Get it only references a handle already created by the system. There is no such rule at all. How do you know when a handle actually needs to be closed then? One way is that if MSDN doesn't specifically state that CloseHandle needs to be used on a handle returned by such-and-such function, then it is safe to assume that you should NOT use CloseHandle on that handle. Another way to figure it out is by trial and error. If using CloseHandle causes your program to have more bugs, then DON'T use CloseHandle. If NOT using CloseHandle causes your program to have more bugs, then USE CloseHandle. I often use a combination of these approaches. If after following the MSDN documentation my program seems to have bugs, but doing it differently than specified by MSDN tends to reduce the bugs in my program, I go by what I've determined works via trial-and-error.

  • `GetDC` doesn't "create" a device context, and you should not call `CloseHandle` on the result. Well, actually, you don't call `CloseHandle` on *any* GDI object handle. The cleanup function corresponding to `CreateDC` is `DeleteDC`... and you do NOT use it on an `HDC` you got from `GetDC`. An `HDC` from `GetDC` later goes to `ReleaseDC`. (And then there are special rules for classes and/or windows with the OWN DC styles) – Ben Voigt Aug 22 '17 at 18:07
  • I mean, there's a valid point hidden in this answer, namely that you know whether to pass a handle to `CloseHandle` by reading the documentation, not whether the API begins with the letters `C`-`r`-`e`-`a`-`t`-`e`. But it's unfortunately buried in a wall of mistakes. – Ben Voigt Aug 22 '17 at 18:09
  • No, the valid point is that this is an unspecified mess of WinAPI/OS we all must suffer through. – Digika Apr 22 '21 at 05:40