From: Joseph M. Newcomer on
You should not be sending Windows messages from another thread. This would be a serious
error in design. If you need to do something from another thread, you should use
PostMessage of a user-defined message. Note that in the PostMessage/SendMessage debate,
it is still erroneous to SendMessage a Windows message unless you truly know what you are
doing. From what I see here, there is no reason at all to send a WM_NOTIFY, so you should
be using a user-defined message.
joe


On Mon, 02 Jul 2007 01:31:13 -0700, Headache <rquirk(a)tandbergtv.com> wrote:

>I thought Windows might marshall the simple types too. I guess I've
>been working too long with COM. However, I tried this in a single
>process and still couldn't get it to work. This time I see the message
>in Spy++. Looking at the MFC source code it seems to frig WM_NOTIFY
>messages. Anyhow here is my source. I send the message in another
>thread (as I guess my handler is on the main thread which is blocked
>waiting on a SendMessage) and I'm new'ing the structure on the heap
>and not even deleting it. I don't get into the MainFrame OnClick
>handler even though it is in the message map. It makes me think I'm
>missing something simple. Don't ask about where I break out the code
>in OnCmdMsg - it was code I wrote while looking at the framework. Once
>I get this going I'll have a look at the SendMessageRemote suggestion.
>
>// MainFrm.cpp : implementation of the CMainFrame class
>//
>
>#include "stdafx.h"
>#include "WmNotify.h"
>
>#include "MainFrm.h"
>#include ".\mainfrm.h"
>
>#ifdef _DEBUG
>#define new DEBUG_NEW
>#endif
>
>
>// CMainFrame
>
>IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)
>
>BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
> ON_WM_CREATE()
> ON_NOTIFY(NM_CLICK, 0, OnClick)
> ON_COMMAND(ID_EDIT_SENDMESSAGE, OnSendMessage)
>END_MESSAGE_MAP()
>
>static UINT indicators[] =
>{
> ID_SEPARATOR, // status line indicator
> ID_INDICATOR_CAPS,
> ID_INDICATOR_NUM,
> ID_INDICATOR_SCRL,
>};
>
>
>
>UINT __cdecl SendMessageToMainWnd(LPVOID pParam)
>{
> NMITEMACTIVATE *pNMIA = new NMITEMACTIVATE;
*****
Because you are using 'SendMessage' there is no need to use 'new'. Note also that you are
missing the corresponding 'delete'. So you have a memory leak.

Presumably this is a static method, so it is usually good practice to add the 'static'
keyword to it as a comment, e.g.,
/* static */ UINT CDECL SendMessageToMainWnd(LPVOID pParam)

If it is not static, there would be a serious problem.
*****
> ::ZeroMemory(pNMIA, sizeof(NMITEMACTIVATE));
> // Set up header
> pNMIA->hdr.code = NM_CLICK;
> pNMIA->hdr.hwndFrom = ::AfxGetMainWnd()->GetSafeHwnd();
> pNMIA->hdr.idFrom = 426;
> // Setup item and subitem
> pNMIA->iItem = 1;
> pNMIA->iSubItem = 2;
>
> //DWORD dwThreadID = (DWORD)pParam;
> ::AfxGetMainWnd()->SendMessage(WM_NOTIFY, pNMIA->hdr.idFrom,
>(LPARAM)pNMIA);
>
> return 0;
>}
>
>// CMainFrame construction/destruction
>
>CMainFrame::CMainFrame()
>{
> // TODO: add member initialization code here
>}
>
>CMainFrame::~CMainFrame()
>{
>}
>
>
>
>int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
>{
> if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
> return -1;
>
> if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE
>| CBRS_TOP
> | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
> !m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
> {
> TRACE0("Failed to create toolbar\n");
> return -1; // fail to create
> }
>
> if (!m_wndStatusBar.Create(this) ||
> !m_wndStatusBar.SetIndicators(indicators,
> sizeof(indicators)/sizeof(UINT)))
> {
> TRACE0("Failed to create status bar\n");
> return -1; // fail to create
> }
> // TODO: Delete these three lines if you don't want the toolbar to be
>dockable
> m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
> EnableDocking(CBRS_ALIGN_ANY);
> DockControlBar(&m_wndToolBar);
>
> return 0;
>}
>
>BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
>{
> if( !CFrameWnd::PreCreateWindow(cs) )
> return FALSE;
> // TODO: Modify the Window class or styles here by modifying
> // the CREATESTRUCT cs
>
> return TRUE;
>}
>
>
>// CMainFrame diagnostics
>
>#ifdef _DEBUG
>void CMainFrame::AssertValid() const
>{
> CFrameWnd::AssertValid();
>}
>
>void CMainFrame::Dump(CDumpContext& dc) const
>{
> CFrameWnd::Dump(dc);
>}
>
>#endif //_DEBUG
>
>
>// CMainFrame message handlers
>void CMainFrame::OnClick(NMHDR *pNMHDR, LRESULT* pResult)
*****
Is the control in question a child control of the mainframe? If it isn't, this code is in
the wrong place and should be moved.
*****
>{
> ::AfxMessageBox(_T("Hurrah!!"));
> ASSERT(NM_CLICK == pNMHDR->code);
>
> NMITEMACTIVATE *pNMIA = (NMITEMACTIVATE *)pNMHDR;
>
> *pResult = 1;
>}
>
>
>void CMainFrame::OnSendMessage()
>{
> ::AfxBeginThread(SendMessageToMainWnd, (LPVOID)::AfxGetApp()-
>>m_nThreadID);
>}
>
>BOOL CMainFrame::OnCmdMsg(UINT nID, int nCode, void* pExtra,
>AFX_CMDHANDLERINFO* pHandlerInfo)
****
I presume that this is here just for debugging purposes
****
>{
> // TODO: Add your specialized code here and/or call the base class
> if ( (HIWORD(nCode) == WM_NOTIFY) && (LOWORD(nCode) == NM_CLICK) )
> {
> CString str;
> str.Format(_T("Msg = %d, nCode = %d\n"), HIWORD(nCode),
>LOWORD(nCode));
> TRACE(str);
> }
> return CFrameWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
>}
Joseph M. Newcomer [MVP]
email: newcomer(a)flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
From: Joseph M. Newcomer on
WHile it seems reasonable that this should be done, the truth is that it is not done. Life
would be a lot easier for all of us if it had been done.
joe

On Sun, 01 Jul 2007 20:31:04 GMT, "David Ching" <dc(a)remove-this.dcsoft.com> wrote:

>"Joseph M. Newcomer" <newcomer(a)flounder.com> wrote in message
>news:eb3g835kcr8bfllckal6ihe85m28da8gv2(a)4ax.com...
>> This was handled as a special case because of its importance, particularly
>> because a huge
>> number of programs depended on being able to get window captions. It is
>> one of the very,
>> very, very, very few messages that actually works this way.
>
>I repeat: It seems if they marshalled some messages they would marshall all
>of them.
>
>Who's to say that getting a foreign process's window caption is important
>but not the state of a toolbar button within that window? I need to do
>both, that's why I created SendMessageRemote(), but it shouldn't have been
>necessary.
>
>-- David
>
Joseph M. Newcomer [MVP]
email: newcomer(a)flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
From: Headache on
I make no apologies for the tattiness of my code I just wanted the
mechanism to work and tried lot's
of things out. Anyhow, I decided to redouble my efforts and have found
out exactly why this doesn't work.

Examining the call stack you can see that CWnd::OnNotify the first
parameter has value 0x000001aa.
This is the window's ID (426 decimal). In the next call to CMainFrame
this has morphed into an nID
parameter of 0x00003c38.

> mfc71d.dll!AfxFindMessageEntry(const AFX_MSGMAP_ENTRY * lpEntry=0x00426028, unsigned int nMsg=0x0000004e, unsigned int nCode=0x0000fffe, unsigned int nID=0x00003c38) Line 1659 C++
mfc71d.dll!CCmdTarget::OnCmdMsg(unsigned int nID=0x00003c38, int
nCode=0x0000fffe, void * pExtra=0x0013fb74, AFX_CMDHANDLERINFO *
pHandlerInfo=0x00000000) Line 383 + 0x18 C++
mfc71d.dll!CFrameWnd::OnCmdMsg(unsigned int nID=0x00003c38, int
nCode=0x004efffe, void * pExtra=0x0013fb74, AFX_CMDHANDLERINFO *
pHandlerInfo=0x00000000) Line 897 + 0x18 C++
WmNotify.exe!CMainFrame::OnCmdMsg(unsigned int nID=0x00003c38, int
nCode=0x004efffe, void * pExtra=0x0013fb74, AFX_CMDHANDLERINFO *
pHandlerInfo=0x00000000) Line 170 C++
mfc71d.dll!CWnd::OnNotify(unsigned int __formal=0x000001aa, long
lParam=0x00037970, long * pResult=0x0013fc70) Line 2576 C++
mfc71d.dll!CWnd::OnWndMsg(unsigned int message=0x0000004e, unsigned
int wParam=0x000001aa, long lParam=0x00037970, long *
pResult=0x0013fca4) Line 1771 + 0x28 C++
mfc71d.dll!CWnd::WindowProc(unsigned int message=0x0000004e,
unsigned int wParam=0x000001aa, long lParam=0x00037970) Line 1745 +
0x1e C++


This is the reason why:

BOOL CWnd::OnNotify(WPARAM, LPARAM lParam, LRESULT* pResult)
{
ASSERT(pResult != NULL);
NMHDR* pNMHDR = (NMHDR*)lParam;
HWND hWndCtrl = pNMHDR->hwndFrom;

// get the child ID from the window itself
UINT_PTR nID = _AfxGetDlgCtrlID(hWndCtrl);

OnNotify ignores the NMHDR.idFrom field and instead recalculates it
from _AfxGetDlgCtrlID(hWndCtrl).
Now the hWndFrom that I passed was from my MainFrame window so the ID
becomes that of my MainFrame
window and not the control.

I think some of you hinted at this. The HWND and ID should both tie
up.

Breaking out a WM_NOTIFY message in the command map looks like this:

BOOL CMainFrame::OnCmdMsg(UINT nID, int nCode, void* pExtra,
AFX_CMDHANDLERINFO* pHandlerInfo)
{
TRACE(_T("HIWORD(nCode) = %x\n"), HIWORD(nCode));
TRACE(_T("LOWORD(nCode) = %x\n"), LOWORD(nCode));
TRACE(_T("WM_NOTIFY = %x\n"), WM_NOTIFY);
TRACE(_T("NM_CLICK = %x\n"), NM_CLICK);
// TODO: Add your specialized code here and/or call the base class
if ( (HIWORD(nCode) == WM_NOTIFY) && (LOWORD(nCode) ==
LOWORD(NM_CLICK)) )
{
CString str;
str.Format(_T("nID = %ld, nCode = %ld\n"), nID, nCode);
TRACE(str);
}
return CFrameWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}

You have to do LOWORD(nCode) == LOWORD(NM_CLICK) because
only the low word of NM_CLICK (defined as 0xFFFFFFFE) is placed into
nCode.

As an aside it appears impossible to write a program where the
mainframe notifies itself
(*VIA STATIC MAP ENTRIES*) as the Mainframe's ID is decided at run-
time.

Well, I said I was trying something arcane out. Now to get back to
looking at RemoteSendMessage API or whatever
it's called.