windows程式設計讀書筆記三
我想我知道為什麼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) ;