我想我知道为什么CAD软件中,当鼠标移动很快时,图像会跟不上了。
鼠标移经窗口的客户区时,Windows系统不会为鼠标经过的每个象素位置都产生WM_MOUSEMOVE消息。程序收到的WM_MOUSEMOVE消息个数取决于鼠标硬件和窗口过程处理鼠标移动的速度。换言之,如果消息队列里还未有处理WM_MOUSEMOVE消息,Windows就不会重复向消息队列中添加该消息。
鼠标的捕获如何实现?
让我想起了以前写运控程序时的 每个轴的点动实现。比如按下X+按钮时,X轴运动,松开X+按钮时,X轴停止。当时实现的方法是重写PreTranslateMessage函数。不过关于PreTranslateMessage的内部机制还没搞清。
1、 毕竟 产生message的当前句柄应该是鼠标所在的位置的程序的。所以当鼠标离开按钮或窗口时,系统得到的鼠标消息 hwnd字段应该为NULL。为什么这么认为?假设有两个窗口,在第一个窗口上按下鼠标后,移动到第二个窗口上,第二个窗口并没有因为鼠标在它上面而被激活。(这时系统还是能得到鼠标消息的,但是没有程序响应了。)
2、如果hwnd字段为NULL,以下程序就无效,事实以下程序是可用的。
3、这是MFC CWnd的虚函数。WinApi里没有的。如何理解其工作机制先扔一边吧,看内部代码还没看出所以然。
BOOL CLeadTechMotionDlg::PreTranslateMessage(MSG* pMsg)
{
// TODO: 在此添加专用代码和/或调用基类
if (pMsg->message == WM_LBUTTONDOWN)
{
if (pMsg->hwnd == GetDlgItem(IDC_BUTTON_X_POSITIVE)->m_hWnd)
{
// 按钮按下 X+
DMC5480JogAxis(Axis_X, 3000 * nSpeedRate, 300000, 600000, 1);
}
else if (pMsg->hwnd == GetDlgItem(IDC_BUTTON_X_NEGATIVE)->m_hWnd)
{
// 按钮按下 X-
DMC5480JogAxis(Axis_X, 3000 * nSpeedRate, 300000, 600000, 0);
}
}
else if (pMsg->message == WM_LBUTTONUP)
{
if (pMsg->hwnd == GetDlgItem(IDC_BUTTON_X_POSITIVE)->m_hWnd)
{
// 按钮弹起 X+
DMC5480StopAxis(Axis_X);
}
else if (pMsg->hwnd == GetDlgItem(IDC_BUTTON_X_NEGATIVE)->m_hWnd)
{
// 按钮弹起 X-
DMC5480StopAxis(Axis_X);
}
}
return CDialogEx::PreTranslateMessage(pMsg);
}
那么在SDK环境中如何捕获鼠标呢?
SetCapture(hwnd);
调用该函数后,Windows会将所有鼠标消息发送给句柄为 hwnd 的窗口过程。鼠标消息总是以客户区消息的形式出现,即使鼠标位于窗口的非客户区。参数 lParam 仍然表示鼠标在客户区的位置。但是,当鼠标位于客户区外的左方或上方时,这些x和y坐标会出现负值。如果用户想释放鼠标了,则可以调用
ReleaseCapture();
在MFC CWnd 中也有同名的函数 SetCapture(); ReleaseCapture()。
在这我们注意到,鼠标消息里含有两个坐标1、消息产生位置相对于客户区坐标; 2、消息产生时相对屏幕的左边。
POINT point1, point2;
point1.x = LOWORD(pMsg->lParam);
point1.y = HIWORD(pMsg->lParam);
point2 = pMsg->pt;
关于消息
typedef struct tagMSG { // msg
HWND hwnd; //窗口句柄
UINT message; //消息常量标识符
WPARAM wParam; //32位消息的特定附加信息,具体表示什么处决于message
LPARAM lParam; //32位消息的特定附加信息,具体表示什么处决于message
DWORD time; //消息创建时的时间
POINT pt; //消息创建时的鼠标位置
} MSG;
hwnd 接收消息的32位窗口句柄。窗口可以是任何类型的屏幕对象,因为Win32能够维护大多数可视对象的句柄(窗口、对话框、按钮、编辑框等)。message 用于区别其他消息的常量值,这些常量可以是Windows单元中预定义的常量,也可以是自定义的常量。
wParam 通常是一个与消息有关的常量值,也可能是窗口或控件的句柄。
lParam 通常是一个指向内存中数据的指针。由于wParam,lParam和指针都是32位的,需要时可以强制类型转换。具体表示什么,与message相关,他们是事先定义好的。
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static HWND hwndButton[NUM] ;
static RECT rect ;
static TCHAR szTop[] = TEXT ("message wParam lParam"),
szUnd[] = TEXT ("_______ ______ ______"),
szFormat[] = TEXT ("%-16s%04X-%04X %04X-%04X"),
szBuffer[50] ;
static int cxChar, cyChar ;
HDC hdc ;
PAINTSTRUCT ps ;
int i ;
switch (message)
{
case WM_CREATE :
cxChar = LOWORD (GetDialogBaseUnits ()) ;
cyChar = HIWORD (GetDialogBaseUnits ()) ;
for (i = 0 ; i < NUM ; i++)
hwndButton[i] = CreateWindow ( TEXT("button"),
button[i].szText,
WS_CHILD | WS_VISIBLE | button[i].iStyle,
cxChar, cyChar * (1 + 2 * i),
20 * cxChar, 7 * cyChar / 4,
hwnd, (HMENU) i,
((LPCREATESTRUCT) lParam)->hInstance, NULL) ;
return 0 ;
case WM_SIZE :
return 0 ;
case WM_PAINT :
hdc = BeginPaint (hwnd, &ps) ;
EndPaint (hwnd, &ps) ;
return 0 ;
case WM_DRAWITEM :
case WM_COMMAND :
break ;
case WM_DESTROY :
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
上述代码中的 WM_CREATE, 是由主程序在 创建主窗口过程中创建的。此时message 的参数
wParam This parameter is not used. 参数无效。 lParam A pointer to a CREATESTRUCT structure that contains information about the window being created. 一个指向CREATESTRUCT结构类型的指针。CREATESTRUCT结构 定义了应用程序中窗口过程的初始化参数
typedef struct tagCREATESTRUCT {
LPVOID lpCreateParams;
HANDLE hInstance;
HMENU hMenu;
HWND hwndParent;
int cy;
int cx;
int y;
int x;
LONG style;
LPCSTR lpszName;
LPCSTR lpszClass;
DWORD dwExStyle;
} CREATESTRUCT;
参数
lpCreateParams
指向要用于创建窗口的数据。
hInstance
标识拥有新的窗口中的模块的模块实例句柄。
hMenu
标识要使用新窗口中的菜单。 如果子窗口,将包含整数 id。
hwndParent
将拥有新的窗口的窗口。 此成员是NULL如果新的窗口为顶级窗口。
cy
指定新的窗口的高度。
cx
指定新的窗口的宽度。
y
指定新的窗口的左上角的 y 坐标。 如果新的窗口是一个子窗口; 坐标是相对于父窗口否则坐标是相对于屏幕的原点。
x
指定新的窗口的左上角的 x 坐标。 如果新的窗口是一个子窗口; 坐标是相对于父窗口否则坐标是相对于屏幕的原点。
style
指定新的窗口样式。
lpszName
指向以 null 结尾的字符串,它指定新窗口的名称。
lpszClass
指向以 null 结尾的字符串,它指定新窗口的窗口类名称 ( WNDCLASS结构; 有关详细信息,请参阅Windows SDK)。
dwExStyle
指定扩展样式新窗口。
在创建子窗口时的第10个参数 hlnstance 与窗口相关联的模块实例的句柄。
这里就应该是父窗口的 实例的句柄。 那就需要提取lParam指向的结构里的 hInstance成员了
((LPCREATESTRUCT) lParam)->hInstance
其他操作办法
a、使用全局变量 hInst, 来让窗口过程获得 WinMain的实例句柄。 在WinMain函数中,只需要在主窗口产生之前,简单设置
static HINSTANCE hInst = hInstance;
hwndButton[i] = CreateWindow ( TEXT("button"),
button[i].szText,
WS_CHILD | WS_VISIBLE | button[i].iStyle,
cxChar, cyChar * (1 + 2 * i),
20 * cxChar, 7 * cyChar / 4,
hwnd, (HMENU) i,
hInst, NULL) ;
b、使用GetWindowLong来获得实例句柄。
GetWindowLong (hwnd, GWL_HINSTANCE)
hwndButton[i] = CreateWindow ( TEXT("button"),
button[i].szText,
WS_CHILD | WS_VISIBLE | button[i].iStyle,
cxChar, cyChar * (1 + 2 * i),
20 * cxChar, 7 * cyChar / 4,
hwnd, (HMENU) i,
(HINSTANCE) GetWindowLong (hwnd, GWL_HINSTANCE), NULL) ;