MKoD - D Programming Language

WinNT / WinXP Console input example - code-name winconsole.d

Very Kool!

An MSDN OnLine WinNT / WinXP Console converted to D example:

/+****************************************************************
 * Program       : winconsole.exe (a Windows' console get text example)
 * Source        : winconsole.d
 * Author        : David L. 'SpottedTiger' Davis
 * Created Date  : 29.Jul.04 Converted, Compiled and Tested with dmd v0.97 
 * Modified Date : 31.Jul.04 Minor changes.
 *               : 18.Jun.05 Compiled and Tested with dmd v0.127
 *               : 04.Jun.06 Compiled and Tested with dmd v0.160
 *               : 21.Jan.07 Compiled and Tested with dmd v1.0
 * Requirements  : std.c.windows.windows and std.c.windows.wincon
 * License       : Public Domain
 *               :
 * Source From   : MSDN OnLine Example: "Using the High-Level Input and Output Functions" 
 *               : http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc
 *               :      /base/using_the_high_level_input_and_output_functions.asp
 *****************************************************************
 *
 * Note: wincon.d needs to be copied into C:\dmd\src\phobos\std\c\windows
 *	
 * To Compiled: C:\dmd\MKOD_ex>dmd winconsole.d ..\src\phobos\std\c\windows\wincon.d
 +/
 
private import std.c.windows.windows;

// Must copy from the site and place into the
// "C:\dmd\src\phobos\std\c\windows directory."
private import std.c.windows.wincon;  

extern( Windows )
{
// -- WinBase.h --
enum : uint
{
    STD_INPUT_HANDLE  = -10,
    STD_OUTPUT_HANDLE = -11,
    STD_ERROR_HANDLE  = -12
}

struct OVERLAPPED 
{
    DWORD   Internal;
    DWORD   InternalHigh;
    DWORD   Offset;
    DWORD   OffsetHigh;
    HANDLE  hEvent;
};

alias OVERLAPPED *LPOVERLAPPED;

struct SECURITY_ATTRIBUTES 
{
    DWORD  nLength;
    LPVOID lpSecurityDescriptor;
    BOOL   bInheritHandle;
};

alias SECURITY_ATTRIBUTES *PSECURITY_ATTRIBUTES; 
alias SECURITY_ATTRIBUTES *LPSECURITY_ATTRIBUTES;

HANDLE GetStdHandle
(
    DWORD nStdHandle
);

BOOL SetStdHandle
(
    DWORD nStdHandle,
    HANDLE hHandle
);

BOOL WriteFile
(
    HANDLE hFile,
    LPCVOID lpBuffer,
    DWORD nNumberOfBytesToWrite,
    LPDWORD lpNumberOfBytesWritten,
    LPOVERLAPPED lpOverlapped
);

BOOL ReadFile
(
    HANDLE hFile,
    LPVOID lpBuffer,
    DWORD nNumberOfBytesToRead,
    LPDWORD lpNumberOfBytesRead,
    LPOVERLAPPED lpOverlapped
);

} // end extern( Windows )

HANDLE hStdout; 
HANDLE hStdin; 
CONSOLE_SCREEN_BUFFER_INFO csbiInfo; 

int main() 
{ 

    char[] lpszPrompt1 = "Type a line and press Enter, or q to quit: ";
    char[] lpszPrompt2 = "Type any key, or q to quit: ";
    CHAR   chBuffer[ 256 ]; 
    DWORD  cRead;
    DWORD  cWritten; 
    DWORD  fdwMode; 
    DWORD  fdwOldMode; 
    WORD   wOldColorAttrs; 

    // Get handles to STDIN and STDOUT. 
    hStdin  = GetStdHandle( STD_INPUT_HANDLE ); 
    hStdout = GetStdHandle( STD_OUTPUT_HANDLE ); 
    
    if ( hStdin == INVALID_HANDLE_VALUE || hStdout == INVALID_HANDLE_VALUE ) 
    {
        MessageBoxA( null, "GetStdHandle", "Console Error", MB_OK );
        return 0;
    }

    // Save the current text colors. 
    if ( !GetConsoleScreenBufferInfo( hStdout, cast(PCONSOLE_SCREEN_BUFFER_INFO)&csbiInfo ) ) 
    {
        MessageBoxA( null, "GetConsoleScreenBufferInfo", "Console Error", MB_OK ); 
        return 0;
    }

    wOldColorAttrs = csbiInfo.wAttributes; 

    // Set the text attributes to draw red text on black background. 
    if ( !SetConsoleTextAttribute( hStdout, FOREGROUND_RED | FOREGROUND_INTENSITY ) )
    {
        MessageBoxA( null, "SetConsoleTextAttribute", "Console Error", MB_OK );
        return 0;
    }

    // Write to STDOUT and read from STDIN by using the default 
    // modes. Input is echoed automatically, and ReadFile 
    // does not return until a carriage return is typed. 
    // 
    // The default input modes are line, processed, and echo. 
    // The default output modes are processed and wrap at EOL. 
    while ( 1 ) 
    { 
        if ( !WriteFile( 
                  hStdout,                       // output handle 
                  cast(LPCSTR)lpszPrompt1,       // prompt string 
                  cast(DWORD)lpszPrompt1.length, // string length 
                  &cWritten,                     // bytes written 
                  null ) )                       // not overlapped 
        {
            MessageBoxA( null, "WriteFile", "Console Error", MB_OK ); 
            return 0;
        }

        if ( !ReadFile( 
                  hStdin,    // input handle 
                  cast(void *)chBuffer,  // buffer to read into 
                  255,       // size of buffer 
                  &cRead,    // actual bytes read 
                  null ) )   // not overlapped 
            break; 
            
        if ( chBuffer[ 0 ] == 'q' ) break; 
    } 

    // Turn off the line input mode, and echo the input mode. 
    if ( !GetConsoleMode( hStdin, &fdwOldMode ) ) 
    {
        MessageBoxA( null, "GetConsoleMode", "Console Error", MB_OK ); 
        return 0;
    }

    fdwMode = fdwOldMode & 
        ~( ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT ); 
        
    if ( !SetConsoleMode( hStdin, fdwMode ) ) 
    {
        MessageBoxA( null, "SetConsoleMode", "Console Error", MB_OK ); 
        return 0;
    }

    // Without line and echo input modes, ReadFile returns 
    // when any input is available. Carriage returns must 
    // be handled, and WriteFile is used to echo input. 
    NewLine();

    while ( 1 ) 
    { 
        if ( !WriteFile( 
                  hStdout,                       // output handle 
                  cast(LPCSTR)lpszPrompt2,       // prompt string 
                  cast(DWORD)lpszPrompt2.length, // string length 
                  &cWritten,                     // bytes written 
                  null ) )                       // not overlapped 
        {
            MessageBoxA( null, "WriteFile", "Console Error", MB_OK );
            return 0;
        }

        if ( !ReadFile( hStdin, cast(void *)chBuffer, 1, &cRead, null ) ) break; 
        
        if ( chBuffer[ 0 ] == '\r' )
            NewLine();
        else if ( !WriteFile( hStdout, cast(void *)chBuffer, cRead, &cWritten, null ) ) 
           break;
        else
            NewLine();
            
        if (chBuffer[ 0 ] == 'q' ) break; 
    } 

    // Restore the original console mode. 
    SetConsoleMode( hStdin, fdwOldMode );

    // Restore the original text colors. 
    SetConsoleTextAttribute( hStdout, wOldColorAttrs );
    
    return 0;
    
} // end int main() 

// The NewLine function handles carriage returns when the processed 
// input mode is disabled. It gets the current cursor position 
// and resets it to the first cell of the next row. 
void NewLine() 
{ 
    if ( !GetConsoleScreenBufferInfo( hStdout, cast(PCONSOLE_SCREEN_BUFFER_INFO)&csbiInfo ) ) 
    {
        MessageBoxA( null, "GetConsoleScreenBufferInfo", "Console Error", MB_OK ); 
        return;
    }

    csbiInfo.dwCursorPosition.X = 0; 

    // If it is the last line in the screen buffer, scroll 
    // the buffer up. 
    if ( ( csbiInfo.dwSize.Y - 1 ) == csbiInfo.dwCursorPosition.Y ) 
    { 
        ScrollScreenBuffer( hStdout, 1 ); 
    } 
    // Otherwise, advance the cursor to the next line. 
    else csbiInfo.dwCursorPosition.Y += 1; 
 
    if ( !SetConsoleCursorPosition( hStdout, csbiInfo.dwCursorPosition ) ) 
    {
        MessageBoxA( null, "SetConsoleCursorPosition", "Console Error", MB_OK ); 
        return;
    }
    
} // end void NewLine()

void ScrollScreenBuffer
( 
    in HANDLE h, 
    in INT x 
)
{
    SMALL_RECT srctScrollRect;
    SMALL_RECT srctClipRect;
    CHAR_INFO  chiFill;
    COORD      coordDest;

    srctScrollRect.Left   = 0;
    srctScrollRect.Top    = 1;
    srctScrollRect.Right  = csbiInfo.dwSize.X - x; 
    srctScrollRect.Bottom = csbiInfo.dwSize.Y - x; 
 
    // The destination for the scroll rectangle is one row up. 
    coordDest.X = 0; 
    coordDest.Y = 0; 
 
    // The clipping rectangle is the same as the scrolling rectangle. 
    // The destination row is left unchanged. 
    srctClipRect = srctScrollRect; 
 
    // Set the fill character and attributes. 
    chiFill.Attributes = FOREGROUND_RED | FOREGROUND_INTENSITY; 
    chiFill.Char.AsciiChar = cast(char)' '; 
 
    // Scroll up one line. 
    ScrollConsoleScreenBufferA( 
        h,               // screen buffer handle 
        &srctScrollRect, // scrolling rectangle 
        &srctClipRect,   // clipping rectangle 
        coordDest,       // top left destination cell 
        &chiFill );       // fill character and color 
        
} // end void ScrollScreenBuffer( in HANDLE, in INT )

Example output:

C:\dmd\MKoD_ex>bin\dmd winconsole.d
C:\dmd\bin\..\..\dm\bin\link.exe winconsole,,,user32+kernel32/noi;

C:\dmd\MKoD_ex>winconsole

Type a line and press Enter, or q to quit: Prompt is now waiting for my input.
Type a line and press Enter, or q to quit: Every character is typing in as RED. :)
Type a line and press Enter, or q to quit: q = Quit

Type any key, or q to quit: q

C:\dmd\MKoD_ex>
Mars: fourth Rock from the Sun.