关于WM_CHAR 消息的解读

本文解析了WM_CHAR消息及OnChar函数的工作原理,重点解释了如何处理键盘输入中的数字和小数点,并探讨了虚拟键码、扫描码与ASCII码之间的关系。
      

一直都以为OnChar消息函数 或者 说 WM_CHAR 可以处理所有的按键信息。原来是错误的,WM_CHAR 对应的只是字符而不是按键。其中,字符是指的0-127内的ASCII码。具体的说明参照以下的文章。

    http://hi.baidu.com/%BF%AA%D0%C4_%D0%D6%B5%DC/blog/item/170b5273f26e7a1f8601b0c1.html

    

 理解虚拟键码、扫描码和ASCII字符码

        在编写MFC应用程序过程中,需要对原有的CEdit作功能上的扩展,新生成的类CEditEx继承于CEdit,只允许用户输入数字和小数点。
要实现只允许用户输入数字和小数点,需要屏蔽非数字和小数点的字符,屏蔽工作在OnChar消息函数中进行:当按下键盘后,解发WM_CHAR消息,并进入OnChar消息函数,用::isdigit(nChar)验证数字,用nChar == '.'验证小数点,满足其一即调用父类OnChar函数继续处理:CEdit::OnChar(nChar, nRepcnt, nFlag)。

另外,一些按键如ESC、Tab键,当按下后并不能够触发WM_CHAR消息并进入OnChar消息函数,这时需要在PreTranslateMessage函数中用::TranslateMessage函数对pMsg消息翻译处理,其实也就是在这个函数中将虚拟键码(Virtua- Key) pMsg->wParam重新翻译为ASCII字符码,当翻译的ASCII字符码在0-127之间时,将向消息队列中递交字符消息WM_CHAR。
试一下:如果在调用::TranslateMessage函数后立刻用GetMessage截获消息MSG,将会发现下一个消息是WM_CHAR,并且wParam已被翻译为ASCII字符码。

理解1. 
OnChar函数参数变量nChar是ASCII字符码,经过试验只有当按键所对应的ASCII码在0-127之间时才触发WM_CHAR消息,并进入OnChar消息函数,例如:键入上下左右键,是不会进入OnChar函数的。

理解2.
如果对计算机键盘I/O比较了解,应该知道键盘上每一个键对应一个扫描码,扫描码是由OEM生商制定的,不同厂商生产的键盘同样一个按键的扫描码都有可能出现不一致的情况,为摆脱由于系统设备不一致造成扫描码不一致的情形,通过键盘驱动程序将扫描码映射为统一的虚拟键码表示,如回车键定义为VK_RETURN,其16进制值为0x0D。


相关资源: ascii码表百科

                   http://baike.baidu.com/view/15482.htm

#include <stdio.h> #include <Windows.h> #include <process.h> #include <conio.h> #include "MvCameraControl.h" #ifndef MV_E_TIMEOUT #define MV_E_TIMEOUT -7 #endif // 等待按键输入 void WaitForKeyPress(void) { while (!_kbhit()) Sleep(10); _getch(); } // 全局变量 bool g_bExit = false; bool g_bStartRecord = false; // 用户触发录制 long g_nTargetFrameCount = 0; // 目标帧数 long g_nRecordedFrames = 0; // 已录帧数 void* g_handle = nullptr; float g_fFrameRate = 30.0f; int g_nWidth = 640, g_nHeight = 480; HWND g_hWnd = nullptr; unsigned char* g_pDisplayBuffer = nullptr; CRITICAL_SECTION g_csDisplay; // 保护显示缓冲区 const char* WINDOW_CLASS_NAME = "CameraLiveView"; const char* WINDOW_TITLE = "Camera Live View - Press 'R' to Record 10s"; // 函数声明 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); unsigned int __stdcall DisplayThread(void* lpParam); // 创建显示窗口线程 // 显示窗口线程函数 - 仅运行窗口过程 unsigned int __stdcall DisplayThread(void* lpParam) { HINSTANCE hInstance = (HINSTANCE)lpParam; WNDCLASSEX wcex = { sizeof(WNDCLASSEX) }; wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wcex.lpszClassName = WINDOW_CLASS_NAME; wcex.hIconSm = LoadIcon(wcex.hInstance, IDI_APPLICATION); RegisterClassEx(&wcex); g_hWnd = CreateWindow( WINDOW_CLASS_NAME, WINDOW_TITLE, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, g_nWidth + 20, g_nHeight + 60, NULL, NULL, hInstance, NULL); if (!g_hWnd) { printf("Failed to create window!\n"); return 1; } ShowWindow(g_hWnd, SW_SHOW); UpdateWindow(g_hWnd); MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; } // 判断是否为彩色像素格式 bool IsColor(MvGvspPixelType enType) { switch (enType) { case PixelType_Gvsp_BGR8_Packed: case PixelType_Gvsp_YUV422_Packed: case PixelType_Gvsp_YUV422_YUYV_Packed: case PixelType_Gvsp_BayerGR8: case PixelType_Gvsp_BayerRG8: case PixelType_Gvsp_BayerGB8: case PixelType_Gvsp_BayerBG8: // 支持更多 Bayer 和 YUV 格式... case PixelType_Gvsp_BayerGB10: case PixelType_Gvsp_BayerBG10: case PixelType_Gvsp_BayerRG10: case PixelType_Gvsp_BayerGR10: return true; default: return false; } } // 窗口过程函数 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); EnterCriticalSection(&g_csDisplay); if (g_pDisplayBuffer && g_nWidth > 0 && g_nHeight > 0) { BITMAPINFO bmi = { 0 }; bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biWidth = g_nWidth; bmi.bmiHeader.biHeight = -g_nHeight; // Top-down DIB bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 24; bmi.bmiHeader.biCompression = BI_RGB; SetDIBitsToDevice(hdc, 0, 0, g_nWidth, g_nHeight, 0, 0, 0, g_nHeight, g_pDisplayBuffer, &bmi, DIB_RGB_COLORS); } else { TextOut(hdc, 10, 10, "No image data", 13); } LeaveCriticalSection(&g_csDisplay); EndPaint(hWnd, &ps); break; } case WM_KEYDOWN: if (wParam == 'R' || wParam == 'r') { if (!g_bStartRecord) { g_bStartRecord = true; g_nRecordedFrames = 0; g_nTargetFrameCount = (long)(g_fFrameRate * 10.0f); printf(">> Recording started: Target %ld frames (@ %.1ffps)\n", g_nTargetFrameCount, g_fFrameRate); } } else if (wParam == VK_ESCAPE || wParam == 'Q' || wParam == 'q') { g_bExit = true; PostQuitMessage(0); } break; case WM_DESTROY: g_bExit = true; PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } // 打印设备信息 bool PrintDeviceInfo(MV_CC_DEVICE_INFO* pstMVDevInfo) { if (!pstMVDevInfo) return false; if (pstMVDevInfo->nTLayerType == MV_GIGE_DEVICE) { DWORD ip = pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp; printf("IP: %d.%d.%d.%d\n", (ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, ip & 0xFF); printf("Name: %s\n\n", pstMVDevInfo->SpecialInfo.stGigEInfo.chUserDefinedName); } else if (pstMVDevInfo->nTLayerType == MV_USB_DEVICE) { printf("Name: %s\nSN: %s\nDevNum: %d\n\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.chUserDefinedName, pstMVDevInfo->SpecialInfo.stUsb3VInfo.chSerialNumber, pstMVDevInfo->SpecialInfo.stUsb3VInfo.nDeviceNumber); } // 其他类型略... return true; } // 主函数 int main() { int nRet = MV_OK; void* handle = nullptr; unsigned char* pConvertData = nullptr; unsigned int nConvertDataSize = 0; do { InitializeCriticalSection(&g_csDisplay); // 初始化 SDK nRet = MV_CC_Initialize(); if (MV_OK != nRet) { printf("Initialize SDK failed! nRet [0x%x]\n", nRet); break; } // 枚举设备 MV_CC_DEVICE_INFO_LIST stDeviceList = { 0 }; nRet = MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE, &stDeviceList); if (MV_OK != nRet || stDeviceList.nDeviceNum == 0) { printf("No devices found or enum failed! nRet [0x%x]\n", nRet); break; } for (unsigned int i = 0; i < stDeviceList.nDeviceNum; ++i) { printf("[Device %u]:\n", i); PrintDeviceInfo(stDeviceList.pDeviceInfo[i]); } unsigned int nIndex; printf("Select camera index (0-%d): ", stDeviceList.nDeviceNum - 1); scanf_s("%u", &nIndex); if (nIndex >= stDeviceList.nDeviceNum) { printf("Invalid index!\n"); break; } // 创建句柄 nRet = MV_CC_CreateHandle(&handle, stDeviceList.pDeviceInfo[nIndex]); if (MV_OK != nRet) { printf("Create handle failed! nRet [0x%x]\n", nRet); break; } g_handle = handle; // 打开设备 nRet = MV_CC_OpenDevice(handle); if (MV_OK != nRet) { printf("Open device failed! nRet [0x%x]\n", nRet); break; } // GigE 包大小优化 if (stDeviceList.pDeviceInfo[nIndex]->nTLayerType == MV_GIGE_DEVICE) { int nPacketSize = MV_CC_GetOptimalPacketSize(handle); if (nPacketSize > 0) { MV_CC_SetIntValueEx(handle, "GevSCPSPacketSize", nPacketSize); } } // 关闭触发模式 MV_CC_SetEnumValue(handle, "TriggerMode", 0); // 获取图像参数 MVCC_INTVALUE stParam = { 0 }; MV_CC_GetIntValue(handle, "Width", &stParam); g_nWidth = stParam.nCurValue; MV_CC_GetIntValue(handle, "Height", &stParam); g_nHeight = stParam.nCurValue; MVCC_FLOATVALUE stFloat = { 0 }; MV_CC_GetFloatValue(handle, "ResultingFrameRate", &stFloat); g_fFrameRate = (stFloat.fCurValue > 0.0f) ? stFloat.fCurValue : 30.0f; // 初始化显示缓冲区与临界区 int imageSize = g_nWidth * g_nHeight * 3; g_pDisplayBuffer = new unsigned char[imageSize]; memset(g_pDisplayBuffer, 0, imageSize); InitializeCriticalSection(&g_csDisplay); // ✅ 必须初始化! // 配置录像参数 MV_CC_RECORD_PARAM stRecordPar = {0}; stRecordPar.enPixelType = PixelType_Gvsp_BGR8_Packed; // 编码器期望 RGB/BGR 输入 stRecordPar.nWidth = g_nWidth; stRecordPar.nHeight = g_nHeight; stRecordPar.fFrameRate = g_fFrameRate; stRecordPar.nBitRate = 5000; stRecordPar.enRecordFmtType = MV_FormatType_AVI; static char szFilePath[] = "./Recording.avi"; stRecordPar.strFilePath = szFilePath; nRet = MV_CC_StartRecord(handle, &stRecordPar); if (MV_OK != nRet) { printf("Start record failed! nRet [0x%x]\n", nRet); break; } // 启动取流 nRet = MV_CC_StartGrabbing(handle); if (MV_OK != nRet) { printf("Start grabbing failed! nRet [0x%x]\n", nRet); break; } HANDLE hThread = (HANDLE)_beginthreadex(nullptr, 0, DisplayThread, GetModuleHandle(nullptr), 0, nullptr); if (!hThread) { printf("Create display thread failed!\n"); break; } // 主循环 MV_FRAME_OUT frame = { 0 }; while (!g_bExit) { nRet = MV_CC_GetImageBuffer(handle, &frame, 1000); if (nRet == MV_OK) { MV_FRAME_OUT_INFO_EX* pInfo = &frame.stFrameInfo; const unsigned char* pData = frame.pBufAddr; // 转换为 BGR8 用于显示和录像 if (IsColor(pInfo->enPixelType)) { nConvertDataSize = g_nWidth * g_nHeight * 3; pConvertData = (unsigned char*)realloc(pConvertData, nConvertDataSize); if (!pConvertData) { printf("Memory allocation failed!\n"); break; } MV_CC_PIXEL_CONVERT_PARAM_EX convertParam = { 0 }; convertParam.nWidth = pInfo->nWidth; convertParam.nHeight = pInfo->nHeight; convertParam.pSrcData = const_cast<unsigned char*>(pData); convertParam.nSrcDataLen = pInfo->nFrameLenEx; convertParam.enSrcPixelType = pInfo->enPixelType; convertParam.enDstPixelType = PixelType_Gvsp_BGR8_Packed; convertParam.pDstBuffer = pConvertData; convertParam.nDstBufferSize = nConvertDataSize; MV_CC_SetBayerCvtQuality(handle, 1); // 插值质量 nRet = MV_CC_ConvertPixelTypeEx(handle, &convertParam); if (nRet != MV_OK) { printf("Convert failed! nRet [0x%x]\n", nRet); } else { // 更新显示缓冲区(加锁) EnterCriticalSection(&g_csDisplay); memcpy(g_pDisplayBuffer, pConvertData, nConvertDataSize); LeaveCriticalSection(&g_csDisplay); InvalidateRect(g_hWnd, NULL, FALSE); // 触发重绘 } } // 输入帧到录像编码器 if (g_bStartRecord && g_nRecordedFrames < g_nTargetFrameCount) { MV_CC_INPUT_FRAME_INFO inputInfo = { 0 }; inputInfo.pData = (unsigned char*)pData; inputInfo.nDataLen = pInfo->nFrameLenEx; nRet = MV_CC_InputOneFrame(handle, &inputInfo); if (nRet == MV_OK) { InterlockedIncrement(&g_nRecordedFrames); } else { printf("Input frame failed! nRet [0x%x]\n", nRet); } } else if (g_bStartRecord && g_nRecordedFrames >= g_nTargetFrameCount) { MV_CC_StopRecord(handle); g_bStartRecord = false; printf("Recording finished. Saved %ld frames.\n", g_nRecordedFrames); } MV_CC_FreeImageBuffer(handle, &frame); } else if (nRet != MV_E_TIMEOUT) { printf("Get image buffer failed! nRet [0x%x]\n", nRet); } Sleep(1); } // 清理 g_bStartRecord = false; if (g_hWnd) PostMessage(g_hWnd, WM_CLOSE, 0, 0); WaitForSingleObject(hThread, 3000); CloseHandle(hThread); MV_CC_StopGrabbing(handle); if (g_bStartRecord || g_nRecordedFrames > 0) { MV_CC_StopRecord(handle); } MV_CC_CloseDevice(handle); MV_CC_DestroyHandle(handle); DeleteCriticalSection(&g_csDisplay); // ✅ 销毁临界区 delete[] g_pDisplayBuffer; free(pConvertData); } while (0); MV_CC_Finalize(); printf("Press any key to exit...\n"); WaitForKeyPress(); return 0; } 分析代码、解读并优化
最新发布
10-25
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值