User defined message handling
Derek F. Groft - UTL -- groftde@ebs.ac.com Monday, September 09, 1996 Environment: VC++ 4.2, NT 3.51 I am writing an extension dll that has a class, CMyListCtrl derived from CListCtrl, in which I implement some custom functionality. I am looking for a generic way to call a user defined callback. When a certain condition occurs in a CMyListCtrl object, I would like to, for example, send a WM_MY_MESSAGE message to the control which would, in turn, cause a user's function to be called. To the user, I am hoping that it looks like a normal message, for example NM_KILLFOCUS, was sent. What I am looking for is something that looks like ON_NOTIFY in the message map: ON_NOTIFY( WM_MY_MESSAGE, IDC_Control, OnMyMessage) This would allow the user to set up his own callback for each CMyListCtrl object on his Dialog. I have toyed with ON_MESSAGE( WM_MY_MESSAGE, OnMyMessage) however, the user will have to query something to figure out which list control had this event. It seems like I should be able to set something up in the Message map, but I can't figure out how. Any ideas?? Thanks in advance, Derek
Isaac Katzenelson -- isaac_k@goldnet.net.il Tuesday, September 10, 1996 Derek F. Groft - UTL wrote: > > Environment: VC++ 4.2, NT 3.51 > > I am writing an extension dll that has a class, CMyListCtrl derived from > CListCtrl, in which I implement some custom functionality. > > I am looking for a generic way to call a user defined callback. When a certain > condition occurs in a CMyListCtrl object, I would like to, for example, > send a WM_MY_MESSAGE message to the control which would, in turn, cause > a user's function to be called. To the user, I am hoping that it looks > like a normal message, for example NM_KILLFOCUS, was sent. > > What I am looking for is something that looks like ON_NOTIFY in the message > map: > ON_NOTIFY( WM_MY_MESSAGE, IDC_Control, OnMyMessage) > > This would allow the user to set up his own callback for each CMyListCtrl > object on his Dialog. I have toyed with > > ON_MESSAGE( WM_MY_MESSAGE, OnMyMessage) > > however, the user will have to query something to figure out which list > control had this event. It seems like I should be able to set something > up in the Message map, but I can't figure out how. > > Any ideas?? > > Thanks in advance, > Derek ***************************************************************** Hi Derek I'll give you a sample from one of my projects. #define WM_COMM_CB WM_USER+10 IMPLEMENT_DYNCREATE(CVideoconView, CView) BEGIN_MESSAGE_MAP(CVideoconView, CView) ON_MESSAGE(WM_COMM_CB, OnCommCallBack) END_MESSAGE_MAP() LRESULT CVideoconView::OnCommCallBack(WPARAM wParam, LPARAM lParam) { } DO NOT FORGET to define afx_msg LRESULT OnCommCallBack(WPARAM wParam, LPARAM lParam); in your class. Isaac.
Roger Onslow/Newcastle/Computer Systems Australia/ Wednesday, September 11, 1996 [Mini-digest: 3 responses] >What I am looking for is something that looks like >ON_NOTIFY in the message map: > ON_NOTIFY( WM_MY_MESSAGE, IDC_Control, OnMyMessage) Use a registered windows message. I have done exactly this for my CListCtrl derived class. NOTE: You could also try WM_USER messages, but you have to be careful that they aren't used internally in MFC or clash with some other message. Registered windows messages are unique and cannot clash (especially if you give them an obviously unique string id); Here is an extract from my code: // ListCtrl.h : header file // extern const UINT WM_QLISTVIEWSORT; // my registered message class QListCtrl : public CListCtrl { ... public: //{{AFX_MSG(QListCtrl) ... afx_msg LRESULT OnSort(WPARAM wparam, LPARAM lparam); //}}AFX_MSG DECLARE_MESSAGE_MAP() }; // ListCtrl.cpp : implementation file // ... // declare my registered message with name QLISTCTRLSORT and ID WM_QLISTCTRLSORT. const UINT WM_QLISTCTRLSORT = ::RegisterWindowMessage("QLISTCTRLSORT"); BEGIN_MESSAGE_MAP(QListCtrl, CListCtrl) //{{AFX_MSG_MAP(QListCtrl) ,,, ON_REGISTERED_MESSAGE(WM_QLISTCTRLSORT, OnSort) //}}AFX_MSG_MAP END_MESSAGE_MAP() ... LRESULT QListCtrl::OnSort(WPARAM /*wparam*/, LPARAM /*lparam*/) { if (GetSortColumn() >= 0) { c_this = this; SortItems(CompareFuncCallback,0); } return 0; } Hope this helps you Roger Onslow -----From: Roger Onslow/Newcastle/Computer Systems Australia/AU Isaac wrote: >I'll give you a sample from one of my projects. > >#define WM_COMM_CB WM_USER+10 > >IMPLEMENT_DYNCREATE(CVideoconView, CView) > >BEGIN_MESSAGE_MAP(CVideoconView, CView) > ON_MESSAGE(WM_COMM_CB, OnCommCallBack) >END_MESSAGE_MAP() > >LRESULT CVideoconView::OnCommCallBack(WPARAM wParam, LPARAM lParam) >{ >} > >DO NOT FORGET to define > afx_msg LRESULT OnCommCallBack(WPARAM wParam, LPARAM lParam); >in your class. > >Isaac. WM_USER+x message can be dangerous because they can be used by MFC or the system or OCX's etc. Here's what Win32 KB says: When an application subclasses a predefined Windows control or provides a special message in its dialog box procedure, it cannot use a WM_USER+x message to define a new message because the predefined controls use some WM_USER+x messages internally. It was necessary to use the RegisterWindowMessage function to retrieve a unique message number between 0xC000 and 0xFFFF. So you can use RegisterWindowMessage to get around this. -OR- use WM_APP+x instead Here's what Win32 KB (same article) says about WM_APP: In the Microsoft Windows environment, an application can define a private message for its own use without calling the RegisterWindowMessage API. Message numbers between 0x8000 and 0xBFFF are reserved for this purpose. For Windows NT and Windows 95, the system defines a new message WM_APP (value 0x8000). Applications can make use of the range WM_APP through 0xBFFF for private messages without conflict. The only requirement is that the .EXE file must be marked version 4.0 (use the linker switch /subsystem:windows,4.0). Windows NT 3.5 and 3.51 and Windows 95 will run applications marked version 4.0. Either of these solutions is probably preferrable to WM_USER. Roger Onslow -----From: groftde@ebs.ac.com (Derek F. Groft - UTL) Either my original message was misinterpreted or I am unclear as to why the solutions offered solve my problem. Let me try to re-phrase. The problem I am having is not just getting a user defined message to call a callback. That I can do with ON_MESSAGE or ON_REGISTERED_MESSAGE The problem I have is the case where more than one of the same control is on the same dialog. I need the message to have information regarding WHICH control sent my user defined message. Consider NM_KILLFOCUS. If a user wants to catch this message for two separate controls, he can because MFC generates two separate items in the message map, ON_NOTIFY( NM_KILLFOCUS, IDC_CONTROL_1, OnFunction_1) ON_NOTIFY( NM_KILLFOCUS, IDC_CONTROL_2, OnFunction_2) Even with buttons, the entries in the message map are unique for each button, as: ON_BN_CLICKED( IDC_BUTTON_1, OnClickFunc1) ON_BN_CLICKED( IDC_BUTTON_2, OnClickFunc2) I would like to send a message WM_MY_MESSAGE from within one of my controls and I want the user to be able to have a separate callback for each ListCtrl on the dialog, as in: ON_NOTIFY( WM_MY_MESSAGE, IDC_LISTCTRL_1, Func1) ON_NOTIFY( WM_MY_MESSAGE, IDC_LISTCTRL_2, Func2) I have, in fact, implemented my solution by sending the id of the control along with WM_MY_MESSAGE so that my users can just check the id as in if ( (UINT)LPARAM == IDC_LISTCTRL_1 ) { ... } else if ( (UINT)LPARAM == IDC_LISTCTRL_2 ) { ... } This solution allows me to use ON_MESSAGE(), though it makes it a bit more complex for the users of my DLL. When I asked this question, I hoping that I could implement it more cleanly like ON_BN_CLICKED or ON_NOTIFY. Thanks again for your help Derek
Kelly Capelli -- capelli@waste.org Wednesday, September 11, 1996 [Mini-digest: 4 responses] Hi Derek.. I don't think it's possible to do what you're suggesting with the message map. The message map entries are basically building a complex structure which holds function pointers for the various windows messages, notification codes, etc. Because this message map structure and what the framework does with the structure aren't extensible, you can't alter the signature of your user message in the message map like you want to. Below are the ON_NOTIFY and ON_REGISTERED_MESSAGE macros (from VC++ 4.1). Note that the ON_NOTIFY macro is always tied to a WM_NOTIFY message. If Windows allowed you to add additional notification codes for the WM_NOTIFY message (which you can't, to my knowledge), you could actually use the message map to set up your callback. ----------- #define ON_REGISTERED_MESSAGE(nMessageVariable, memberFxn) \ { 0xC000, 0, 0, 0, (UINT)(UINT*)(&nMessageVariable), \ (AFX_PMSG)(AFX_PMSGW)(LRESULT(AFX_MSG_CALL CWnd::*)(WPARAM, \ LPARAM))memberFxn}, #define ON_NOTIFY(wNotifyCode, id, memberFxn) \ { WM_NOTIFY,(WORD)(int)wNotifyCode, (WORD)id, (WORD)id, AfxSig_vNMHDRpl, \ (AFX_PMSG)(void (AFX_MSG_CALL CCmdTarget::*)(NMHDR*,LRESULT*))memberFxn }, Good luck.. -Angela -----From: Kostya SebovYou should use neither WM_USER+ nor registered message to acieve your goal. Use instead WM_COMMAND or WM_NOTIFY message with custom NOTIFICATION CODE (custom means here definef by yourself like you did with WM_USER+ message ID). This will let you use ON_CONTROL or ON_NOTIFY macro respectively in your dialog's message map. Be sure to mimick all the other parts of WPARAM/LPARAM not to confuse the system or MFC message architecture. Hope this'll help. --- Kostya Sebov. ---------------------------------------------------------------------------- Tel: (38 044) 266-6387 | Fax: (38 044) 266-6195 | E-mail: sebov@is.kiev.ua -----From: Pradeep Tapadiya If you writing 32 bit application, you can also use WM_APP+xxx for private messages. Look at KB article Q86835. Here is an extract: For Windows NT and Windows 95, the system defines a new message WM_APP (value 0x8000). Applications can make use of the range WM_APP through 0xBFFF for private messages without conflict. The only requirement is that the .EXE file must be marked version 4.0 (use the linker switch /subsystem:windows,4.0). Windows NT 3.5 and 3.51 and Windows 95 will run applications marked version 4.0. Pradeep pradeep@nuview.com -----From: Pradeep Tapadiya Have you looked at ON_NOTIFY_RANGE? If the id's of the controls are contiguous, you can declare ON_NOTIFY_RANGE(NM_MY_NOTIFICATION, idFirst, idLast, myFunc) where myFunc has a prototype afx_msg BOOL myFunc(UINT id, NMHDR* pNotifyStruct, LRESULT* pResult); Here, id is the id of the child control that sent the notification. To get notification For all possible child ids, one can declare ON_NOTIFY_RANGE(NM_MY_NOTIFICATION, 0, 0xffff, myFunc) Hope this helps Pradeep pradeep@nuview.com
| Вернуться в корень Архива |