15 мая 2023 года "Исходники.РУ" отмечают своё 23-летие!
Поздравляем всех причастных и неравнодушных с этим событием!
И огромное спасибо всем, кто был и остаётся с нами все эти годы!

Главная Форум Журнал Wiki DRKB Discuz!ML Помощь проекту


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 Sebov 

You 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






| Вернуться в корень Архива |