vc++实现软键盘功能(附带源码)
一、项目背景详细介绍
在现代 Windows 桌面应用程序中,软键盘(On-Screen Keyboard) 是一种非常常见的输入辅助工具。
它的作用是:当用户无法或不方便使用物理键盘时,通过点击屏幕上的虚拟按键来输入字符。
软键盘广泛用于:
-
触摸屏终端(如自助终端机、ATM、工业触控面板);
-
医疗、零售等无键盘设备;
-
安全输入场景(如银行登录密码输入、虚拟终端登录);
-
智能家居控制界面。
在 VC++ 应用程序中,我们可以使用 Win32 API + MFC/SDK 界面 实现一个完整的软键盘组件。
本项目以 Win32 SDK + GDI绘图 + 窗口消息机制 为基础,创建一个可点击的软键盘窗口,模拟物理键盘输入。
项目目标包括:
-
实现可视化键盘界面;
-
支持鼠标点击模拟按键;
-
支持大小写切换;
-
支持数字、符号;
-
将输入结果发送至目标窗口(如文本框)。
二、项目需求详细介绍
2.1 功能性需求
-
程序启动后显示一个软键盘窗口;
-
界面上包含字母键、数字键、符号键;
-
点击按键时,将字符发送到焦点窗口;
-
支持 Shift(切换大小写);
-
支持 Backspace、Enter 等控制键;
-
界面美观,按钮布局规则;
-
支持最小化、关闭等窗口操作。
三、相关技术详细介绍
3.1 Win32 GUI 编程基础
在 Win32 SDK 下,一个 GUI 程序主要由以下部分组成:
-
WinMain():程序入口; -
WNDCLASS/WNDCLASSEX:注册窗口类; -
CreateWindow():创建窗口; -
WndProc():窗口过程函数,用于处理消息(鼠标点击、重绘、键盘输入等)。
软键盘程序的核心逻辑就是:
-
创建一个主窗口;
-
绘制所有“按键”;
-
在鼠标点击时检测按键区域;
-
模拟键盘输入消息发送给目标窗口。
3.2 GDI 图形绘制技术
我们将使用 GDI(Graphics Device Interface) 绘制每个按键:
-
Rectangle():绘制按键矩形; -
DrawText():绘制文字; -
InvalidateRect():触发重绘。
每个按键的数据结构将包含:
RECT rect; // 按钮区域 std::wstring text; // 按钮文字
3.3 模拟按键输入技术
当用户点击软键盘上的按钮时,程序需要将该字符“输入”到当前聚焦的窗口中。
我们使用 Windows 提供的消息函数:
PostMessage(hwndTarget, WM_CHAR, 字符代码, 0);
或者对于控制键(如 Backspace、Enter):
PostMessage(hwndTarget, WM_KEYDOWN, VK_BACK, 0); PostMessage(hwndTarget, WM_KEYUP, VK_BACK, 0);
获取目标窗口句柄的方式:
HWND hwndTarget = GetForegroundWindow(); // 当前聚焦窗口
3.4 按钮状态与点击检测
当用户单击鼠标时,系统会发送 WM_LBUTTONDOWN 消息。
我们通过坐标检测判断点击的是哪个按钮:
POINT pt = { LOWORD(lParam), HIWORD(lParam) }; for (auto &btn : buttons) { if (PtInRect(&btn.rect, pt)) { OnButtonClick(btn.text); } }
3.5 大小写切换(Shift 实现)
通过设置一个全局布尔变量:
bool isUpperCase = false;
每次点击 Shift 按钮时切换状态,并更新所有按键文字(如 'a'→'A')。
四、实现思路详细介绍
-
创建主窗口:注册窗口类并创建软键盘窗口;
-
初始化按键数据:在内存中定义所有按钮的区域与文字;
-
绘制界面:在
WM_PAINT中使用 GDI 绘制矩形与文字; -
响应鼠标点击:在
WM_LBUTTONDOWN消息中检测被点击的按键; -
发送输入消息:通过
PostMessage模拟键盘输入; -
处理特殊键:Shift、Backspace、Enter 等;
-
刷新显示:点击 Shift 后重绘整个界面。
五、完整实现代码
/******************************************************
* 文件: SoftKeyboard.cpp
* 功能: VC++ 实现软键盘(Win32 API)
******************************************************/
#include <windows.h>
#include <string>
#include <vector>
struct KeyButton {
RECT rect; // 按钮区域
std::wstring text; // 按钮文字
};
std::vector<KeyButton> g_buttons; // 全局按钮列表
bool g_isUpperCase = false; // 是否大写
HWND g_hwndMain = NULL; // 主窗口句柄
/******************************************************
* 函数: CreateKeyboardLayout
* 功能: 创建键盘布局,定义每个按键的矩形区域与文字
******************************************************/
void CreateKeyboardLayout() {
g_buttons.clear();
int startX = 20, startY = 40;
int keyWidth = 40, keyHeight = 40;
int gap = 5;
std::wstring row1 = L"1234567890";
std::wstring row2 = L"qwertyuiop";
std::wstring row3 = L"asdfghjkl";
std::wstring row4 = L"zxcvbnm";
// 第一行(数字)
for (int i = 0; i < (int)row1.size(); i++) {
RECT r = { startX + i * (keyWidth + gap), startY,
startX + i * (keyWidth + gap) + keyWidth, startY + keyHeight };
g_buttons.push_back({ r, std::wstring(1, row1[i]) });
}
// 第二行(字母)
startY += keyHeight + gap;
for (int i = 0; i < (int)row2.size(); i++) {
RECT r = { startX + i * (keyWidth + gap) + 20, startY,
startX + i * (keyWidth + gap) + 20 + keyWidth, startY + keyHeight };
g_buttons.push_back({ r, std::wstring(1, row2[i]) });
}
// 第三行
startY += keyHeight + gap;
for (int i = 0; i < (int)row3.size(); i++) {
RECT r = { startX + i * (keyWidth + gap) + 40, startY,
startX + i * (keyWidth + gap) + 40 + keyWidth, startY + keyHeight };
g_buttons.push_back({ r, std::wstring(1, row3[i]) });
}
// 第四行
startY += keyHeight + gap;
for (int i = 0; i < (int)row4.size(); i++) {
RECT r = { startX + i * (keyWidth + gap) + 60, startY,
startX + i * (keyWidth + gap) + 60 + keyWidth, startY + keyHeight };
g_buttons.push_back({ r, std::wstring(1, row4[i]) });
}
// 特殊键
RECT shiftRect = { 20, startY, 100, startY + keyHeight };
g_buttons.push_back({ shiftRect, L"Shift" });
RECT backRect = { 480, 40, 560, 80 };
g_buttons.push_back({ backRect, L"Back" });
RECT enterRect = { 480, startY, 560, startY + keyHeight };
g_buttons.push_back({ enterRect, L"Enter" });
}
/******************************************************
* 函数: DrawKeyboard
* 功能: 绘制键盘按键
******************************************************/
void DrawKeyboard(HDC hdc) {
for (auto &btn : g_buttons) {
Rectangle(hdc, btn.rect.left, btn.rect.top, btn.rect.right, btn.rect.bottom);
std::wstring text = btn.text;
if (text.size() == 1 && g_isUpperCase)
text[0] = towupper(text[0]);
DrawText(hdc, text.c_str(), -1, (LPRECT)&btn.rect,
DT_CENTER | DT_VCENTER | DT_SINGLELINE);
}
}
/******************************************************
* 函数: SendCharToForeground
* 功能: 将字符发送到当前焦点窗口
******************************************************/
void SendCharToForeground(wchar_t ch) {
HWND hwndTarget = GetForegroundWindow();
if (hwndTarget && hwndTarget != g_hwndMain) {
PostMessage(hwndTarget, WM_CHAR, ch, 0);
}
}
/******************************************************
* 函数: OnButtonClick
* 功能: 按钮点击事件处理
******************************************************/
void OnButtonClick(const std::wstring &text) {
if (text == L"Shift") {
g_isUpperCase = !g_isUpperCase;
InvalidateRect(g_hwndMain, NULL, TRUE);
} else if (text == L"Back") {
HWND hwndTarget = GetForegroundWindow();
if (hwndTarget && hwndTarget != g_hwndMain) {
PostMessage(hwndTarget, WM_KEYDOWN, VK_BACK, 0);
PostMessage(hwndTarget, WM_KEYUP, VK_BACK, 0);
}
} else if (text == L"Enter") {
HWND hwndTarget = GetForegroundWindow();
if (hwndTarget && hwndTarget != g_hwndMain) {
PostMessage(hwndTarget, WM_KEYDOWN, VK_RETURN, 0);
PostMessage(hwndTarget, WM_KEYUP, VK_RETURN, 0);
}
} else if (text.size() == 1) {
wchar_t ch = g_isUpperCase ? towupper(text[0]) : text[0];
SendCharToForeground(ch);
}
}
/******************************************************
* 函数: WndProc
* 功能: 主窗口消息处理函数
******************************************************/
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_CREATE:
CreateKeyboardLayout();
break;
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
DrawKeyboard(hdc);
EndPaint(hwnd, &ps);
break;
}
case WM_LBUTTONDOWN: {
POINT pt = { LOWORD(lParam), HIWORD(lParam) };
for (auto &btn : g_buttons) {
if (PtInRect(&btn.rect, pt)) {
OnButtonClick(btn.text);
break;
}
}
break;
}
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
/******************************************************
* 函数: WinMain
* 功能: 程序入口
******************************************************/
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow) {
const wchar_t CLASS_NAME[] = L"SoftKeyboardWindow";
WNDCLASS wc = {};
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
RegisterClass(&wc);
g_hwndMain = CreateWindowEx(
0, CLASS_NAME, L"虚拟软键盘", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 640, 320,
NULL, NULL, hInstance, NULL);
ShowWindow(g_hwndMain, nCmdShow);
UpdateWindow(g_hwndMain);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
六、代码详细解读
-
CreateKeyboardLayout():
定义所有按键的矩形区域与文字内容,构建软键盘布局。 -
DrawKeyboard():
使用 GDI 绘制所有按键矩形与文字,自动根据大小写状态更新。 -
SendCharToForeground():
获取当前焦点窗口句柄并向其发送字符输入消息。 -
OnButtonClick():
处理按钮逻辑,包括 Shift 切换、Backspace 删除、Enter 输入与普通字符输入。 -
WndProc():
处理窗口消息,包括绘制、点击、退出等。 -
WinMain():
程序入口,注册窗口类并创建窗口,启动消息循环。
七、项目详细总结
本项目演示了如何在 VC++ 环境下使用 Win32 API 实现一个功能完整的软键盘系统。
主要技术点包括:
-
窗口消息机制:响应鼠标点击;
-
GDI 绘图技术:动态绘制界面;
-
输入模拟:通过
PostMessage实现虚拟输入; -
状态控制:实现大小写切换;
-
组件化设计:可独立嵌入到其他系统中。
该实现逻辑清晰、结构紧凑,适用于教学、项目演示或嵌入式设备开发。
八、项目常见问题及解答
Q1:点击软键盘时文字没有输入到目标窗口?
A1:确认当前输入焦点在可输入窗口上(例如文本框)。软键盘窗口本身不应获得焦点。
Q2:为什么按键显示错位?
A2:检查 DPI 设置或调整按键间距参数。
Q3:能否支持中文输入?
A3:可以,但需增加输入法或拼音逻辑模块,这属于扩展功能。
Q4:如何让软键盘保持在最前?
A4:使用 SetWindowPos(hwnd, HWND_TOPMOST, ...)。
Q5:是否可做为 DLL 嵌入其他程序?
A5:可以,将 CreateKeyboardLayout() 与绘制逻辑封装为类,导出 API 调用。
九、扩展方向与性能优化
-
增加符号键与功能键布局(Tab、Ctrl、Alt);
-
支持触摸输入(WM_TOUCH),用于触屏设备;
-
增加透明背景与圆角键绘制(GDI+);
-
加入动画反馈(按键高亮效果);
-
增加输入日志与统计模块;
-
多语言键盘布局(中/英/日等);
-
模块化封装为可重用控件(DLL/COM);
-
优化绘图性能:使用双缓冲防止闪烁。
更多推荐

所有评论(0)