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();
Related
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.
ERROR: afxwin2.inl line 165
My app is a dialog box with a few edit boxes. Once I click the button to evaluate the information entered I want to open a child dialog to display the results. I tried overloading DoModal() like this:
//in the child dialog
//.h
CResultsDlg::CResultsDlg(CParentDlg *parent);
virtual INT_PTR DoModal(float bmi);
//.cpp
CResultsDlg::CResultsDlg(CParentDlg *parent) : CDialogEx(CResultsDlg::IDD), _parent(parent)
{ //initializations }
INT_PTR CResultsDlg::DoModal(float bmi)
{
m_sBMI.Format("%f", bmi);
m_hBMI.SetWindowTextA(m_sBMI); //crashes !!!!!!!!!!
m_hBMI.SendMessageA(WM_SETTEXT, 0, (LPARAM)"15.11"); //crashes !!!!!!!!
// OnInitDialog(); //because this wasn't getting called at all
return CDialogEx::DoModal();
}
BOOL CResultsDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// __super::OnInitDialog(); //no difference...
m_hBMI.SetWindowTextA("10.3"); //crashes !!!
return true; // return TRUE unless you set the focus to a control
}
//in the parent dialog
//.cpp
void CParentDlg::OnBnClickedCalculate()
{
CResultsDlg childResultsDlg = this;
childResultsDlg.DoModal(15.7);
}
m_hBMI is a handle to a static text control. I tested an edit box but it still crashed.
I understand that it probably has something to do with the controls not being created yet but I tried every way I know.
Using breakpoints, I confirmed that OnInitDialog does not get called at all unless I put it in the overloaded DoModal function. SetWindowText/SendMessage still crashes in OnInitDialog with the same ASSERT error.
If I remove all SetWindowText/SendMessage then the child window does come up modal like it should but the 'result' static text control is the same as the text I set it to in the dialog editor properties pane.
Thanks !!!!!
*MORE DETAILS*-----------
void CResultsDlg::DoDataExchange(CDataExchange* pDX) // DDX/DDV support
{
CDialogEx::DoDataExchange(pDX);
DDX_Text(pDX, IDC_BMI, m_fBMI);
DDV_MinMaxFloat(pDX, m_fBMI, 0, 100);
DDX_Control(pDX, IDC_BMI, m_hBMI);
}
The usual sequence when you start a dialog is:
You call CDialog::DoModal.
The dialog window gets created.
The child controls of the dialog get created.
OnInitDialog gets called.
CDialog::OnInitDialog calls DoDataExchange.
You have a DDX_Control call in your DoDataExchange method to map the child control to a member variable.
Notice that the member variables only get initialized at the end of that sequence. You're trying to use them way before, so you get a crash.
Store the value you need for initialization in a member variable and take care of it in DoDataExchange.
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.
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.
I created a control's variable for CEdit:
class CGateDlg : public CDialog
{
...
public:
// here is my control's variable
CEdit m_edit_a;
// here I map variable to control
virtual void DoDataExchange(CDataExchange* pDX);
}
And this is how I map my variable to the control:
void CGateDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_EDIT_A, m_edit_a);
}
This is how it works: user types some text into the edit box. Then he presses the "Reset" button which clears the edit box. This is a piece of code responsible for clearing edit box after clicking Reset button:
void CGateDlg::OnBnClickedReset()
{
// clear edit box
m_edit_a.SetWindowTextW(L"");
}
Application starts without any errors. I type some text into EditBox and hit "Reset" button. Then I get an error which leads me to winocc.cpp, line 245 (ENSURE(this)):
void CWnd::SetWindowText(LPCTSTR lpszString)
{
ENSURE(this);
ENSURE(::IsWindow(m_hWnd) || (m_pCtrlSite != NULL));
if (m_pCtrlSite == NULL)
::SetWindowText(m_hWnd, lpszString);
else
m_pCtrlSite->SetWindowText(lpszString);
}
I think the problem is with the hWnd:
this 0x0030fa54 {CEdit hWnd=0x00000000} CWnd * const
but how to fix it ?
Everything works fine when I access my control's value using this:
CEdit *m_edit_a;
m_edit_a = reinterpret_cast<CEdit *>(GetDlgItem(IDC_EDIT_A));
m_edit_a->SetWindowTextW(L"");
What am I doing wrong ?
I can see two possibilities:
The control does not exist when the dialog starts. The first thing that CDialog::OnInitDialog will do is call DoDataExchange, so if you're creating the control later in the initialization process it's too late.
Your own OnInitDialog is not calling CDialog::OnInitDialog so DoDataExchange is not being called.
I think you should no use directly the meber of your control (in this case m_edit_a). Instead you should use a memeber variable, let's say CStrimg m_edit_data, and you should link it to the control:
DDX_Text(pDX, IDC_EDIT_A, m_edit_data); // as you did it in DDC_Cotrol
Now you can use directy the variable, but in order the control to be updated you should use the following code before using it:
UpdateData(true); // unlocks the control in a sense
m_edit_data = "this is my test";
UpdateData(false); // locks the control again (in a sense)
This is normal procedure in MFC :), hope I helped...
ohh... you should also add the control to String Table ... (let me know if you do not know)
I can not find something wrong with you. I Create a new project using VC6.0,and associate a variable to the Edit,just link you do. the exe operates normally.
class CEditTestDlg : public CDialog
{
// Construction
public:
CEditTestDlg(CWnd* pParent = NULL); // standard constructor
// Dialog Data
//{{AFX_DATA(CEditTestDlg)
enum { IDD = IDD_EDITTEST_DIALOG };
CEdit m_Edit;
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CEditTestDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
......
.cpp
void CEditTestDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CEditTestDlg)
DDX_Control(pDX, IDC_EDIT1, m_Edit);
//}}AFX_DATA_MAP
}
void CEditTestDlg::OnBnClickedReset()
{
// TODO: Add your control notification handler code here
m_Edit.SetWindowText("tttt");
}
so,I think it is not a code problem.You had better try again.
If your dialog starts off calling CDialog::OnInitDialog() and your DoDataExchange starts off calling CDialog::DoDataExchange but still you have null hWnd pointers and get CNotSupportedException, make sure your resource (rc) file's dialog template includes all the controls (IDC_) and such you have in DoDataExchange.
Check for overriding definitions if using a DLL that also provides resources.