定制命令行窗口
By MikeFeng, QQ 76848502
用过FTERM没?用过TELNET没?是不是对上面彩色的文字和命令行式的操作记忆犹新?今天写了基于命令行的程序,因为以前没有用到过类似的API,所以决定写这篇没有含金量的文章,以免以后忘记了还要重头学起。
话说需求是这样的:实现一个类似于windows启动菜单一样的界面,可以上下选择,也可以回车执行操作。首先在MSDN里大海捞针,却一无所获。后来想起.NET Framework中有个System.Console的类,于是再次翻出Reflactor,在mscorlib.dll里找到了相关实现。发现了SetConsole*和GetConsole*的Windows API。最后终于在MSDN的DLLs, Processes, and Threads中找到了相关资料。
一个命令行窗口有两种显示模式,一种是全屏,一种是窗口。这个用SetConsoleDisplayMode可以设置。另外,从数据的输入输出方面来说,命令行窗口有一下5中模式:
ENABLE_ECHO_INPUT:可以用ReadFile和ReadConsole函数来获得输入。这个必须和ENABLE_LINE_INPUT一起使用
ENABLE_INSERT_MODE:必须和ENABLE_EXTENDED_FLAGS一起使用,它可以在光标位置插入文本。如果这个标志没有开启,那么光标后面的文本将被覆盖。
ENABLE_LINE_INPUT:仅当回车被侦测到时ReadFile和ReadConsole函数才返回处理结果。如果这个标记没有开启,那么只要有输入数据就会进行处理。
ENABLE_MOUSE_INPUT:输入缓冲接受鼠标事件。就算这个标志被设置,这些事件也不会被ReadFile和ReadConsole处理。
ENABLE_PROCESSED_INPUT:象回删,回车,ctrl+c这样的事件不设置这个标志将不会被ReadFile和ReadConsole捕获。
ENABLE_QUICK_EDIT_MODE:必须和ENABLE_EXTENDED_FLAG一起使用。允许用户用鼠标右键选择和编辑文本。
ENABLE_WINDOW_INPUT:必须和ReadConsoleInput函数配套使用。程序可以获得用户交互的事件,例如上下左右箭头的侦测,以及回车等控制字键,也包括鼠标输入,交点的获得与丢失,以及菜单事件等。使用ReadFile和ReadConsole是获得不到这些信息的。
还有一些扩展的模式,没有仔细研究。
另外比较重要的就是对于文字属性的设定了,其中有设定将要写入文字的前景和背景色的函数SetConsoleTextAttribute,也有设定屏幕中指定范围文字的前景色和背景色函数FillConsoleOutputAttribute。还有一些设置窗口标题,大小等的函数,就不说了。
下面是C写的程序,仅供参考:
#include "stdafx.h"
#include <windows.h> 
void Init(void);
void NewLine(void);
void AddEntry(LPCTSTR);
void ScrollScreenBuffer(HANDLE, INT);
void SetColor(int);
void Work(int);
HANDLE hStdout, hStdin;
CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
WORD wOldColorAttrs;
WORD wColor = BACKGROUND_RED | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
INT currentLine = 0;
INT totalLine = 0;
DWORD fdwOldMode;
void main(void) 
...{
Init();
DWORD dwRead;
COORD coord;
INPUT_RECORD chBuffer;
coord.X = 0; // start at first cell
coord.Y = currentLine; // of first row 
BOOL fSuccess = FillConsoleOutputAttribute(
hStdout, // screen buffer handle
wColor, // color to fill with
80, // number of cells to fill
coord, // first cell to write to
NULL); // actual number written
while (1) 
...{
//if (! ReadFile(hStdin, chBuffer, 1, &cRead, NULL))
// break;
if (! ReadConsoleInput(
hStdin, // input buffer handle
&chBuffer, // buffer to read into
1, // size of read buffer
&dwRead) ) // number of records read
continue;
switch(chBuffer.EventType) 
...{
case KEY_EVENT: // keyboard input
if ( chBuffer.Event.KeyEvent.bKeyDown )
...{
// up arrow is pressed
if (chBuffer.Event.KeyEvent.wVirtualKeyCode == 38)
...{
if (currentLine == 0) continue;
else 
...{
SetColor(--currentLine);
}
}
// down arrow is pressed
else if (chBuffer.Event.KeyEvent.wVirtualKeyCode == 40)
...{
if (currentLine == totalLine-1) continue;
else 
...{
SetColor(++currentLine);
}
}
// enter is pressed
else if (chBuffer.Event.KeyEvent.wVirtualKeyCode == 13)
...{
Work(currentLine);
}
// press q to exit
else if (chBuffer.Event.KeyEvent.uChar.AsciiChar == 'q')
...{
return;
}
//else if (chBuffer.Event.KeyEvent)
}
break; 
//case MOUSE_EVENT: // mouse input
// printf("%d",KEY_EVENT);
// //MouseEventProc(chBuffer.Event.MouseEvent);
// break; 
case WINDOW_BUFFER_SIZE_EVENT: // scrn buf. resizing
printf("%d",KEY_EVENT);
//ResizeEventProc(
//irInBuf[i].Event.WindowBufferSizeEvent);
break; 
//case FOCUS_EVENT: // disregard focus events
// printf("%d",KEY_EVENT);
// break;
//case MENU_EVENT: // disregard menu events
// printf("%d",KEY_EVENT);
// break; 
default:
break;
} 
} 
// Restore the original console mode.
SetConsoleMode(hStdin, fdwOldMode);
// Restore the original text colors.
SetConsoleTextAttribute(hStdout, wOldColorAttrs);
}
void Init()
...{
DWORD fdwMode; 
// 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) 
...{
MessageBox(NULL, "GetStdHandle", "Console Error", MB_OK);
return;
}
// Save the current text colors. 
if (! GetConsoleScreenBufferInfo(hStdout, &csbiInfo)) 
...{
MessageBox(NULL, "GetConsoleScreenBufferInfo",
"Console Error", MB_OK);
return;
}
wOldColorAttrs = csbiInfo.wAttributes; 
// Turn off the line input mode, and echo the input mode. 
if (! GetConsoleMode(hStdin, &fdwOldMode)) 
...{
MessageBox(NULL, "GetConsoleMode", "Console Error", MB_OK);
return;
}
fdwMode = fdwOldMode && ENABLE_WINDOW_INPUT ;
if (! SetConsoleMode(hStdin, fdwMode)) 
...{
MessageBox(NULL, "SetConsoleMode", "Console Error", MB_OK);
return;
}
AddEntry(TEXT("Line 1"));
AddEntry(TEXT("Line 2"));
AddEntry(TEXT("Line 3"));
AddEntry(TEXT("Line 4"));
AddEntry(TEXT("Line 5"));
AddEntry(TEXT("Line 6"));
AddEntry(TEXT("Line 7"));
AddEntry(TEXT("Line 8"));
AddEntry(TEXT("Line 8"));
printf(" Press q to exit. ");
}
void AddEntry(LPCTSTR lpszPrompt)
...{
DWORD cWritten;
if ( WriteFile(
hStdout, // output handle
lpszPrompt, // prompt string
lstrlen(lpszPrompt), // string length
&cWritten, // bytes written
NULL )) 
...{
totalLine++;
NewLine();
}
}
void SetColor(int i)
...{
COORD coord; 
coord.X = 0; // start at first cell
coord.Y = i; // of first row 
BOOL fSuccess = FillConsoleOutputAttribute(
hStdout, // screen buffer handle
wColor, // color to fill with
80, // number of cells to fill
coord, // first cell to write to
NULL); // actual number written 
if ( i > 0 )
...{
coord.X = 0; // start at first cell
coord.Y = i-1; // of first row 
BOOL fSuccess = FillConsoleOutputAttribute(
hStdout, // screen buffer handle
wOldColorAttrs, // color to fill with
80, // number of cells to fill
coord, // first cell to write to
NULL); // actual number written
}
if ( i < totalLine )
...{
coord.X = 0; // start at first cell
coord.Y = i+1; // of first row 
BOOL fSuccess = FillConsoleOutputAttribute(
hStdout, // screen buffer handle
wOldColorAttrs, // color to fill with
80, // number of cells to fill
coord, // first cell to write to
NULL); // actual number written
}
}
void Work(int workNumber)
...{
switch(workNumber) 
...{
case 0:
break;
case 1:
break;
case 2:
break;
default:
break;
}
printf("Work %d finished! ", workNumber+1);
}
// 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(void) 
...{
if (! GetConsoleScreenBufferInfo(hStdout, &csbiInfo)) 
...{
MessageBox(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)) 
...{
MessageBox(NULL, "SetConsoleCursorPosition", "Console Error",
MB_OK);
return;
}
} 
void ScrollScreenBuffer(HANDLE h, INT x)
...{
SMALL_RECT srctScrollRect, 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 = (char)' ';
// Scroll up one line.
ScrollConsoleScreenBuffer(
h, // screen buffer handle
&srctScrollRect, // scrolling rectangle
&srctClipRect, // clipping rectangle
coordDest, // top left destination cell
&chiFill); // fill character and color
}
效果如下:


被折叠的 条评论
为什么被折叠?



