ON_COMMAND_RANGE 多個按鈕響應一個函數

來源:互聯網
上載者:User

很久沒來寫部落格了,都收到官方的寄件提醒再不寫就不是專家會員,沒辦法只能再寫一篇完成任務。

今天主要介紹一下ON_COMMAND_RANGE的用法

第一次用這個方法還是剛畢業那會,那時寫過一個控製程序,介面上有很多電器的控制按鈕,這些按鈕的響應函數基本一致,只是相應的ID值不一樣,要是一一寫響應函數那不累死人,於是就東找西找,找到ON_COMMAND_RANGE。

最近一個偶然機會也要用到它,三下五除二,CODE寫完了,

1.在要添加的工程上添加函數afx_msg void OnButtonPort();

2.在

BEGIN_MESSAGE_MAP(CXXXDlg, CDialog)
//{{AFX_MSG_MAP(CXXXDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_TIMER()
//}}AFX_MSG_MAP

//這裡的IDC_BUTTON_PORT_1和 IDC_BUTTON_START_ALL之間有很多個Button,並且ID連續
ON_COMMAND_RANGE(IDC_BUTTON_PORT_1, IDC_BUTTON_START_ALL, OnButtonPort)
ON_WM_DEVICECHANGE()
END_MESSAGE_MAP()

3.寫你自己的響應函數 void CXXXDlg::OnButtonPort()

註:此代碼DEBUG OK,Relase異常,不可直接參考,且聽下面分解:

DEBUG通過,不料Release卻直接崩潰,寫了這麼多年的CODE還真第一次遇到這種情況,為什麼ON_COMMAND_RANGE Debug正常,Release不正常呢?

先MSDN:

Use this macro to map a contiguous range of command IDs to a single message handler function.

ON_COMMAND_RANGE(id1, id2, memberFxn )
Parameters
id1
Command ID at the beginning of a contiguous range of command IDs.
id2
Command ID at the end of a contiguous range of command IDs.
memberFxn
The name of the message-handler function to which the commands are mapped.
Remarks
The range of IDs starts with id1 and ends with id2.
Use ON_COMMAND_RANGE to map a range of command IDs to one member function. Use ON_COMMAND to map a single command to a member function. Only one message-map entry can match a given command ID. That is, you can't map a command to more than one handler. For more information on mapping message ranges, see Handlers for Message-Map Ranges.
There is no automatic support for message map ranges, so you must place the macro yourself.

MSDN也沒有特別說明要注意什麼的,我覺得我用的也很正常,於是在網上又搜了一大會,有一個網友非常專業的解釋的原因,這裡收藏了:http://yiyunscu.blog.163.com/blog/static/3626332020099802057982/

以下是轉載內容:

該網友定義台下:

afx_msg void OnCommandMy(WPARAM wParam, LPARAM lParam );

申明只適用於ON_COMMAND訊息的函數申明, 而ON_COMMAND_RANGE的函數申明在MSDN中建議寫成這樣:
OnCommandMy(UINT nID);
通過switch(nID) case **:進行針對不同菜單進行訊息響應.
nID就是菜單傳入訊息的ID號, 奇怪的是, 在Debug版本下, 先前的申明方式運行完全正常, 查閱了MSDN, 找出了可能的原因:

Handler functions for single commands normally take no parameters. With the exception of update handler functions, handler functions for message-map ranges require an extra parameter, nID, of type UINT. This parameter is the first parameter. The extra parameter accommodates the extra command ID needed to specify which command the user actually chose.

針對單個Command訊息響應函數可以不帶參數, 但是對於多個Command訊息如ON_COMMAND_RANGE申明的訊息響應需要將函數參數列表中的第一個參數定義為UINT nID, 指明command 的ID號, 按照MSDN的理解, ON_COMMAND_RANGE也可以像ON_COMMAND那樣在訊息響應函數中定義兩個參數, 如afx_msg void OnCommandMy(WPARAM wParam, LPARAM lParam );在Debug和Release下, 編譯不會出現問題, 在Debug下運行也不會出現問題, 但是在Release下面卻出現記憶體錯誤, 所以可以帶多個參數感覺只能在Debug下可以行的能, 在Release下就沒失效了.

今天又花了整整一天上網查閱相關的資料並利用VC查看相應的彙編代碼發現, 應該是函數調用和返回時棧操作不平衡導致Release版本下出現了記憶體錯誤的問題, ON_COMMAND_RANGE在MFC預設的訊息響應函數中, 參數只有一個, 如:
#define ON_COMMAND_RANGE(id, idLast, memberFxn) \
 { WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)idLast, AfxSig_vw, \
  (AFX_PMSG)(void (AFX_MSG_CALL CCmdTarget::*)(UINT))&memberFxn },
  // ON_COMMAND_RANGE(id, idLast, OnFoo) is the same as
  //   ON_CONTROL_RANGE(0, id, idLast, OnFoo)

函數調用過程中, 會將傳入的參數進行壓棧操作, 因為MFC預設的傳入參數只有一個, 因此調用OnCommandMy時會有系統傳入的一個訊息參數進行壓棧操作. 在函數返回時, 應該進行出棧操作, 並且保證調用完成後棧維持平衡, 否則會出現可能的記憶體錯誤.

在DEBUG上沒有出現記憶體錯誤在於在調用OnCommandMy函數返回時編譯器在傳回碼處添加了如下的彙編代碼:

pop edi
       pop esi
       pop  ebx
       add esp, 48h
       cmp ebp, esp
       call __chkesp (0041e680)
       mov esp, ebp
       pop ebp
       ret 8(兩個參數出棧)

此彙編代碼的作用就是在函數返回時檢查調用中和調用返回時的棧是否一致, 如果不一致, 就強制平棧操作, 因為在這個調用過程中, 傳入OnCommandMy的訊息參數只有一個(只是申明成兩個, 實際只有一個參數傳入), 所以存在棧不一致的情況, 但是強制平棧可以避免由此引起的錯誤.

在Release版本下, 就沒有了檢測棧的操作,

只是簡單的下面幾句彙編程式碼完成出棧操作:

mov esp, ebp
       pop ebp
       ret 8兩個參數出棧)

可以明顯看到, Release下出現了棧操作不平衡的情況, 即入棧數小於出棧數, 從而導致棧區地址錯誤, 當其它函數兩次對棧區進行地址訪問時就極有可能出現記憶體錯誤的現象了.

所以, 平時寫程式時在Debug下高度完成之後, 最好還在Release下看一下, 因為有些時候, Debug下對函數參數的檢查不是那麼嚴格, 並且在棧的操作上, Debug可以協助我們解決很多隱藏的問題, 但是Release下就不會了. 另外在自訂的訊息響應函數中, Debug和Release都不會對響應函數的參數列表與MFC預設參數列表進行一致性檢測, 從而可能隱藏重大的記憶體出錯的可能性, 導致最終軟體在Release下運行可能發生崩潰.

終於明白了,原來是ON_COMMAND_RANGE只能帶一個參數,帶兩個或不帶都會異常

所以重新定義

afx_msg void OnButtonPort(UINT nID);

而且此nID就是你點擊的按鈕ID值,再也不用之前的麻煩代碼了

CWnd *pWnd = GetFocus();
int nPortID = pWnd->GetDlgCtrlID() ;

問題解決!

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.