1. 程式人生 > >訊息中的wParam和lParam

訊息中的wParam和lParam

具體是這麼說:“在Win 3.x中,WPARAM是16位的,而LPARAM是32位的,兩者有明顯的區別。因為地址通常是32位的,所以LPARAM 被用來傳遞地址,這個習慣在Win32 API中仍然能夠看到。在Win32 API中,WPARAM和LPARAM都是32位,所以沒有什麼本質的區 別。


Windows的訊息必須參考幫助檔案才能知道具體的含義。如果是你定義的訊息,願意怎麼使這兩個引數都行。但是習慣上,我們願意使用LPARAM傳 遞地址,而WPARAM傳遞其他引數。”


在Win32API的早期,為了保證和Win16API的程式碼可移植性MS定義了WPARAM和LPARAM兩個巨集。 
當時保留了w字首的原因一方面是由於WPARAM巨集也已W開頭,還有也因為要提醒程式設計師注意到可移植性,當然到了現在Win16早已退出歷史舞臺,這個字首也就約定俗成的沿用下來了。 
例如:主程式MyDlg.cpp 
1.自定義訊息:#define WM_TRAY WM_USER 100 
2.函式原形:afx_msg LRESULT OnTrayNotify(WPARAM wParam,LPARAM lParam); 
3.訊息對映:ON_MESSAGE(WM_TRAY,OnTrayNotify) 
4.原函式: 
LRESULT CMyDlg::OnTrayNotify(WPARAM wParam,LPARAM lParam) 

return m_tray.OnTrayNotify(wParam,lParam); 
}


WPARAM常常代表一些控制元件的ID或者高位底位組合起來分別表示滑鼠的位置,如果訊息的傳送者需要將某種結構的指標或者是某種型別的控制代碼時,習慣上用LPARAM來傳遞,可以參考各種控制元件的通知訊息:可以檢視:EN_CHANGE (EDIT控制元件的一個通知訊息),CBEM_INSERTITEM(可擴充套件組合框的可接受訊息)等等來加以領會。 
理論上在使用自定義訊息時,WPARAM LPARAM的含義可以程式設計師任意指定的,但是最好遵從MFC中的習慣。在呼叫SendMessage()函式時,第二個引數是WPARAM,第三個引數是這個訊息的LPARAM,但是你在程式中某個類中寫下ON_MESSAGE()巨集來處理這個訊息時,處理函式SomeHandler(WPARAM,LPRAM(預設是0))中解釋這兩個引數時必須按照SendMessage呼叫中的意義來進行。 
訊息響應機制 
1、訊息的組成:一個訊息由一個訊息名稱(UINT),和兩個引數(WPARAM,LPARAM)。當用戶進行了輸入或是視窗的狀態發生改變時系統都會發送訊息到某一個視窗。例如當選單轉中之後會有WM_COMMAND訊息傳送,WPARAM的高字中(HIWORD(wParam))是命令的ID號,對選單來講就是選單ID。當然使用者也可以定義自己的訊息名稱,也可以利用自定義訊息來發送通知和傳送資料。 


2、誰將收到訊息:一個訊息必須由一個視窗接收。在視窗的過程(WNDPROC)中可以對訊息進行分析,對自己感興趣的訊息進行處理。例如你希望對選單選擇進行處理那麼你可以定義對WM_COMMAND進行處理的程式碼,如果希望在視窗中進行圖形輸出就必須對WM_PAINT進行處理。 


3、未處理的訊息到那裡去了:M$為視窗編寫了預設的視窗過程,這個視窗過程將負責處理那些你不處理訊息。正因為有了這個預設視窗過程我們才可以利用Windows的視窗進行開發而不必過多關注視窗各種訊息的處理。例如視窗在被拖動時會有很多訊息傳送,而我們都可以不予理睬讓系統自己去處理。 


4、視窗控制代碼:說到訊息就不能不說視窗控制代碼,系統通過視窗控制代碼來在整個系統中唯一標識一個視窗,傳送一個訊息時必須指定一個視窗控制代碼表明該訊息由那個視窗接收。而每個視窗都會有自己的視窗過程,所以使用者的輸入就會被正確的處理。例如有兩個視窗共用一個視窗過程程式碼,你在視窗一上按下滑鼠時訊息就會通過視窗一的控制代碼被髮送到視窗一而不是視窗二。


5、示例:下面有一段虛擬碼演示如何在視窗過程中處理訊息 


LONG yourWndProc(HWND hWnd,UINT uMessageType,WPARAM wP,LPARAM) 

switch(uMessageType) 
{//使用SWITCH語句將各種訊息分開 
case(WM_PAINT): 
doYourWindow(...);//在視窗需要重新繪製時進行輸出 
break; 
case(WM_LBUTTONDOWN): 
doYourWork(...);//在滑鼠左鍵被按下時進行處理 
break; 
default: 
callDefaultWndProc(...);//對於其它情況就讓系統自己處理 
break; 


接下來談談什麼是訊息機制:系統將會維護一個或多個訊息佇列,所有產生的訊息都回被放入或是插入佇列中。系統會在佇列中取出每一條訊息,根據訊息的接收控制代碼而將該訊息傳送給擁有該視窗的程式的訊息迴圈。每一個執行的程式都有自己的訊息迴圈,在迴圈中得到屬於自己的訊息並根據接收視窗的控制代碼呼叫相應的視窗過程。而在沒有訊息時訊息迴圈就將控制權交給系統所以Windows可以同時進行多個任務。下面的虛擬碼演示了訊息迴圈的用法: 


while(1) 

id=getMessage(...); 
if(id == quit) 
break; 
translateMessage(...); 

當該程式沒有訊息通知時getMessage就不會返回,也就不會佔用系統的CPU時間。


在Win32 SDK中訊息本身是作為一個結構體記錄傳遞給應用程式的,這個記錄中包含了訊息的型別以及其他資訊。這個記錄型別叫做MSG,它在window中是這樣宣告的:


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相關,他們是事先定義好的。


如果自定義訊息:#define WM_MYMESSAGE WM_USER+100,需確定wParam,lParam的意義 (假設wParam=0時傳送資料,wParam=1時接收資料,lParam為CMyClass* 指標,指向一個CMyClass物件,準備要傳送的資料或接收資料 傳送WM_MYMESSAGE時 SendMessage(hwnd,WM_MYMESSAGE,0,pMyClassObject) 接收訊息的視窗,接收WM_MYMESSAGE中(CMyClass*)lParam引數即pMyClassObject傳過來的資料


MFC資料型別WPARAM 視窗函式或callback函式的一個引數


 


實際上所有的訊息響應都有WPARAM和LPARAM的存在,只是有些訊息響應WPARAM和LPARAM沒有意義,所以在MFC封裝後有些固定的訊息響應函式看不到WPARAM和LPARAM,但依然可以通過GetCurrentMessage()取得當前的訊息來檢視WPARAM和LPARAM。


WPARAM //typedef UINT WPARAM;control identifier
       LPARAM //typedef LONG LPARAM;notification messages


Windows中的訊息由,訊息號,字引數(lParam),長字引數(wParam)組成。是包含有關訊息的附加資訊,隨不同的訊息有所不同。例如在滑鼠訊息中引數lParam 包含滑鼠游標座標,wParam包含一個指示各種虛鍵狀態的值(如按下滑鼠左鍵還是右鍵。。。).


從訊息引數中獲取字串和位置資訊存放到子串列埠類的成員函式中。字串m_string為什麼對應wParam,點m_point對應lParam
LRESULT CChildView::OnReceive(WPARAM wParam,LPARAM lParam)
{
   m_string = *((CSring *)wParam);
   m_point = *((CPoint *)lParam);
  Invalidate();
  return 0;


程式程式碼*在對話方塊中取出資料,並向其他視窗傳送訊息和資料,將資料指標作為一個引數傳送*/
void CTestDlg2::OnCommBtn()
{
     char szOut[30];
      GetDlgItemText(IDC_OUT,szOut,30);
      m_pParent->SendMessage(WM_DLG_NOTIFY,(WPARAM)szOut);
}


/*在訊息接收視窗中*/
/*對映訊息處理函式*/
ON_MESSAGE(WM_DLG_NOTIFY,OnDlgNotifyMsg)


/*在檢視中繪製出字串 m_szOut*/
void CMy53_s1View::OnDraw(CDC* pDC)
{
      CMy53_s1Doc* pDoc = GetDocument();
      ASSERT_VALID(pDoc);
     // TODO: add draw code for native data here
      pDC->TextOut(0,0,"Display String");
      pDC->TextOut(0,20,m_szOut);
}
/*處理通知訊息,儲存資訊並更新顯示*/
LONG CMy53_s1View::OnDlgNotifyMsg(WPARAM wP,LPARAM lP)
{
      m_szOut=(char*)wP;
      Invalidate();
     return 0;
}


一個字串的地址通過WPARAM來標識,再通過Windows訊息傳送出去;之後在訊息處理函式中WPARAM接受到的引數就是該地址,然後就可以對該地址進行操作了~~~


這是Windows訊息機制中經常用到的兩個data type,呵呵