Getting Messages from child Windows - mfc

I want to getting messages from child window. I am not able to do. Pls help me out.
I want a retrieve a message send by another child window.
Here user defined message is constant 510, and I am handling this inside OnGirish()
// WndSecond.cpp : implementation file
//
#include "stdafx.h"
#include "DemoB.h"
#include "WndSecond.h"
// #define ID_GIRISH 500
// CWndSecond
IMPLEMENT_DYNAMIC(CWndSecond, CWnd)
//Constructor
CWndSecond::CWndSecond()
{
}
//Destructor
CWndSecond::~CWndSecond()
{
}
//Message map for this window
BEGIN_MESSAGE_MAP(CWndSecond, CWnd)
ON_WM_PAINT()
ON_COMMAND(510,OnGirish)
END_MESSAGE_MAP()
// CWndSecond message handlers
BOOL CWndSecond::PreCreateWindow(CREATESTRUCT& cs)
{
if (!CWnd::PreCreateWindow(cs))
return FALSE;
cs.dwExStyle |= WS_EX_CLIENTEDGE;
cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS,
::LoadCursor(NULL, IDC_ARROW), CreateSolidBrush(RGB(0,255,0)), NULL);
return TRUE;
}
void CWndSecond::OnPaint()
{
CClientDC dc(this);
}
//Handler for retrieving user defined message sent by another window
void CWndSecond::OnGirish()
{
//Here I want to retrieve the message sent by another child window
GetMessae(510,0,0);
MessageBox(_T("Message"), _T("Hello World"),MB_ICONASTERISK|MB_OK);
}

RegisterWindowMessage is only required if you need to send inter-process messages, otherwise a constant of a value higher than WM_USER/WM_APP is fine. In an MFC application UI thread it's "never" right to call GetMessage() directly. The MFC framework handles the message queue itself and dispatches the messages to your windows. To send a message to a specific window use ::SendMessage or ::PostMessage as appropriate with the receivers window handle as the first argument, and use ON_MESSAGE in your message map. Prototype for handler LRESULT func(WPARAM,LPARAM);

You should use RegisterWindowMessage to define the message. And, the ON_REGISTERED_MESSAGE macro to handle it.

Related

How do message maps interface with the SendMessage() method?

Despite reading lots of MSDN articles, I can't seem to wrap my head around MFC's message maps and the SendMessage() function. Right now I have an IP control called IDC_IPADDRESS_MYADDRESS that I want to set the value of. I know that IPM_SETADDRESS is the correct message type, but I don't know how to successfully send the message and update the ip control's value.
What do I need to add to my message map,
BEGIN_MESSAGE_MAP(myDlg, CDialogEx)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTON1, &myDlg::OnBnClickedButton1)
END_MESSAGE_MAP()
And how do I correctly use that map entry to update the value of the ip address control? Below is my attempt at updating it with a SendMessage() call in the dialog init method.
// myDlgmessage handlers
BOOL myDlg::OnInitDialog()
{
myDlg::OnInitDialog();
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
//set default IP address
DWORD IP = MAKEIPADDRESS(192, 168, 0, 254);
SendMessage(IPM_SETADDRESS, 0, IP);
return TRUE; // return TRUE unless you set the focus to a control
}
SendMessage(IPM_SETADDRESS, 0, IP);
IPM_SETADDRESS is the correct message but it is being sent to the main dialog. The dialog is not looking for this message and ignores it.
You want to send the message to the IP control instead. That means you need the window handle for the ip address control:
CWnd *ptr_ip_address = GetDlgItem(IDC_IPADDRESS_MYADDRESS);
if (ptr_ip_address)
ptr_ip_address->SendMessage(IPM_SETADDRESS, 0, IP);
In MFC you can use CIPAddressCtrl class instead. You have to declare m_ip_address and subclass it with DoDataExchange. This class also has SetAddress method.
class CMyDialog : public CDialogEx
{
...
CIPAddressCtrl m_ip_address;
void DoDataExchange(CDataExchange* pDX);
};
void CMyDialog::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_IPADDRESS_MYADDRESS , m_ip_address);
}
BOOL myDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
m_ip_address.SetAddress(192, 168, 0, 254);
...
}
MFC message map is not directly related to your question. The message map is used to respond to windows messages. For example you want to respond to ON_BN_CLICKED. But here you are sending a message to the control, not receiving a message.
You can read more about this in books for WinAPI programming. In plain windows programming there is a "message loop" and a "windows procedure" where you can respond to messages. MFC uses message map to simplify this procedure.
BOOL myDlg::OnInitDialog()
{
myDlg::OnInitDialog(); <- recursive
...
}
By the way, putting myDlg::OnInitDialog in myDlg::OnInitDialog causes stack overflow. Call the base class instead, CDialogEx::OnInitDialog();

MFC Send message to a button (child to parent)

I want to send a message from my child window (CDialog) to the parent window (CFormview). If I press the cancel button at the child window, the Dialog should quit and the program should continue with the code of the STOP-Button at the parent Window.
void ChildDialog::OnBnClickedCancel()
{
CDTParentView *pButtonWnd = (CDTParentView *)AfxGetMainWnd();
pButtonWnd->OnBnClickedbuttonStop();
CDialogEx::OnCancel();
}
but there is an error in this objore.cpp:
BOOL CObject::IsKindOf(const CRuntimeClass* pClass) const
{
ENSURE(this != NULL);
// it better be in valid memory, at least for CObject size
ASSERT(AfxIsValidAddress(this, sizeof(CObject)));
// simple SI case
CRuntimeClass* pClassThis = GetRuntimeClass(); <------- error
ENSURE(pClassThis);
return pClassThis->IsDerivedFrom(pClass);
}
Can anyone tell me, whats the problem?
And maybe post a better idea to send the button-clicked message?
Your code isn't actually sending a message, it's trying to call the handler directly. It's easy to simulate the clicking of a button the same way Windows would do it, then your existing code will handle it naturally.
CWnd * pMain = AfxGetMainWnd();
CWnd * pButton = pMain->GetDlgItem(ID_STOP_BUTTON);
pMain->PostMessage(WM_COMMAND, MAKEWPARAM(ID_STOP_BUTTON, BN_CLICKED), (LPARAM)pButton->m_hWnd);
AfxGetMainWnd does not return a pointer to the CFormView, it returns a pointer to the CMainFrame. If your dialog is modal you can simply check the return value of the DoModal call that displays the dialog. Or you might have better luck with calling GetParent to get a pointer to the CFormView.

Unable to receive / capture Windows Messages

I'm fairly new to MFC and the Message handling context.
I've a DLL Consumer application, which has a CFrameWndEx derived class,CMainFrame. Now this calls a DLL, which puts a CDialog etc. into this MainFrame window.
I wish to receive certain messages into my application.
So what I have done is, declared the expected messages in the Message Map, of the DLL Consumer application, and defined the appropriate message handlers.
Now, even though I can see that the application is being sent those registered messages, I'm not able to receive / handle them in the Consumer Window, i.e., nothing happens when those messages are broadcast.
Mainfrm.h
class CMainFrame : public CFrameWndEx
{
public:
CMainFrame();
protected:
DECLARE_DYNAMIC(CMainFrame)
public:
void OnFileDialogdisplay(void);
afx_msg LRESULT OnLogonChanged(WPARAM,LPARAM);
afx_msg LRESULT OnLanguageChanged(WPARAM,LPARAM);
afx_msg LRESULT OnShutdownRequested(WPARAM,LPARAM);
afx_msg LRESULT OnReconnectServer(WPARAM,LPARAM);
afx_msg LRESULT OnChangeRole(WPARAM,LPARAM);
}
Mainfrm.cpp
<some header files>
static UINT UWM_LOGON_CHANGED = ::RegisterWindowMessage(UWM_LOGON_CHANGE);
static UINT UWM_LANGUAGE_CHANGED = ::RegisterWindowMessage(UWM_LANGUAGE_CHANGE);
static UINT UWM_RECONNECT = ::RegisterWindowMessage(UWM_RECONNECT_SERVER);
static UINT UWM_SHUTDOWN_REQUESTED = ::RegisterWindowMessage(UWM_REQUEST_SHUTDOWN);
static UINT UWM_ROLE = ::RegisterWindowMessage(UWM_ROLE_CHANGE);
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWndEx)
ON_WM_CREATE()
ON_WM_SETFOCUS()
ON_COMMAND(ID_VIEW_CUSTOMIZE, &CMainFrame::OnViewCustomize)
ON_REGISTERED_MESSAGE(AFX_WM_CREATETOOLBAR, &CMainFrame::OnToolbarCreateNew)
ON_COMMAND(ID_FILE_DIALOGDISPLAY, &CMainFrame::OnFileDialogdisplay)
ON_REGISTERED_MESSAGE(UWM_LOGON_CHANGED, OnLogonChanged)
ON_REGISTERED_MESSAGE(UWM_LANGUAGE_CHANGED, OnLanguageChanged)
ON_REGISTERED_MESSAGE(UWM_SHUTDOWN_REQUESTED, OnShutdownRequested)
ON_REGISTERED_MESSAGE(UWM_RECONNECT, OnReconnectServer)
ON_REGISTERED_MESSAGE(UWM_ROLE, OnChangeRole)
//ON_WM_NCCALCSIZE()
END_MESSAGE_MAP()
//code to register to Main server application to be able to receive messages
void manageregistration(CMainFrame* pFrame, bool flag)
{
if (flag)
{ // registration
HWND MyHandle = (HWND)pFrame->GetActiveWindow();
RegisterApmsClientPgm(_T("AAUserInterface"), MyHandle);
}
}
//Handlers
LRESULT CMainFrame::OnShutdownRequested(WPARAM,LPARAM lp)
{
AfxMessageBox(_T("Error"),MB_ICONERROR | MB_OK);
testProgram();
return 0;
}
LRESULT CMainFrame::OnChangeRole(WPARAM,LPARAM lp)
{
AfxMessageBox(_T("Error"),MB_ICONERROR | MB_OK);
testProgram();
return 0;
}
// etc etc.etc.
So, after all this, I can see that the Consumer application, is registered to receive these messages from another application which broadcasts them.
However, upon creating the condition where the messages are being broadcast, and they are as I can verify from other applications which receive them, no such message is being caught in my application.
I am not sure where the problem could be. The window is always on top, albeit with another CDialog derived DLL inside it.
Try using pFrame->m_hWnd. You can't assume that the Mainframe window is always active.

How can I create an ITaskbarList3 in C++ Builder?

I'm trying to use the ITaskbarList3 interface introduced with Windows 7 so that I can show task progress for a lengthy task in my taskbar icon. The documentation states that I should wait for a TaskbarButtonCreated message before trying to initialize my ITaskbarList3 component, but I don't seem to be getting any TaskbarButtonCreated messages.
Here is what I have so far:
I have a global variable in my .cpp file to store the custom message ID for TaskbarButtonCreated.
static const UINT m_uTaskbarBtnCreatedMsg =
RegisterWindowMessage( _T("TaskbarButtonCreated") );
I created a separate WndProc function to handle the new message.
void __fastcall TForm1::WndProcExt(TMessage &Message)
{
if(Message.Msg == uTaskbarBtnCreatedMsg && uTaskbarBtnCreatedMsg != 0) {
OnTaskbarBtnCreated();
}
else {
WndProc(Message);
}
}
In my form constructor, the very first line sets the WindowProc property to WndProcExt to route messages. I also tried tossing in a ChangeWindowMessageFilter to see if the TaskbarButtonCreated message was being filtered for some reason.
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
{
WindowProc = WndProcExt;
ChangeWindowMessageFilterEx(Handle, uTaskbarBtnCreatedMsg, MSGFLT_ALLOW, NULL);
...
}
In the debugger, the return value from ChangeWindowMessageFilterEx is always true. I've also confirmed my WndProcExt function receives all kinds of Windows messages, just not the one I'm looking for. The OnTaskbarBtnCreated function never gets called.
Am I missing a step? Is the message being filtered out or sent before my message handler is ready for it?
It is not a good idea to have the TForm assign a value to its own WindowProc property. For starters, the Handle window may have already been allocated before your constructor is even entered, due to DFM streaming, so you would miss all of the window's initial messages (which there can be several) before your constructor starts running. You need to override the virtual WndProc() method instead, and do pass the TaskbarButtonCreated message to the default handler, don't block it:
static const UINT m_uTaskbarBtnCreatedMsg = RegisterWindowMessage( _T("TaskbarButtonCreated") );
void __fastcall TForm1::WndProc(TMessage &Message)
{
TForm::WndProc(Message);
if ((Message.Msg == uTaskbarBtnCreatedMsg) && (uTaskbarBtnCreatedMsg != 0))
OnTaskbarBtnCreated();
}
As for ChangeWindowMessageFilterEx(), you need to call that every time the TForm's Handle window gets (re)allocated (which can happen multiple times during the Form's lifetime), so you need to override the virtual CreateWnd() method instead:
void __fastcall TForm1::CreateWnd()
{
TForm::CreateWnd();
if (CheckWin32Version(6, 1) && (uTaskbarBtnCreatedMsg != 0))
ChangeWindowMessageFilterEx(Handle, uTaskbarBtnCreatedMsg, MSGFLT_ALLOW, NULL);
// any other Handle-specific registrations, etc...
}
void __fastcall TForm1::DestroyWindowHandle()
{
// any Handle-specific de-registrations, etc...
TForm::DestroyWindowHandle();
}
Lastly, set the TApplication::ShowMainFormOnTaskbar property to true in the project's WinMain() function before your MainForm is created so its window, rather than the TApplication window, is managing the taskbar button (and to enable other Vista+ related features, like Flip 3D and Taskbar previews). Otherwise, you will have to use the TApplication::HookMainWindow() method to intercept any "TaskbarButtonCreated" messages that may get sent to the TApplication window.

How do I use SetTimer in non-dialog .cpp in my mfc app?

My question is for normal mfc SetTimer, as follows
void CTimersDlg::OnButtonBegin()
{
// create the timer
SetTimer(m_nTimerID, uElapse, NULL);
}
void CTimersDlg::OnButtonStop()
{
// destroy the timer
KillTimer(m_nTimerID);
}
void CTimersDlg::OnTimer(UINT nIDEvent) // called every uElapse milliseconds
{
// do something, but quickly
CDialog::OnTimer(nIDEvent);
}
but if I need to use SetTimer in non dialog.cpp, for example in my sender.cpp
how do I create the timer? As in the SetTimer fields, the handler(callback) function?
You can pass NULL as the window handle and include callback function in the call to SetTimer. This will allow you to receive timer notifications without associating it with a specific window.
If the timer is intended to be used in a separate "worker" thread (one without a window) you will still need to process the message queue in order to receive timer notifications. If you are creating a thread using a CWinThread object this is already handled for you in the default implementation of CWinThread::Run.
If you can update your question to include more information about the contents of sender.cpp I can provide a more suitable example. This uses the plain Windows API to create a timer and handle the required dispatch queue.
// Example only.
VOID CALLBACK timerCallback(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
printf("Timer called\n");
}
void SomeFunc()
{
SetTimer(NULL, 1, 1000, timerCallback);
MSG msg;
// msg-pump
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
SetTimer is associated with windows, you need windows handle to use it. For non window classes you should consider using CreateWaitableTimer or use TimerQueue