科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件控制台窗口界面的编程控制(五)

控制台窗口界面的编程控制(五)

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

控制台窗口中的光标反映了文本插入的当前位置,通过SetConsoleCursorPosition函数可以改变这个“当前”位置,这样就能控制字符(串)输出。

作者:丁有和 来源:yesky 2007年10月30日

关键字: 控制台 窗口界面 编程 Linux

  • 评论
  • 分享微博
  • 分享邮件
八、光标操作

  控制台窗口中的光标反映了文本插入的当前位置,通过SetConsoleCursorPosition函数可以改变这个“当前”位置,这样就能控制字符(串)输出。事实上,光标本身的大小和显示或隐藏也可以通过相应的API函数进行设定。例如:

BOOL SetConsoleCursorInfo( // 设置光标信息
 HANDLE hConsoleOutput, // 句柄
 CONST CONSOLE_CURSOR_INFO *lpConsoleCursorInfo // 光标信息
);

BOOL GetConsoleCursorInfo( // 获取光标信息
 HANDLE hConsoleOutput, // 句柄
 PCONSOLE_CURSOR_INFO lpConsoleCursorInfo // 返回光标信息
);

  这两个函数都与CONSOLE_CURSOR_INFO结构体类型有关,其定义如下:

typedef struct _CONSOLE_CURSOR_INFO {
 DWORD dwSize; // 光标百分比大小
 BOOL bVisible; // 是否可见
} CONSOLE_CURSOR_INFO, *PCONSOLE_CURSOR_INFO;

  需要说明的是,dwSize值反映了光标的大小,它的值范围为1-100;当为1时,光标最小,仅是一条最靠下的水平细线,当为100,光标最大,为一个字符大小的方块。

  九、读取键盘信息

  键盘事件通常有字符事件和按键事件,这些事件所附带的信息构成了键盘信息。它是通过API函数ReadConsoleInput来获取的,其原型如下:

BOOL ReadConsoleInput(
 HANDLE hConsoleInput, // 输入设备句柄
 PINPUT_RECORD lpBuffer, // 返回数据记录
 DWORD nLength, // 要读取的记录数
 LPDWORD lpNumberOfEventsRead // 返回已读取的记录数
);

  其中,INPUT_RECORD定义如下:

typedef struct _INPUT_RECORD {
 WORD EventType; // 事件类型
 union {
  KEY_EVENT_RECORD KeyEvent;
  MOUSE_EVENT_RECORD MouseEvent;
  WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent;
  MENU_EVENT_RECORD MenuEvent;
  FOCUS_EVENT_RECORD FocusEvent;
 } Event;
} INPUT_RECORD;

  与键盘事件相关的记录结构KEY_EVENT_RECORD定义如下:

typedef struct _KEY_EVENT_RECORD {
 BOOL bKeyDown; // TRUE表示键按下,FALSE表示键释放
 WORD wRepeatCount; // 按键次数
 WORD wVirtualKeyCode; // 虚拟键代码
 WORD wVirtualScanCode; // 虚拟键扫描码
 union {
  WCHAR UnicodeChar; // 宽字符
  CHAR AsciiChar; // ASCII字符
 } uChar; // 字符
 DWORD dwControlKeyState; // 控制键状态
} KEY_EVENT_RECORD;

  我们知道,键盘上每一个有意义的键都对应着一个唯一的扫描码,虽然扫描码可以作为键的标识,但它依赖于具体设备的。因此,在应用程序中,使用的往往是与具体设备无关的虚拟键代码。这种虚拟键代码是与设备无关的键盘编码。在Visual C++中,最常用的虚拟键代码已被定义在Winuser.h中,例如:VK_SHIFT表示SHIFT键,VK_F1表示功能键F1等。

  上述结构定义中,dwControlKeyState用来表示控制键状态,它可以是CAPSLOCK_ON(CAPS LOCK灯亮)、ENHANCED_KEY(按下扩展键)、LEFT_ALT_PRESSED(按下左ALT键)、LEFT_CTRL_PRESSED(按下左CTRL键)、NUMLOCK_ON (NUM LOCK灯亮)、RIGHT_ALT_PRESSED(按下右ALT键)、RIGHT_CTRL_PRESSED(按下右CTRL键)、SCROLLLOCK_ON(SCROLL LOCK灯亮)和SHIFT_PRESSED(按下SHIFT键)中的一个或多个值的组合。

  下面的程序是将用户按键的字符输入到一个控制台窗口的某个区域中,并当按下NUM LOCK、CAPS LOCK和SCROLL LOCK键时,在控制台窗口的最后一行显示这些键的状态。

#include
HANDLE hOut;
HANDLE hIn;
void DrawBox(bool bSingle, SMALL_RECT rc);
void ClearScreen(void);
void CharWindow(char ch, SMALL_RECT rc); // 将ch输入到指定的窗口中
void ControlStatus(DWORD state); // 在最后一行显示控制键的状态
void DeleteTopLine(SMALL_RECT rc); // 删除指定窗口中最上面的行并滚动
void main()
{
 hOut = GetStdHandle(STD_OUTPUT_HANDLE); // 获取标准输出设备句柄
 hIn = GetStdHandle(STD_INPUT_HANDLE); // 获取标准输入设备句柄
 WORD att = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY |  
       BACKGROUND_BLUE ;
 // 背景是蓝色,文本颜色是黄色
 SetConsoleTextAttribute(hOut, att);
 ClearScreen(); // 清屏
 INPUT_RECORD keyRec;
 DWORD state = 0, res;
 char ch;
 SMALL_RECT rc = {20, 2, 40, 12};
 DrawBox(true, rc);
 COORD pos = {rc.Left+1, rc.Top+1};
 SetConsoleCursorPosition(hOut, pos); // 设置光标位置
 for(;;) // 循环
 {
  ReadConsoleInput(hIn, &keyRec, 1, &res);
  if (state != keyRec.Event.KeyEvent.dwControlKeyState) {
   state = keyRec.Event.KeyEvent.dwControlKeyState;
   ControlStatus(state);
  }
  if (keyRec.EventType == KEY_EVENT){
   if (keyRec.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE) break;
   // 按ESC键退出循环
   if (keyRec.Event.KeyEvent.bKeyDown) {
    ch = keyRec.Event.KeyEvent.uChar.AsciiChar;
    CharWindow(ch, rc);
   }
  }
 }
 pos.X = 0; pos.Y = 0;
 SetConsoleCursorPosition(hOut, pos); // 设置光标位置
 CloseHandle(hOut); // 关闭标准输出设备句柄
 CloseHandle(hIn); // 关闭标准输入设备句柄
}

void CharWindow(char ch, SMALL_RECT rc) // 将ch输入到指定的窗口中
{
 static COORD chPos = {rc.Left+1, rc.Top+1};
 SetConsoleCursorPosition(hOut, chPos); // 设置光标位置
 if ((ch<0x20)||(ch>0x7e)) return;
 WriteConsoleOutputCharacter(hOut, &ch, 1, chPos, NULL);
 if (chPos.X>=(rc.Right-1))
 {
  chPos.X = rc.Left;
  chPos.Y++;
 }
 if (chPos.Y>(rc.Bottom-1))
 {
  DeleteTopLine(rc);
  chPos.Y = rc.Bottom-1;
 }
 chPos.X++;
 SetConsoleCursorPosition(hOut, chPos); // 设置光标位置
}

void ControlStatus(DWORD state) // 在最后一行显示控制键的状态
{
 CONSOLE_SCREEN_BUFFER_INFO bInfo;
 GetConsoleScreenBufferInfo( hOut, &bInfo );
 COORD home = {0, bInfo.dwSize.Y-1};
 WORD att0 = BACKGROUND_INTENSITY ;
 WORD att1 = FOREGROUND_GREEN | FOREGROUND_INTENSITY | BACKGROUND_RED;
 FillConsoleOutputAttribute(hOut, att0, bInfo.dwSize.X, home, NULL);
 FillConsoleOutputCharacter(hOut, ' ', bInfo.dwSize.X, home, NULL);
 SetConsoleTextAttribute(hOut, att1);
 COORD staPos = {bInfo.dwSize.X-16,bInfo.dwSize.Y-1};
 SetConsoleCursorPosition(hOut, staPos);
 if (state & NUMLOCK_ON)
 WriteConsole(hOut, "NUM", 3, NULL, NULL);
 staPos.X += 4;
 SetConsoleCursorPosition(hOut, staPos);
 if (state & CAPSLOCK_ON)
 WriteConsole(hOut, "CAPS", 4, NULL, NULL);
 staPos.X += 5;
 SetConsoleCursorPosition(hOut, staPos);
 if (state & SCROLLLOCK_ON)
 WriteConsole(hOut, "SCROLL", 6, NULL, NULL);
 SetConsoleTextAttribute(hOut, bInfo.wAttributes); // 恢复原来的属性
 SetConsoleCursorPosition(hOut, bInfo.dwCursorPosition); // 恢复原来的光标位置
}

void DeleteTopLine(SMALL_RECT rc)
{
 COORD crDest;
 CHAR_INFO chFill;
 SMALL_RECT rcClip = rc;
 rcClip.Left++; rcClip.Right--;
 rcClip.Top++; rcClip.Bottom--;
 crDest.X = rcClip.Left;
 crDest.Y = rcClip.Top - 1;
 CONSOLE_SCREEN_BUFFER_INFO bInfo;
 GetConsoleScreenBufferInfo( hOut, &bInfo );
 chFill.Attributes = bInfo.wAttributes;
 chFill.Char.AsciiChar = ' ';
 ScrollConsoleScreenBuffer(hOut, &rcClip, &rcClip, crDest, &chFill);
}

  程序运行结果如下图所示:
  


查看本文来源

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章