Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【Windows编程】系列第十篇:文本插入符

【Windows编程】系列第十篇:文本插入符

作者头像
程序员互动联盟
发布于 2018-03-14 06:57:20
发布于 2018-03-14 06:57:20
1.8K00
代码可运行
举报
运行总次数:0
代码可运行

大家知道,在使用微软的编程环境创建工程时会让你选择是控制台模式还是Windows应用程序。如果选择控制台的console模式,就会在运行时出现一个黑洞洞的字符模式窗口,里面就有等待输入一闪一闪的插入符。输入光标从DOS时代就存在,但是在Win32中赋予了更强大的功能。下图就是Windows的CMD窗口,其中的输入点就是插入光标:

要注意的是这里的插入符或插入光标并不是Windows中另外一个“光标”,这里是指示插入字符的位置,而不是用于鼠标,手写输入等可以定位、移动的光标(Cursor),而是插入符Caret,本文也成为插入光标,注意插入二字,为了方便,以下在本文中也简称为光标或插入符,但要注意此光标非彼光标。

为什么会有插入光标(插入符)?了解了这个基本问题,就成功了一半了。我们知道计算机可以通过键盘来输入各种字符和控制符,那么自然就存在一个问题,输入的字符应该放到屏幕的什么位置?这个就是光标产生的原因,光标实际上就是一个字符插入标识。简单的说就是我们想把字符输入到哪个位置,就先把插入符设置到那里,设置时其实就是告诉电脑输入的字符你给我在哪里显示!从这个我们也可以推断,插入符在同一时刻只能有一个。

  • 光标相关API函数

要使用光标,首先得创建一个光标,创建光标的API函数为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
BOOL CreateCaret(HWND hWnd, HBITMAP hBitmap, int nWidth, int nHeight);

hWnd参数表示光标是属于哪个窗口。

hBitmap参数是一个位图的句柄,计算机将使用这个句柄的位图来作为光标的形状。

既然光标是给使用电脑的人插入字符用的,那就得有形状让使用者能看到,因此光标需要有一个可见的小图标。当然为了不同的情况和需求,Windows让我们可以自定义光标的形状。常见的位图创建或者加载API函数如CreateBitmap、CreateDIBitmap、LoadBitmap等都可以创建或加载一个位图,将句柄作为该参数。

nWidth和nHeight分别是位图的宽和高。

光标创建之后是不会自动显示的,也就是默认是隐藏状态,需要主动调用下面的显示函数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
BOOL ShowCaret(HWND hWnd);

当然有显示光标也可以隐藏光标:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
BOOL HideCaret(HWND hWnd);

以上两个函数的参数很简单,都是要显示窗口的句柄。

要设置插入符的位置,使用下面API函数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
BOOL SetCaretPos(int X, int Y);

参数X、Y分别是相对于窗口客户区的坐标。

我们可以用如下API函数获取当前光标的位置:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
BOOL GetCaretPos(LPPOINT lpPoint);

参数lpPoint返回当前光标所在的位置。

我们知道光标会闪烁,这个闪烁的时间间隔是可以设置的,我们可以用如下API来设置和获取插入光标的闪烁时间:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
BOOL SetCaretBlinkTime(UINT uMSeconds);
UINT GetCaretBlinkTime(VOID);

参数uMSeconds为闪烁的间隔毫秒数。

最后不再使用时需要销毁光标:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
BOOL DestroyCaret(VOID);
  • 光标处理相关消息

与插入光标相关的消息主要有WM_SETFOCUS、WM_KILLFOCUS。通常在WM_SETFOCUS中创建和显示光标,而在WM_KILLFOCUS中销毁光标。一般应有中再结合WM_KEYDOWN和WM_CHAR消息,实现文本的输入。

  • 光标应用实例

以下是一个简单的虚拟终端,我们常见的很多终端软件都是这样来实现的,比如常见的SecureCRT、Tera Term、XShell、putty等等。本实例就是用了插入光标来实现字符输入、插入,部分关键代码如下,完整实例代码请猛戳左下角阅读原文

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <windows.h>

//处理字符输入
static void DrawChar(HDC hDC, int x, int y, TCHAR *str, int num)
{
    RECT rect;
    SelectObject(hDC, GetStockObject(SYSTEM_FIXED_FONT));
    SetTextColor(hDC, TextColor);
    SetBkMode(hDC, TRANSPARENT);
    rect.left = x;
    rect.top = y;
    rect.right = x + num * nCharWidth;
    rect.bottom = y + nCharHeight;
    FillRect(hDC, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH));
    TextOut(hDC, nCaretPosX * nCharWidth, nCaretPosY * nCharHeight, &TEXTMATRIX(nCaretPosX, nCaretPosY), nLineChars - nCaretPosX);
}

static LRESULT CALLBACK VTWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int x, y;
HDC hDC;

switch (message)
{
case WM_CREATE:
   hDC = GetDC(hWnd);
   SelectObject(hDC, GetStockObject(SYSTEM_FIXED_FONT));
   GetTextMetrics(hDC, &tm);
   ReleaseDC(hWnd, hDC);
   nCharWidth = tm.tmAveCharWidth;
   nCharHeight = tm.tmHeight;
   TextColor = RGB(255, 255, 255);
   nCaretOffsetY = 12;
   return 0;

case WM_SIZE:
   nVTWidth = LOWORD(lParam);
   nLineChars = max(1, nVTWidth/nCharWidth);
   nVTHeight = HIWORD(lParam);
   nRowChars = max(1, nVTHeight/nCharHeight);
        
   if (pTextMatrix != NULL)
   {
     free(pTextMatrix);
   }
   pTextMatrix = (TCHAR *)malloc(nLineChars * nRowChars);
   if (pTextMatrix)
   {
     for (y=0; y<nRowChars; y++)
     {
       for (x=0; x<nLineChars; x++)
       {
         TEXTMATRIX(x, y) = TEXT(' ');
       }
     }
   }
   SetCaretPos(0, nCaretOffsetY);
   return 0;

case WM_LBUTTONDOWN:
   SetFocus(hWnd);
   break;

case WM_KEYDOWN:
   switch (wParam)
   {
   case VK_HOME:  
     nCaretPosX = 0;
     break; 

   case VK_END: 
     nCaretPosX = nLineChars - 1;
     break; 

   case VK_PRIOR: 
     nCaretPosY = 0; 
     break; 

   case VK_NEXT:
     nCaretPosY = nRowChars -1; 
     break; 

   case VK_LEFT: 
     nCaretPosX = max(nCaretPosX - 1, 0); 
     break; 

  case VK_RIGHT:  
     nCaretPosX = min(nCaretPosX + 1, nLineChars - 1); 
     break; 

  case VK_UP:  
     nCaretPosY = max(nCaretPosY - 1, 0); 
     break; 

  case VK_DOWN: 
     nCaretPosY = min(nCaretPosY + 1, nRowChars - 1); 
     break; 

  case VK_DELETE:  
     for (x = nCaretPosX; x < nLineChars; x++) 
       TEXTMATRIX(x, nCaretPosY) = TEXTMATRIX(x + 1, nCaretPosY); 
     TEXTMATRIX(nLineChars - 1, nCaretPosY) = ' '; 
     HideCaret(hWnd); 
     hDC = GetDC(hWnd); 
     DrawChar(hDC, nCaretPosX * nCharWidth, nCaretPosY * nCharHeight,&TEXTMATRIX(nCaretPosX, nCaretPosY), nLineChars - nCaretPosX/nCharWidth);
     ReleaseDC(hWnd, hDC); 
     ShowCaret(hWnd); 
   break; 
 } 
 SetCaretPos(nCaretPosX * nCharWidth, nCaretPosY * nCharHeight + nCaretOffsetY); 
    return 0;

case WM_SHOWWINDOW:
  SetFocus(hWnd);
  break;

case WM_SETFOCUS:
  CreateCaret(hWnd, NULL, nCharWidth, 2);
  SetCaretPos(nCaretPosX * nCharWidth, nCaretPosY * nCharHeight + nCaretOffsetY);
  ShowCaret(hWnd);
  break;

case WM_KILLFOCUS:
case WM_DESTROY:
  HideCaret(hWnd);
  DestroyCaret();
  break;

case WM_CHAR:
  switch (wParam)
  {
    case 0x08: 
     if (nCaretPosX > 0)
     {
        nCaretPosX--;
        SendMessage(hWnd, WM_KEYDOWN, VK_DELETE, 1L);
     }
     break;
    case 0x09: 
     do
     {
       SendMessage(hWnd, WM_CHAR, TEXT(' '), 2L);
     } while (nCaretPosX % 4 != 0);
     break;
    case 0x0D: 
      nCaretPosX = 0;
       if (++nCaretPosY == nRowChars)
       {
          nCaretPosY = 0;
       }
       break;
     case 0x1B:
     case 0x0A: 
       MessageBeep((UINT)-1);
       break;
     default:
       TEXTMATRIX(nCaretPosX, nCaretPosY) = (TCHAR)wParam;
       HideCaret(hWnd);
       hDC = GetDC(hWnd);
       DrawChar(hDC, nCaretPosX * nCharWidth, nCaretPosY * nCharHeight, &TEXTMATRIX(nCaretPosX, nCaretPosY), 1);
       ReleaseDC(hWnd, hDC);
       ShowCaret(hWnd);
       if (++nCaretPosX == nLineChars)
       {
           nCaretPosX = 0;
           if (++nCaretPosY == nRowChars)
           {
              nCaretPosY = 0;
           }
       }
       break;
    }
    SetCaretPos(nCaretPosX * nCharWidth, nCaretPosY * nCharHeight + nCaretOffsetY);
    return 0;

 case WM_PAINT:
 {
    PAINTSTRUCT ps;
    hDC = BeginPaint(hWnd, &ps);
    SelectObject(hDC, GetStockObject(SYSTEM_FIXED_FONT));
    SetBkMode(hDC, TRANSPARENT);
    SetTextColor(hDC, TextColor);
    for (y=0; y<nLineChars; y++)
    {
      TextOut(hDC, 0, y * nCharHeight, &TEXTMATRIX(0, y), nLineChars);
    }
    EndPaint(hWnd, &ps);
 }
 return 0;
 default:
  break;
 }

 return DefWindowProc (hWnd, message, wParam, lParam);}

主程序调用上面的CreateVirtualTerminal函数来创建一个窗口。

示例程序运行后,在窗口中输入部分文本(模仿上面的cmd窗口^_^),效果如下:

本例实现了一个简单的终端模拟小程序,为了读者重用方便,我将终端模拟的小窗口单独作为一个完整的源文件,并且把窗口背景设为黑色,前景色设为白色,看起来更像CMD、Linux等命令行窗口。

更多经验交流可以加入Windows编程讨论QQ群454398517

转载请注明出处http://www.coderonline.net/?p=1905,谢谢合作!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2015-08-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员互动联盟 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
C++ 游戏之点点水果
大二时利用C++编写的点水果小游戏 程序代码总共3个文件,main.cpp Fruit.h Fruit.cpp  代码将在图片下面给出 至于讲解,由于过了一年多的时间,有点忘记了,但我会努力回忆并即时
magicsoar
2018/02/06
9650
C++ 游戏之点点水果
【Windows编程】系列第六篇:创建Toolbar与Statusbar
上一篇我们学习了解了如何使用Windows GDI画图,该应用程序都是光光的静态窗口,我们使用Windows应用程序,但凡稍微复杂一点的程序都会有工具栏和状态栏,工具栏主要用于一些快捷功能按钮。比如典型的windows应用程序的上面是菜单栏,从菜单栏我们可以选择应用程序提供的各种功能,但是有的功能比较常用,且不能放在第一级菜单,需要进入二级、三级甚至更多的菜单才能选择。显然这样使用起来比较麻烦,于是这时候工具栏的作用就体现出来了,一般工具栏位于菜单栏的下面,但是位于客户窗口的上面。下面就是windows的文
程序员互动联盟
2018/03/13
2.3K1
【Windows编程】系列第六篇:创建Toolbar与Statusbar
windows下实现win32俄罗斯方块练手,编程的几点心得
一个运筹学者接到任务,设计末座大楼的电梯调度策略,使乘客等待的时间最短,在走访了这座大楼之后,他认识到雇主真正想要解决的问题是,尽量减少乘客的不适( 乘客不喜欢等电梯)。他这样解决问题:在每部电梯附近装上几面镜子。乘客在等电梯时候,可以自我欣赏一下,对电梯速度的抱怨大幅减少了。他发现了用户的真正需求
流川疯
2019/01/18
8630
【C++】基础:小游戏示例
DevFrank
2024/07/24
1.9K0
【C++】基础:小游戏示例
win32贪吃蛇实现
写程序是一个循序渐进的过程,一开始都是加加减减,修修补补,这和我们做企业做创新的原理都是一样的,没有一蹴而就的成功,最近看了周鸿祎的《我的互联网方法论》蛮有启发,分享给大家几句摘抄: 1.所有的颠覆式创新都不是敲锣打鼓来的,而是隐藏在一片噪声里。
流川疯
2022/05/10
4770
win32贪吃蛇实现
Windows 下窗口画图---十天教会你俄罗斯方块
接上篇:https://blog.csdn.net/qq_41603898/article/details/80968333
用户2965768
2018/08/30
6510
windows程序设计-第四章 system2.c 新增滚动条功能
新添加的滚动条功能,首先就是要在createWindow的时候,修改参数风格模式 hwnd = CreateWindow (szAppName, TEXT ("Get System Metrics No. 2"), WS_OVERLAPPEDWINDOW | WS_VSCROLL, CW_USEDEFAULT, CW_USEDEFAULT, C
用户1154259
2018/01/17
7200
windows程序设计-第四章 system2.c 新增滚动条功能
【Windows编程】系列第五篇:GDI图形绘制
上两篇我们学习了文本字符输出以及Unicode编写程序,知道如何用常见Win32输出文本字符串,这一篇我们来学习Windows编程中另一个非常重要的部分GDI图形绘图。Windows的GDI函数包含数百个API可供我们使用,本篇把最常用的GDI绘图做一个讲解。GDI可以绘制点、直线曲线、填充封闭区域、位图以及文本,其中文本部分已经在上一篇中将了,请参考【Windows编程】系列第三篇:文本字符输出。 跟前面的GDI对象一样,本篇的这些绘图函数也必须要设备上下文句柄(HDC)作为函数参数,从前文我们知道,HD
程序员互动联盟
2018/03/13
3.7K0
【Windows编程】系列第五篇:GDI图形绘制
使用计时器-方法2
第一种方法 是把WM_TIMER消息发送给正常的窗口过程。 第二种方法 是 自己让 windows把计时器消息发送给程序中的另一个窗口过程 回调函数: VOID CALLBACK TimerProc(HWND hwnd,UINT message,UINT iTimerID,DWORD dwTimer) { .... } 第一种方式调用的SetTImer函数,第四个参数一般直接为NULL SetTimer(hwnd,iTimerID,iMsecInterval,NULL); 第二种方式调用的Set
用户1154259
2018/01/17
6520
俄罗斯方块
//*********************************************// //**************  头文件  *********************// //*********************************************// #include <windows.h> #include <time.h> #include "stdafx.h" //*********************************************// //**************  宏定义  *********************// //*********************************************// #define WND_POS_X 10 //窗口左上角点的横坐标 #define WND_POS_Y 10 //窗口左上角点的纵坐标 #define WND_WIDTH 600 //窗口的宽度 #define WND_HEIGHT 600 //窗口的高度 #define RECT_UPPER_X 0 //背景矩形框的左上角点的横坐标 #define RECT_UPPER_Y 0 //背景矩形框的左上角点的纵坐标 #define RECT_LOWER_X 300 //背景矩形框的右下角点的横坐标 #define RECT_LOWER_Y 600 //背景矩形框的右下角点的纵坐标 #define SIDE_LEN 30 //游戏小方块的边长 #define G_ARR_ROW (RECT_LOWER_Y/SIDE_LEN) // 背景矩形框的行数 600/30=20行 #define G_ARR_RANK 10// 背景矩形框的列数 300/30=10列 #define COMB_ROW 2 //随机方块的组合使用2行4列矩阵存储 #define COMB_RANK 4 #define CHANGE_SIZE 3 //3*3矩阵作为旋转矩阵 #define N_TIMER 1 //定时器的ID #define T_TIMER 500 //定时器的时间,单位为毫秒 #define IDR_MENU1                       130 #define IDR_MENU3                       133 #define IDI_ICON2                       134 #define N_TIMER                          1 //*********************************************// //**************  全局变量  *********************// //*********************************************// int g_arrBackGround[20][20] = { 0 };//背景矩阵 int g_arrRandomSquare[COMB_ROW][COMB_RANK] = { 0 };//随机块组合 int g_nIndex = 0;//随机块组合对应的序号 int G_ROW = 0;//实时记录3*3矩阵的行位置,即需要变形的矩阵位置 int G_RANK = 0;//实时记录3*3矩阵的列位置,即需要变形的矩阵位置 int gScore = 0; int t_nIndex;   //*********************************************//   //**************  函数声明 *********************//   //*********************************************//   //回调函数 LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); //消息响应函数 void OnCreate();//创建窗口时用作初始化随机数种子 void OnPaint(HDC hDC);//窗口变化时重绘函数 void OnReturn(HWND hWnd);//按键处理--回车键 void OnTimer(HWND hWnd);//定时器响应函数 void OnDown(HWND hWnd);//向下加速 void OnLeft(HWND hWnd);//左移响应函数 void OnRight(HWND hWnd);//右移响应函数 void OnUp(HWND hWnd);//上键变形
用户2965768
2018/08/30
8330
俄罗斯方块
BitBlt_bitcmp
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
全栈程序员站长
2022/11/02
2980
游戏编程之八 窗口
Msg("FoxBear is minimized, pausing");
张哥编程
2024/12/17
960
【C/C++】回调函数详解&注册窗口类&LRESULT & CALLBACK详解以及游戏中的应用
WNDCLASSEX 成员比较多,这里简单的做一下介绍,具体信息可以查看相关 MSDN 文档。
20岁爱吃必胜客
2022/11/13
1.9K0
【C/C++】回调函数详解&注册窗口类&LRESULT & CALLBACK详解以及游戏中的应用
windows api学习笔记-使用定时器
#include <windows.h> #include "resource.h" #include <string> LRESULT CALLBACK MainWndProc(HWND,UINT,WPARAM,LPARAM);//窗口函数的函数原型 int APIENTRY WinMain( //APIENTRY是__stdcall的宏定义 HINSTANCE hInstance, //本模块的实例句柄
liulun
2022/05/09
5770
自制截图工具V1.0
之前比较空闲,就尝试着做了个截图工具,功能还算实用。等以后有时间再研究V2.0。 这个项目都是用C语言做的,所以还算精简。 #include <windows.h> #include <stdio.h> #include "resource.h" #pragma comment(lib,"winmm.lib") HINSTANCE g_hInstance = NULL;//全局进程句柄 HWND g_hwndMain = NULL;//全局主窗口句柄 #define UM_SCREENCUT WM_U
_gongluck
2018/03/08
1.3K0
windows api学习笔记-简单的记事本
#include <windows.h> #include "resource.h" #include <string> LRESULT CALLBACK MainWndProc(HWND,UINT,WPARAM,LPARAM);//窗口函数的函数原型 int APIENTRY WinMain( //APIENTRY是__stdcall的宏定义 HINSTANCE hInstance, //本模块的实例句柄
liulun
2022/05/09
4680
【Windows编程】系列第七篇:Menubar的创建和使用
上一篇我们学习了利用windows API创建工具栏和状态栏,与上一篇紧密联系的就是菜单栏,菜单栏是一个大多数复杂一些的Windows应用程序不可或缺的部分。比如下图就是Windows自带的记事本的菜
程序员互动联盟
2018/03/14
1.3K0
【Windows编程】系列第七篇:Menubar的创建和使用
【读书笔记】Windows程序设计5
与从BeginPaint函数返回的设备句柄不同,从GetDC返回的设备句柄中的裁剪区域是整个客户区,而不仅仅
zhang_derek
2022/12/21
2.6K0
VC++6.0入门——第一讲,认识VC++6.0,新建工程等
推荐链接:https://cloud.tencent.com/developer/article/2465228
Arya
2024/11/12
3000
VC++6.0入门——第一讲,认识VC++6.0,新建工程等
HGE 引擎学习笔记 1——如何模拟物理碰撞
WINDOWS SDK 窗口对此过程的模拟(仅摹仿了速度和碰撞等,对声音的相关处理貌似比较复杂,还没搞清楚怎么写。由于是做个简单 DEMO,并没有加入多线程等技术,所以程序里的坐标等数据的同步并不精准,上、下、左、右键最好是短暂地点一下即松开,连着按的话会出现速度的突兀变化):
mzlogin
2020/04/16
6920
相关推荐
C++ 游戏之点点水果
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验