WM_KEYDOWN的lParam参数构造

本文介绍如何构造Windows消息WM_KEYDOWN与WM_KEYUP来模拟键盘按键操作,包括基本的F1键和带有Alt键组合的按键构造方法。

构造F1的WM_KEYDOWN的lParam:

0x1 | (MapVirtualKey(VK_F1, MAPVK_VK_TO_VSC) << 16)

如果Alt也按下

0x1 | ((KF_ALTDOWN|MapVirtualKey(VK_MENU, MAPVK_VK_TO_VSC)) << 16)


构造F1的WM_KEYUP的lParam:

0x1 | ((KF_REPEAT|KF_UP|MapVirtualKey(VK_F1, MAPVK_VK_TO_VSC)) << 16)

如果Alt也按下:

0x1 | ((KF_ALTDOWN|KF_REPEAT|KF_UP|MapVirtualKey(VK_MENU, MAPVK_VK_TO_VSC)) << 16)


使用:

::PostMessage(activeWnd->m_hWnd,WM_KEYDOWN,'V',0x1|(MapVirtualKey('V', MAPVK_VK_TO_VSC) << 16));
::PostMessage(activeWnd->m_hWnd,WM_KEYUP,'V',0x1 | ((KF_REPEAT|KF_UP|MapVirtualKey('V', MAPVK_VK_TO_VSC)) << 16));


转自:http://blog.youkuaiyun.com/evilwk/article/details/9041933

#include <windows.h> #include <windowsx.h> #define ID_TIMER 1 #define STRMAXLEN 25 //一个显示列的最大长度 #define STRMINLEN 8 //一个显示列的最小长度 LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; ////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////// typedef struct tagCharChain //整个当作屏幕的一个显示列,这是个双向列表 { struct tagCharChain *prev; //链表的前个元素 TCHAR ch; //一个显示列中的一个字符 struct tagCharChain *next; //链表的后个元素 }CharChain, *pCharChain; typedef struct tagCharColumn { CharChain *head, *current, *point; int x, y, iStrLen; //显示列的开始显示的x,y坐标,iStrLen是这个列的长度 int iStopTimes, iMustStopTimes; //已经停滞的次数和必须停滞的次数,必须停滞的次数是随机的 }CharColumn, *pCharColumn; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("matrix") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; // class style wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if(!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("此程序必须运行在NT下!"), szAppName, MB_ICONERROR) ; return 0; } hwnd = CreateWindow (szAppName, NULL, WS_DLGFRAME | WS_THICKFRAME | WS_POPUP, // windows style 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, SW_SHOWMAXIMIZED) ; //最大化显示 show windows UpdateWindow (hwnd) ; ShowCursor(FALSE); //隐藏鼠标光标 srand ((int) GetCurrentTime ()) ; //初始化随机数发生器 while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } ShowCursor(TRUE); //显示鼠标光标 return msg.wParam ; } TCHAR randomChar() //随机字符产生函数 { return (TCHAR)(rand()%(126-33)+33); //33到126之间 } int init(CharColumn *cc, int cyScreen, int x) //初始化 { int j; cc->iStrLen = rand()%(STRMAXLEN-STRMINLEN) + STRMINLEN; //显示列的长度 cc->x = x+3 ; //显示列的开始显示的x坐标 cc->y =rand()%3?rand()%cyScreen:0; //显示列的开始显示的y坐标 cc->iMustStopTimes = rand()%6 ; cc->iStopTimes = 0 ; cc->head = cc->current = (pCharChain)calloc(cc->iStrLen, sizeof(CharChain)); //生成显示列 for(j=0; j<cc->iStrLen-1; j++) { cc->current->prev = cc->point; //cc->point一个显示列的前个元素 cc->current->ch = '\0'; cc->current->next = cc->current+1; //cc->current+1一个显示列的后个元素 cc->point = cc->current++; //cc->point = cc->current; cc->current++; } cc->current->prev = cc->point; //最后一个节点 cc->current->ch = '\0'; cc->current->next = cc->head; cc->head->prev = cc->current; //头节点的前一个为此链的最后一个元素 cc->current = cc->point = cc->head; //free掉申请的内存要用current当参数 cc->head->ch = randomChar(); // 对链表头的 元素填充 return 0; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc ; //ctn 用来确定一个显示链是否 向下前进,如果等待次数超过必须等待的次数,ctn就代表要向下前进 int i, j, temp, ctn; //j为一个显示链中除链表头外的在屏幕上显示的y坐标,temp绿色过度到黑色之用 static HDC hdcMem; HFONT hFont; static HBITMAP hBitmap; static int cxScreen, cyScreen; //屏幕的宽度 高度. static int iFontWidth=10, iFontHeight=15, iColumnCount; //字体的宽度 高度, 列数 static CharColumn *ccChain; switch (message) { case WM_CREATE: cxScreen = GetSystemMetrics(SM_CXSCREEN) ; //屏幕宽度 cyScreen = GetSystemMetrics(SM_CYSCREEN) ; SetTimer (hwnd, ID_TIMER, 10, NULL) ; hdc = GetDC(hwnd); hdcMem = CreateCompatibleDC(hdc); hBitmap = CreateCompatibleBitmap(hdc, cxScreen, cyScreen); SelectObject(hdcMem, hBitmap); ReleaseDC(hwnd, hdc); //创建字体 hFont = CreateFont(iFontHeight, iFontWidth-5, 0, 0, FW_BOLD, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DRAFT_QUALITY, FIXED_PITCH | FF_SWISS, TEXT("Fixedsys")); SelectObject(hdcMem, hFont); DeleteObject (hFont) ; SetBkMode(hdcMem, TRANSPARENT); //设置背景模式为 透明 iColumnCount = cxScreen/(iFontWidth*3/2); //屏幕所显示字母雨的列数 ccChain = (pCharColumn)calloc(iColumnCount, sizeof(CharColumn)); for(i=0; i<iColumnCount; i++) { init(ccChain+i, cyScreen, (iFontWidth*3/2)*i); } return 0 ; case WM_TIMER: hdc = GetDC(hwnd); PatBlt (hdcMem, 0, 0, cxScreen, cyScreen, BLACKNESS) ; //将内存设备映像刷成黑色 for(i=0; i<iColumnCount; i++) { ctn = (ccChain+i)->iStopTimes++ > (ccChain+i)->iMustStopTimes; // (ccChain+i)->point = (ccChain+i)->head; //point用于遍历整个显示列 //第一个字符显示为 白色 SetTextColor(hdcMem, RGB(255, 255, 255)); TextOut(hdcMem, (ccChain+i)->x, (ccChain+i)->y, &((ccChain+i)->point->ch), 1); j = (ccChain+i)->y; (ccChain+i)->point = (ccChain+i)->point->next; //遍历整个显示列,将这个显示列里的字符从下往上显示 temp = 0 ; //temp绿色过度到黑色之用 while((ccChain+i)->point != (ccChain+i)->head && (ccChain+i)->point->ch) { SetTextColor(hdcMem, RGB(0, 255-(255*(temp++)/(ccChain+i)->iStrLen), 0)); TextOut(hdcMem, (ccChain+i)->x, j-=iFontHeight, &((ccChain+i)->point->ch), 1); (ccChain+i)->point = (ccChain+i)->point->next; } if(ctn) (ccChain+i)->iStopTimes = 0 ; else continue; (ccChain+i)->y += iFontHeight; //下次开始显示的y坐标 为当前的y坐标加上 一个字符的高度 //如果开始显示的y坐标减去 整个显示列的长度超过了屏幕的高度 if( (ccChain+i)->y-(ccChain+i)->iStrLen*iFontHeight > cyScreen) { free( (ccChain+i)->current ); init(ccChain+i, cyScreen, (iFontWidth*3/2)*i); } //链表的头 为此链表的前个元素,因为下次开始显示的时候 就相当与在整个显示列的开头添加个元素,然后在开始往上显示 (ccChain+i)->head = (ccChain+i)->head->prev; (ccChain+i)->head->ch = randomChar(); } BitBlt(hdc, 0, 0, cxScreen, cyScreen, hdcMem, 0, 0, SRCCOPY); ReleaseDC(hwnd, hdc); return 0; case WM_RBUTTONDOWN: KillTimer (hwnd, ID_TIMER) ; return 0; case WM_RBUTTONUP: SetTimer (hwnd, ID_TIMER, 10, NULL) ; return 0; //处理善后工作 case WM_KEYDOWN: case WM_LBUTTONDOWN: case WM_DESTROY: KillTimer (hwnd, ID_TIMER) ; DeleteObject(hBitmap); DeleteDC(hdcMem); for(i=0; i<iColumnCount; i++) { free( (ccChain+i)->current ); } free(ccChain); PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } 修改bug
07-16
<think>我们正在处理Windows消息参数WPARAM和LPARAM到CString的转换。注意,这两个参数实际上是整数类型(在32位系统中是32位,64位系统中是64位),但它们在消息中可能代表不同的内容(如整数、指针、字符串指针等)。因此,转换方式取决于具体消息。 根据之前的讨论,我们需要根据消息类型来决定如何解释这两个参数。例如: - 对于某些消息,wParam可能是一个整数,而lParam可能是一个指向字符串的指针。 - 对于其他消息,wParam可能包含两个16位的整数,而lParam可能包含坐标。 因此,我们不能一概而论地将WPARAM和LPARAM转换为字符串,而必须根据具体的消息上下文。 但是,如果我们假设在特定的消息中,lParam或wParam是一个字符串指针(例如,在WM_SETTEXT消息中,lParam指向一个以null结尾的字符串),那么我们可以将其转换为CString。 下面分几种情况讨论: 1. 当参数是一个指向ANSI字符串(char*)的指针时: ```cpp // 假设lParam是LPCTSTR(在Unicode程序中是wchar_t*,在ANSI程序中是char*) // 但在现代编程中,通常使用Unicode,所以我们可以直接使用TCHAR字符串 CString str = (LPCTSTR)lParam; // 如果lParam指向字符串,并且程序使用Unicode,则要求lParam是wchar_t* ``` 2. 当参数是一个指向宽字符字符串(wchar_t*)的指针时,同上。 3. 如果参数是一个整数,我们可以将其格式化为字符串: ```cpp CString str; str.Format(_T("%d"), (int)wParam); // 或者%u, %x等 ``` 4. 如果参数是指向一个结构体的指针,我们需要根据结构体内容来构建字符串。 5. 对于自定义消息,我们需要知道发送方如何设置参数。 因此,关键是要知道当前处理的是什么消息。 示例:在WM_COMMAND消息中,wParam的低16位是控件ID,高16位是通知码,lParam是控件句柄。这时,如果我们想将wParam和lParam都转换为字符串,可以这样做: ```cpp case WM_COMMAND: { UINT id = LOWORD(wParam); UINT code = HIWORD(wParam); HWND hwndCtrl = (HWND)lParam; CString strWParam; strWParam.Format(_T("控件ID: %u, 通知码: %u"), id, code); CString strLParam; strLParam.Format(_T("%p"), hwndCtrl); // 将句柄格式化为指针形式(十六进制) // 现在strWParam和strLParam就是转换后的字符串 } ``` 另一个例子:处理WM_SETTEXT消息,lParam指向字符串: ```cpp case WM_SETTEXT: { CString strText = (LPCTSTR)lParam; // 直接转换 // 使用strText } ``` 注意:在转换指针指向的字符串时,必须确保指针是有效的。 如果消息中传递的是ANSI字符串(而不是Unicode),而我们的程序是Unicode的,那么我们需要进行转换: ```cpp case WM_SETTEXT: { // 假设消息发送者发送的是ANSI字符串(即非Unicode程序发送的) // 但WM_SETTEXT消息在Unicode程序中,lParam应该是Unicode字符串,所以这种情况通常不会发生。 // 如果确实需要处理ANSI字符串,我们可以这样: CStringA strAnsi((LPCSTR)lParam); // 先构造一个ANSI字符串 CString strUnicode(strAnsi); // 再转换为Unicode字符串(如果项目使用Unicode) } ``` 但是,在Windows编程中,我们通常使用TCHAR系列,所以为了通用,我们可以这样: ```cpp #ifdef UNICODE // 如果程序是Unicode,但消息发送的是ANSI字符串,我们需要转换 CString str = CString(CA2CT((LPCSTR)lParam)); #else CString str = (LPCSTR)lParam; #endif ``` 不过,这种需求很少见,因为WM_SETTEXT消息在发送时,系统会根据发送方和接收方的Unicode属性自动转换。所以,一般情况下,我们直接使用`(LPCTSTR)lParam`即可。 总结:将WPARAM和LPARAM转换为CString,需要根据消息类型来解析参数的含义,然后选择适当的转换方式。 以下是一个通用的转换函数示例,用于将任意参数转换为字符串(主要用于调试目的,显示其数值或指针内容): ```cpp CString WParamToString(WPARAM wParam) { CString str; str.Format(_T("%p"), (void*)wParam); // 以指针格式显示,即使它是整数 return str; } CString LParamToString(LPARAM lParam) { CString str; str.Format(_T("%p"), (void*)lParam); return str; } ``` 但请注意,这样转换只是将参数的值以十六进制形式输出(如果是32位程序,则显示8位十六进制;64位程序则显示16位十六进制),并不是将参数解释为字符串指针去获取字符串内容。 因此,具体问题具体分析。如果是为了调试,我们可以根据不同的消息写不同的转换函数。 下面提供一个更智能的转换函数,根据消息类型转换: ```cpp CString ConvertWParamToString(UINT message, WPARAM wParam) { CString str; switch (message) { case WM_COMMAND: str.Format(_T("控件ID: %d, 通知码: %d"), LOWORD(wParam), HIWORD(wParam)); break; case WM_KEYDOWN: case WM_SYSKEYDOWN: str.Format(_T("虚拟键码: %d"), (int)wParam); break; // 其他消息... default: str.Format(_T("0x%p"), (void*)wParam); } return str; } CString ConvertLParamToString(UINT message, LPARAM lParam) { CString str; switch (message) { case WM_COMMAND: str.Format(_T("控件句柄: %p"), (void*)lParam); break; case WM_LBUTTONDOWN: case WM_MOUSEMOVE: str.Format(_T("X=%d, Y=%d"), GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); break; case WM_SETTEXT: str = (LPCTSTR)lParam; // 直接当作字符串 break; // 其他消息... default: str.Format(_T("0x%p"), (void*)lParam); } return str; } ``` 注意:在WM_SETTEXT的处理中,我们直接将lParam当作字符串指针,这仅当该消息被处理时有效,如果用于其他消息且lParam不是字符串指针,则会导致非法访问。 因此,在实际使用时,我们需要确保在正确的消息下使用正确的转换。 最后,回答用户的问题:如何将WPARAM和LPARAM转换为CString?答案是:根据消息类型进行转换,没有通用的方法,但可以针对不同消息编写转换逻辑。如果只是需要显示其数值,可以格式化为十六进制字符串。 下面是一个简单的示例,将参数格式化为十六进制字符串(不考虑消息类型): ```cpp CString strWParam, strLParam; strWParam.Format(_T("0x%p"), (void*)wParam); strLParam.Format(_T("0x%p"), (void*)lParam); ``` 这样转换是安全的,因为它只是将参数的值以指针形式输出,不会访问指针指向的内存。 所以,根据用户需求,如果只是需要数值的字符串表示,可以使用上面的方法。如果用户需要根据消息类型解析出有意义的字符串,则需要针对具体消息编写代码。</think>在 Windows 消息处理中将 `WPARAM` 和 `LPARAM` 转换为 `CString` 需要根据消息类型和参数含义进行安全转换。以下是详细解决方案: ### 1. **数值类型参数的转换** 当参数表示数值(如控件 ID、虚拟键码等)时: ```cpp // 示例:处理 WM_COMMAND 消息 case WM_COMMAND: { UINT controlID = LOWORD(wParam); // 提取控件 ID UINT notificationCode = HIWORD(wParam); // 提取通知码 CString strParam; strParam.Format(_T("控件 ID: %u, 通知码: %u"), controlID, notificationCode); // 使用 strParam... break; } ``` ### 2. **字符串指针参数的转换** 当参数包含字符串指针(如 `WM_SETTEXT`)时: ```cpp // 示例:处理 WM_SETTEXT 消息 case WM_SETTEXT: { // 安全转换:将 LPARAM 解释为 LPCTSTR LPCTSTR pszText = reinterpret_cast<LPCTSTR>(lParam); CString strText(pszText); // 直接构造 CString // 使用 strText... break; } ``` ### 3. **结构化数据的转换** 当参数指向结构体(如 `WM_NOTIFY`)时: ```cpp // 示例:处理 WM_NOTIFY 消息 case WM_NOTIFY: { NMHDR* pNmHdr = reinterpret_cast<NMHDR*>(lParam); if (pNmHdr) { CString strInfo; strInfo.Format(_T("控件 ID: %d, 通知码: %d"), pNmHdr->idFrom, pNmHdr->code); // 使用 strInfo... } break; } ``` ### 4. **通用安全转换方法** ```cpp // 数值转字符串(适用于整数参数) CString FormatParamAsNumber(LPARAM lParam) { CString str; str.Format(_T("%lld"), static_cast<LONGLONG>(lParam)); return str; } // 指针转字符串(适用于字符串/结构体指针) CString FormatParamAsString(LPARAM lParam) { if (IsBadReadPtr(reinterpret_cast<LPCVOID>(lParam), 1)) { return _T("(无效指针)"); } return CString(reinterpret_cast<LPCTSTR>(lParam)); } ``` ### 5. **消息参数转换参考表 | 消息类型 | wParam 转换方式 | lParam 转换方式 | |----------------|-------------------------------------|-------------------------------------| | `WM_COMMAND` | `LOWORD()`/`HIWORD()` 提取数值 | 控件句柄 (`%p` 格式) | | `WM_SETTEXT` | 未使用 | `CString(lParam)` 直接转换字符串 | | `WM_NOTIFY` | 未使用 | `reinterpret_cast<NMHDR*>` 解析结构 | | `WM_KEYDOWN` | `LOWORD()` 提取虚拟键码 | 按键状态位掩码 | | `WM_SIZE` | 调整大小类型 | `LOWORD()`/`HIWORD()` 提取宽高 | ### 关键注意事项 1. **类型安全** ```cpp // 避免 C 风格强制转换 CString strBad = (LPCTSTR)lParam; // ✘ 危险 CString strGood = reinterpret_cast<LPCTSTR>(lParam); // ✔ 安全 ``` 2. **Unicode 兼容** ```cpp #ifdef UNICODE #define PARAM_TO_STR(lp) CString(reinterpret_cast<LPCWSTR>(lp)) #else #define PARAM_TO_STR(lp) CString(reinterpret_cast<LPCSTR>(lp)) #endif ``` 3. **指针有效性验证** ```cpp if (!IsBadReadPtr(reinterpret_cast<void*>(lParam), sizeof(TCHAR))) { CString str(reinterpret_cast<LPCTSTR>(lParam)); } ``` ### 最佳实践 ```cpp void LogMessageParams(UINT msg, WPARAM wp, LPARAM lp) { CString strMsg, strWParam, strLParam; // 根据消息类型选择转换方式 switch(msg) { case WM_SETTEXT: strWParam = FormatParamAsNumber(wp); strLParam = FormatParamAsString(lp); break; case WM_COMMAND: strWParam.Format(_T("ID:%u, Code:%u"), LOWORD(wp), HIWORD(wp)); strLParam.Format(_T("%p"), reinterpret_cast<void*>(lp)); break; default: strWParam.Format(_T("0x%llX"), static_cast<ULONGLONG>(wp)); strLParam.Format(_T("0x%llX"), static_cast<ULONGLONG>(lp)); } TRACE(_T("消息 0x%04X: wParam=%s, lParam=%s\n"), msg, strWParam, strLParam); } ``` ### 相关问题 1. 如何在 Windows 消息处理中安全地转换不同类型的数据结构? 2. 处理跨线程消息时如何确保 `LPARAM` 指针的生命周期安全? 3. Unicode 和 ANSI 版本程序在消息参数处理上有哪些差异? 4. 如何为自定义 Windows 消息设计类型安全的参数传递机制? 5. Windows 消息调试中,有哪些高效记录消息参数的方法?[^1] [^1]: 关于字符串类型转换的安全性考虑,参考 CString 与指针类型的转换规范
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值