Version 0.01, March 20, 2001
by Michael L. Semon (mlsemon@sega.net)
The purpose of the Win32API_Console class is to provide basic declarations for the standard Win32 Console API functions. It has no public instance variables and does nothing behind the scenes to obscure the nature of the Win32 API. 45 of the 46 Console API functions are implemented. Only SetConsoleCtrlHandler is not implemented.
The nice thing about using Win32API_Console is that you don't have to declare them in your code or debug
the data types of the input and output parameters. Also, I've taken the time to glean the constants from header
files as needed from the Microsoft Platform SDK. If you pass FILE_SHARE_READ
to a
function that will accept it, you will be passing the correct value.
The bare-bones nature of Win32API_Console is good if you're used to calling API functions from C or Visual Basic. It may be not so good if you're used to using objects such as the Win32::Console object in Perl. If you want to make your own "high-level" Win32_Console object in Ruby, Win32API_Console would save you a moderate chunk of time.
To use the class Win32API_Console:
require 'Win32API_Console'
To use the constants that are useful while programming Win32API_Console:
include Win32API_Console_Const
There are notes for most of the functions, but these notes are specific either to Ruby or my way of calling the functions. I recommend that you download the Microsoft Platform SDK to learn about the Windows API functions. As an alternative, you can browse the SDK at msdn.microsoft.com.
There's a COORD structure that's a struct of two short integers in C. However, it's always
passed by value (not by pointer) by the API functions, so it has to be treated as one long
integer. For coord, I use coord >> 16
to get one word, then use
coord & 0x0000ffff
to get the other word.
I have access only to Windows 98 SE and Windows Millennium Edition, so I have not tested features specific to Windows NT and Windows 2000. Fortunately, there are hardly any security structures in this module, so it shouldn't be much of a problem.
I have been programming Ruby since March 16--three days when I started this project--so I'm limited as to what I can do with the language.
The current implementation creates a Win32API object for each function that you call, caching the object in class variables on an as-needed basis. Everything should be okay, but let me know if anything is broken.
Thank you for your patience.
I haven't decided whether to include examples with each function. Suggestions and interesting snippets of code are welcome.
obj.AllocConsole()
None.
handle = obj.CreateConsoleScreenBuffer( dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwFlags, lpScreenBufferData )
I'm thinking that lpSecurityAttributes requires Windows NT 3.1 and higher, so it was tested with nil.
obj.FillConsoleOutputAttribute( hConsoleOutput, wAttribute, nLength, dwWriteCoord, lpNumberOfAttrsWritten )
dwWriteCoord is one of those COORD structures. Pack dwWriteCoord as (row << 16) + col
, and
pack lpNumberOfAttrsWritten as ' ' * 4
.
obj.FillConsoleOutputCharacter( hConsoleOutput, cCharacter, nLength, dwWriteCoord, lpNumberOfAttrsWritten )
See the notes on FillConsoleOutputAttribute. In addition, the cCharacter is a char in the C sense of the
word: One byte that can be either a number or a single C-style character (same thing). Pass it a number, either
the value of the letter (like 0x61
) or the Ruby translation (like 'a'[0]
).
obj.FlushConsoleInputBuffer( hConsoleInput )
None.
obj.FreeConsole()
None.
obj.GenerateConsoleCtrlEvent( dwCtrlEvent, dwProcessGroupId )
None.
cp = obj.GetConsoleCP()
None.
obj.GetConsoleCursorInfo( hConsoleOutput, lpConsoleCursorInfo )
I set up lpConsoleCursorInfo as ' ' * 8
and unpack it as lpConsoleCursorInfo.unpack('LL')
.
obj.GetConsoleMode( hConsoleHandle, lpMode )
Set up lpMode, as ' ' * 4
. You can as unpack it as lpMode.unpack("L")
.
cp = obj.GetConsoleOutputCP()
None.
obj.GetConsoleScreenBufferInfo( hConsoleOutput, lpBuffer )
What fun! lpBuffer is an 11-member structure (sub-structures included). Set it up as ' ' * 22
and unpack it
as lpBuf.unpack("SSSSSssssSS")
.
nNumberOfCharsWritten = GetConsoleTitle( lpConsoleTitle, nSize )
I like to set nSize to a generous value and set up lpConsoleTitle as ' ' * nSize
.
hwnd = obj.GetConsoleWindow()
Browsing Wincon.h in the SDK's header files, I believe this is a function for Windows 2000 and higher. If you run it on anything else, Ruby will probably complain that it can't find GetConsoleWindow or GetConsoleWindowA.
coord = obj.GetLargestConsoleWindowSize( hConsoleOutput )
This was my introduction to the COORD structure. Wonderful, given the oddball results of the function. I'm sure the values are correct, though.
I was using this function to try to get the result "80x25" for my console window. This function will not give you that result. Use GetConsoleScreenBufferInfo for that. This function makes me believe things like "for an 8x14 TrueType font, the largest window size is 124x47. For a 6x10 TrueType font, the largest window size is 166x66." This function is useful with SetConsoleScreenBufferSize.
obj.GetNumberOfConsoleInputEvents( hConsoleInput, lpcNumberOfEvents )
Set up lpcNumberOfEvents as ' ' * 4
and unpack as lpcNumberOfEvents.unpack('L')
.
obj.GetNumberOfConsoleMouseEvents( lpNumberOfMouseEvents )
Set up lpcNumberOfMouseEvents as ' ' * 4
and unpack as lpcNumberOfMouseEvents.unpack('L')
.
I get the result 2
for my three-button Microsoft Intellimouse. I'm not sure why this is the case.
hnd = obj.GetStdHandle( nStdHandle )
None.
This is listed in the SDK as if there was a function named "HandlerRoutine". However, looking at the description, it seems to be "substitute the address of your function for 'HandlerRoutine'," and the PHANDLER_ROUTINE C preprocessor macro will take care of the rest when you send it through SetConsoleCtrlHandler.
obj.PeekConsoleInput( hConsoleInput, lpBuffer, nLength, lpNumberOfEventsRead )
Set up lpNumberOfEventsRead as ' ' * 4
and unpack it as lpNumberOfEventsRead.unpack('L')
.
Put on your thinking cap for lpBuffer. Set it up as ' ' * 20
. Unpack it once as
lpBuffer.unpack('l')
and test its value. You will get one of five different values, and your
unpacking actions depend on its value. lpBuffer is a C union of five different structs.
lpBuffer.unpack('lLSSSsL')
lpBuffer.unpack('lLLLL')
lpBuffer.unpack('lL')
lpBuffer.unpack('lS')
lpBuffer.unpack('lL')
The last three examples were taken from the SDK's declarations and have not been tested.
obj.ReadConsole( hConsoleInput, lpBuffer, nNumberOfCharsToRead, lpNumberOfCharsRead, lpReserved )
Set up lpBuffer as ' ' * buffer_length
, nNumberOfCharsToRead as buffer_length
, lpNumberOfCharsRead as ' ' * 4
.
Unpack lpNumberOfCharsRead as lpNumberOfCharsRead.unpack('L')
.
obj.ReadConsoleInput( hConsoleInput, lpBuffer, nLength, lpNumberOfEventsRead )
This is the same as PeekConsoleInput, except that it doesn't return until it has an event to read. See my notes on PeekConsoleEvent for details.
obj.ReadConsoleOutput( hConsoleOutput, lpBuffer, dwBufferSize, dwBufferCoord, lpReadRegion )
This the direct opposite of WriteConsoleOutput. See my notes on WriteConsoleOutput for details.
obj.ReadConsoleOutputAttribute( hConsoleOutput, lpAttribute, nLength, dwReadCoord, lpNumberOfAttrsRead )
This is the exact opposite of WriteConsoleOutputAttribute. See my notes on WriteConsoleOutputAttribute for details.
obj.( hConsoleOutput, lpCharacter, nLength, dwWriteRead, lpNumberOfCharsRead )
This is the exact opposite of WriteConsoleOutputCharacter. See my notes on WriteConsoleOutputCharacter for details.
obj.ScrollConsoleScreenBuffer( hConsoleOutput, lpScrollRectangle, lpClipRectangle, dwDestinationOrigin, lpFill )
Oh joy! This is a fun little function, bringing back warm memories of drawing tricks in old-school DOS programs.
You can pass nil
to lpClipRectangle, but you can't pass
nil
to lpFill. lpScrollRectangle and lpClipRectangle are both packed like
rect = [x1, y1, x2, y2].pack('ssss')
and don't need to be unpacked. dwDestinationOrigin is a COORD and
can be packed like (row << 16) + col
. lpFill can be packed like ['c'[0], attributes].pack('ss')
.
obj.SetConsoleActiveScreenBuffer( hConsoleOutput )
None.
obj.SetConsoleCP( wCodePageID )
This officially unsupported in Win9x, and I got a return code of 0 for all cases in Win98SE. Then again, maybe it's just a lack of codepages on my PC. Let me know if you've used it with success.
Not Implemented
I do not know how to do C-style callbacks in Ruby, so this is not implemented. For details, see my notes for the HandlerRoutine concept.
obj.SetConsoleCursorInfo( hConsoleOutput, lpConsoleCursorInfo )
I set up lpConsoleCursorInfo as [dwSize, bVisible].pack('LL')
and don't need to unpack it.
Windows will set the cursor to one of [12, 18, 25, 31, 37, 43, 50, 56, 62, 68, 75, 81, 87, 93, 100]. This is not
mentioned in the SDK.
obj.SetConsoleCursorPosition( hConsoleOutput, dwCursorPosition )
dwCursorPosition is a COORD structure. Pack it as (row << 16) + col
.
obj.SetConsoleMode( hConsoleHandle, lpMode )
Set up lpMode as flags.pack('L')
.
obj.SetConsoleOutputCP( cp )
None.
obj.SetConsoleScreenBufferSize( hConsoleOutput, dwSize )
Pack dwSize as (row << 16) + col
. Also, the SDK mentioned that GetSystemMetrics
would useful with this function. However, it's not a part of the Console
documentation--it's in another category and not part of my SDK download--so it's
not included here.
obj.SetConsoleTextAttribute( hConsoleOutput, wAttributes )
The variable wAttributes is attributes that have been OR'ed together as a number.
obj.SetConsoleTitle( lpConsoleTitle )
None.
obj.SetConsoleWindowInfo( hConsoleOutput, bAbsolute, lpConsoleWindow )
Pack lpConsoleWindow as [x1, y1, x2, y2].pack('ssss')
. I think this function
works. This function is for things like "you have an 80x25 screen in a 40x15 window.
Position the contents to where (20, 5) shows at (0, 0) in the window." This is unless you have
your console font set to "Auto", in which case the font will shrink to accommodate the
new size of the window. Okay...I'm 95% sure
that I have it right, even if the results baffle me.
obj.SetStdHandle( nStdHandle, hHandle )
If you use rubyw.exe to run the script and use AllocConsole(), puts and print stop working. According to the documentation, a call to CreateFile and SetStdHandle may get puts and print working again. I have to implement CreateFile in another module before I test this idea.
obj.WriteConsole( hConsoleOutput, lpBuffer, nNumberOfCharsToWrite, lpNumberOfCharsWritten, lpReserved )
Set up lpNumberOfCharsWritten as ' ' * 4
and unpack it as lpNumberOfCharsWritten.unpack('L')
.
obj.WriteConsoleInput( hConsoleInput, lpBuffer, nLength, lpNumberOfEventsRead )
This is the opposite of WriteConsoleInput. See my notes on PeekConsoleInput for details.
obj.WriteConsoleOutput( hConsoleOutput, lpBuffer, dwBufferSize, dwBufferCoord, lpWriteRegion )
This is a cool function, but it can go wild if you plug the wrong values into it. The function's use is best explained with an example.
Suppose I want to create my own NeXT logo and display it at (20, 10) in the current console.
char
(or wchar
: it's a union in C) and a short of color attributes. That means I pass the characters as numbers like 'c'[0]
.
.flatten
the array before packing it.
(2 << 16) + 2
to tell Windows that this is a 2x2 array.
require 'Win32API_Console' include Win32API_Console_Const a=Win32API_Console.new() hnd = a.GetStdHandle( STD_OUTPUT_HANDLE ) lpData = [ [ 'N'[0], FOREGROUND_RED | FOREGROUND_INTENSITY, 'e'[0], FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY ], [ 'X'[0], FOREGROUND_GREEN | FOREGROUND_INTENSITY, 'T'[0], FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY ] ].flatten.pack('cxs' * 4) lpRegion = [20,10,21,11].pack('ssss') a.WriteConsoleOutput( hnd, lpData, (2 << 16) + 2, 0, lpRegion )
lpData could have been packed with .pack('ss' * 4)
.
In my example, I'm using the char
part of the CHAR_INFO union, as if I was
packing ASCII characters. .pack('ss' * 4)
would be using the wchar
part of it.
obj.WriteConsoleOutputAttribute( hConsoleOutput, lpAttribute, nLength, dwWriteCoord, lpNumberOfAttrsWritten )
The SDK refers to lpAttribute in vague terms, calling it a "buffer of attributes."
Basically, pack an arbitrary array of attributes like [attr1, attr2, attr3, etc].pack('s*')
.
Set nLength to the number of terms you had in your array. Pack dwWriteCoord
as (row << 16) + col
. Set up lpNumberOfAttributesWritten as ' ' * 4
.
obj.WriteConsoleOutputCharacter( hConsoleOutput, lpCharacter, nLength, dwWriteCoord, lpNumberOfCharsWritten )
The name of this function is misleading, and the SDK doesn't clarify it much. Basically,
WriteConsoleOutputCharacter is WriteConsole + (coordinate settings) - (reserved flag that
has to be nil
anyway).
Set up dwWriteCoord as (row << 16) + col
. Set up lpNumberOfCharsWritten as ' ' * 4
.