1. 程式人生 > >windows程式設計讀書筆記三

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) ;