Win32API_Console class

Version 0.01, March 20, 2001

by Michael L. Semon (mlsemon@sega.net)

Purpose

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.

Usage

To use the class Win32API_Console:

require 'Win32API_Console'

To use the constants that are useful while programming Win32API_Console:

include Win32API_Console_Const

Documentation

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.

A Note on the COORD Structure

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.

Limitations and Potential Bugs

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.

To Do

I haven't decided whether to include examples with each function. Suggestions and interesting snippets of code are welcome.

Methods

AllocConsole

Usage

obj.AllocConsole()

Notes

None.

CreateConsoleScreenBuffer

Usage

handle = obj.CreateConsoleScreenBuffer( dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwFlags, lpScreenBufferData )

Notes

I'm thinking that lpSecurityAttributes requires Windows NT 3.1 and higher, so it was tested with nil.

FillConsoleOutputAttribute

Usage

obj.FillConsoleOutputAttribute( hConsoleOutput, wAttribute, nLength, dwWriteCoord, lpNumberOfAttrsWritten )

Notes

dwWriteCoord is one of those COORD structures. Pack dwWriteCoord as (row << 16) + col, and pack lpNumberOfAttrsWritten as ' ' * 4.

FillConsoleOutputCharacter

Usage

obj.FillConsoleOutputCharacter( hConsoleOutput, cCharacter, nLength, dwWriteCoord, lpNumberOfAttrsWritten )

Notes

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]).

FlushConsoleInputBuffer

Usage

obj.FlushConsoleInputBuffer( hConsoleInput )

Notes

None.

FreeConsole

Usage

obj.FreeConsole()

Notes

None.

GenerateConsoleCtrlEvent

Usage

obj.GenerateConsoleCtrlEvent( dwCtrlEvent, dwProcessGroupId )

Notes

None.

GetConsoleCP

Usage

cp = obj.GetConsoleCP()

Notes

None.

GetConsoleCursorInfo

Usage

obj.GetConsoleCursorInfo( hConsoleOutput, lpConsoleCursorInfo )

Notes

I set up lpConsoleCursorInfo as ' ' * 8 and unpack it as lpConsoleCursorInfo.unpack('LL').

GetConsoleMode

Usage

obj.GetConsoleMode( hConsoleHandle, lpMode )

Notes

Set up lpMode, as ' ' * 4. You can as unpack it as lpMode.unpack("L").

GetConsoleOutputCP

Usage

cp = obj.GetConsoleOutputCP()

Notes

None.

GetConsoleScreenBufferInfo

Usage

obj.GetConsoleScreenBufferInfo( hConsoleOutput, lpBuffer )

Notes

What fun! lpBuffer is an 11-member structure (sub-structures included). Set it up as ' ' * 22 and unpack it as lpBuf.unpack("SSSSSssssSS").

GetConsoleTitle

Usage

nNumberOfCharsWritten = GetConsoleTitle( lpConsoleTitle, nSize )

Notes

I like to set nSize to a generous value and set up lpConsoleTitle as ' ' * nSize.

GetConsoleWindow

Usage

hwnd = obj.GetConsoleWindow()

Notes

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.

GetLargestConsoleWindowSize

Usage

coord = obj.GetLargestConsoleWindowSize( hConsoleOutput )

Notes

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.

GetNumberOfConsoleInputEvents

Usage

obj.GetNumberOfConsoleInputEvents( hConsoleInput, lpcNumberOfEvents )

Notes

Set up lpcNumberOfEvents as ' ' * 4 and unpack as lpcNumberOfEvents.unpack('L').

GetNumberOfConsoleMouseEvents

Usage

obj.GetNumberOfConsoleMouseEvents( lpNumberOfMouseEvents )

Notes

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.

GetStdHandle

Usage

hnd = obj.GetStdHandle( nStdHandle )

Notes

None.

HandlerRoutine

Notes

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.

PeekConsoleInput

Usage

obj.PeekConsoleInput( hConsoleInput, lpBuffer, nLength, lpNumberOfEventsRead )

Notes

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.

The last three examples were taken from the SDK's declarations and have not been tested.

ReadConsole

Usage

obj.ReadConsole( hConsoleInput, lpBuffer, nNumberOfCharsToRead, lpNumberOfCharsRead, lpReserved )

Notes

Set up lpBuffer as ' ' * buffer_length, nNumberOfCharsToRead as buffer_length, lpNumberOfCharsRead as ' ' * 4. Unpack lpNumberOfCharsRead as lpNumberOfCharsRead.unpack('L').

ReadConsoleInput

Usage

obj.ReadConsoleInput( hConsoleInput, lpBuffer, nLength, lpNumberOfEventsRead )

Notes

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.

ReadConsoleOutput

Usage

obj.ReadConsoleOutput( hConsoleOutput, lpBuffer, dwBufferSize, dwBufferCoord, lpReadRegion )

Notes

This the direct opposite of WriteConsoleOutput. See my notes on WriteConsoleOutput for details.

ReadConsoleOutputAttribute

Usage

obj.ReadConsoleOutputAttribute( hConsoleOutput, lpAttribute, nLength, dwReadCoord, lpNumberOfAttrsRead )

Notes

This is the exact opposite of WriteConsoleOutputAttribute. See my notes on WriteConsoleOutputAttribute for details.

ReadConsoleOutputCharacter

Usage

obj.( hConsoleOutput, lpCharacter, nLength, dwWriteRead, lpNumberOfCharsRead )

Notes

This is the exact opposite of WriteConsoleOutputCharacter. See my notes on WriteConsoleOutputCharacter for details.

ScrollConsoleScreenBuffer

Usage

obj.ScrollConsoleScreenBuffer( hConsoleOutput, lpScrollRectangle, lpClipRectangle, dwDestinationOrigin, lpFill )

Notes

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').

SetConsoleActiveScreenBuffer

Usage

obj.SetConsoleActiveScreenBuffer( hConsoleOutput )

Notes

None.

SetConsoleCP

Usage

obj.SetConsoleCP( wCodePageID )

Notes

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.

SetConsoleCtrlHandler

Not Implemented

Usage

Notes

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.

SetConsoleCursorInfo

Usage

obj.SetConsoleCursorInfo( hConsoleOutput, lpConsoleCursorInfo )

Notes

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.

SetConsoleCursorPosition

Usage

obj.SetConsoleCursorPosition( hConsoleOutput, dwCursorPosition )

Notes

dwCursorPosition is a COORD structure. Pack it as (row << 16) + col.

SetConsoleMode

Usage

obj.SetConsoleMode( hConsoleHandle, lpMode )

Notes

Set up lpMode as flags.pack('L').

SetConsoleOutputCP

Usage

obj.SetConsoleOutputCP( cp )

Notes

None.

SetConsoleScreenBufferSize

Usage

obj.SetConsoleScreenBufferSize( hConsoleOutput, dwSize )

Notes

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.

SetConsoleTextAttribute

Usage

obj.SetConsoleTextAttribute( hConsoleOutput, wAttributes )

Notes

The variable wAttributes is attributes that have been OR'ed together as a number.

SetConsoleTitle

Usage

obj.SetConsoleTitle( lpConsoleTitle )

Notes

None.

SetConsoleWindowInfo

Usage

obj.SetConsoleWindowInfo( hConsoleOutput, bAbsolute, lpConsoleWindow )

Notes

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.

SetStdHandle

Usage

obj.SetStdHandle( nStdHandle, hHandle )

Notes

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.

WriteConsole

Usage

obj.WriteConsole( hConsoleOutput, lpBuffer, nNumberOfCharsToWrite, lpNumberOfCharsWritten, lpReserved )

Notes

Set up lpNumberOfCharsWritten as ' ' * 4 and unpack it as lpNumberOfCharsWritten.unpack('L').

WriteConsoleInput

Usage

obj.WriteConsoleInput( hConsoleInput, lpBuffer, nLength, lpNumberOfEventsRead )

Notes

This is the opposite of WriteConsoleInput. See my notes on PeekConsoleInput for details.

WriteConsoleOutput

Usage

obj.WriteConsoleOutput( hConsoleOutput, lpBuffer, dwBufferSize, dwBufferCoord, lpWriteRegion )

Notes

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.

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.

WriteConsoleOutputAttribute

Usage

obj.WriteConsoleOutputAttribute( hConsoleOutput, lpAttribute, nLength, dwWriteCoord, lpNumberOfAttrsWritten )

Notes

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.

WriteConsoleOutputCharacter

Usage

obj.WriteConsoleOutputCharacter( hConsoleOutput, lpCharacter, nLength, dwWriteCoord, lpNumberOfCharsWritten )

Notes

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.