Wrong parent HWND in modal dialog - c++

Why do i get desktop as a parent HWND for my modal dialog here?
class CSaveProfileAsDlg:
public CSimpleDialog<IDD_DLG_RESOURCE>
{
....
LRESULT OnInitDialog(UINT, WPARAM, LPARAM, BOOL&)
{
...
HWND parent = GetParent(); // or GetAncestor(m_hWnd, GA_PARENT);
assert(parent != GetDesktopWindow()); // not ok
...
}
....
}
//somewhere in code
//m_hWnd is some valid HWND
assert(m_hWnd != GetDesktopWindow()); //ok
CSaveProfileAsDlg dlg;
dlg.DoModal(m_hWnd /*as a parent wnd*/);
I can "solve" it by passing corret HWND in CSaveProfileAsDlg ctor, but i'd like to have correct solution.
Thank you!

The documentation is very confusing but I think I found the problem. DoModal internally calls ::DialogBox(), one parameter of which takes a HWND named hWndParent. From the documentation:
hWndParent [in, optional]
Type: HWND
A handle to the window that owns the dialog box.
The keyword here is the word "owns". The section about owned windows confirms this:
Dialog boxes and message boxes are owned windows by default. An application specifies the owner window when calling a function that creates a dialog box or message box.
So we actually talk about the owner window instead of its parent. This makes sense as the dialog is a free floating window and not part of a window hierarchy as "parenthood" would imply.
You can get the owning window by using:
HWND parent = ::GetWindow(m_hWnd, GW_OWNER);

I had a similiar issue. I was wondering why GetParent() return always a different CWnd*.
The right solution is simple, just pass the desired pWnd to the dlg Constructor.
It will be stored in the CDialog member variable m_pParentWnd.
Then you can always get the passed pWnd with this member variable in your Dialog.
//somewhere in code
//pWnd some valid CWnd pointer
CSaveProfileAsDlg dlg (pWnd); // relevant!
dlg.DoModal();
.
class CSaveProfileAsDlg:
public CSimpleDialog<IDD_DLG_RESOURCE>
{
....
LRESULT OnInitDialog(UINT, WPARAM, LPARAM, BOOL&)
{
...
CWnd* _pWnd = GetParent(); // not ok, returns probably CMainFrame or similiar
CWnd* pWnd = m_pParentWnd; // your parent Wnd
...
}
....
}

I've got two versions of the same application and one of them disables all parent popup windows as it should when DoModal is called. Second version disables only top-level CMainFrame and real parent is stay enabled so I can call modal dialog twice and more.
This happen in CWnd::GetSafeOwner_, line :
hWnd = ::GetLastActivePopup(hWnd);
Firsts version return real parent while second version return CMainFrame.
I've spent one day and could not find the cause of such behaviour. However I've found workaround:
When DoModal is called it Disable CMainFrame so I can disable it's children also:
afx_msg void OnEnable(BOOL bEnable);
ON_WM_ENABLE()
void CMainFrame::OnEnable(BOOL bEnable)
{
std::for_each(m_bars.begin(), m_bars.end(),
[=](const std::pair<EBar, BaseBar*>& bar)
{
bar.second->EnableWindow(bEnable);
});
}

Related

mfc access to formview item from dialog box

In my SDI application i need to get this behawiour. After I click on a button on the FormView, a CDialog opens. When I press the OK button on the CDialog, I call a function of the FormView. I don't want to close the CDialog. I try to do it with modeless dialog, but when i call formview function from dialog, i can't access to formview's control, like it's lost hwnd; the error is can't read memory of m_hwnd, the hwnd is ???.
This is my code:
Open modeless dialog:
CCampiDlg *m_pDialog = NULL;
HWND hCampi = NULL;
// Invoking the Dialog
m_pDialog = new CCampiDlg;
if (m_pDialog != NULL)
{
BOOL ret = m_pDialog->Create(m_pDialog->IDD, this);
if (!ret) //Create failed.
{
AfxMessageBox(_T("Error creating Dialog"));
}
m_pDialog->ShowWindow(SW_SHOW);
}
when i press the ok button in the dialog i do:
CEditorTxView pView;
box2 = (CEdit*)(GetDlgItem(IDC_CAMPI_BOX2));
box2->GetWindowTextW(campo);
pView.inserisciCampo(1, campo);
In inserisciCampo function in CEditorTxView (CFormView) i have to do operation with my control txtCtrl, but it's lost hwnd. The declaration of txtCtrl is in the CEditorTxView.h
CTx1 txtCtrl;
And initialize it in DoDataExchange function:
void CEditorTxView::DoDataExchange(CDataExchange* pDX)
{
CFormView::DoDataExchange(pDX);
DDX_Control(pDX, IDC_TX1, txtCtrl);
}
Someone can help me plz?
I can give you two answers here:
How to do what you are asking (get access to a control of the CFormView from a modeless dialog)
How to solve your underlying problem (communicate changes in a modeless dialog to the owner view)
For the first one, you have to declare a pointer to the view in the dialog class and initialize it in the constructor of the view:
class CCampiDlg : public CDialog
{
public:
CCampiDlg(CEditorTxView* pView, CWnd*pParent = NULL) // Change declaration to add pointer to view
: m_pView(pView)
{
}
// ... Whatever
private:
CEditorTxView* m_pView;
}
Now in your button handler:
CEdit* box2 = (CEdit*)(GetDlgItem(IDC_CAMPI_BOX2)); // Why not use a control variable?
box2->GetWindowTextW(campo);
m_pView->inserisciCampo(1, campo);
This should do what you are asking for. However, it is the wrong way to do it.
The problem with this approach is that the dialog knows way too much about its parent. It knows it is of type CEditorTxView and that it has a member called inserisciCampo, that takes a number and some text.
It shouldn't know that much. In fact, knowing anything about it, other than it is of type CView or even CWnd, is too much.
If the dialog knows about the view, you can't reuse the dialog with other views, and anytime the view changes its representation (what now is a textbox may be a combobox in the future, for example) the dialog must change accordingly.
The solution would be to send a message to the parent, explaining what's happened. Then the parent (the view) should know haw to handle that event. For example:
class CCampiDlg : public CDialog
{
public:
CCampiDlg(CWnd*pParent = NULL) {}
protected:
OnOk()
{
CString campo;
c_CampiBox2.GetWindowText(campo);
GetParent()->SendMessage(UWM_CAMPO2_SET, 0, (LPARAM)&campo);
}
}
In the view:
// It can be ON_REGISTERED_MESSAGE:
ON_MESSAGE(UWM_CAMPO2_SET, OnCampo2Set)
//...
LRESULT CEditorTxView::OnCampo2Set(WPARAM, LPARAM lParam)
{
CString* s = (CString*) lParam;
inserisciCampo(1, *campo);
return 0;
}
Now, you have decoupled the view and the dialog. The dialog knows nothing about the view. You can change its type, change the representation, even make it a dialog, and you don't have to change anything in the dialog. And if you need that same modeless dialog somewhere else, you just drop it there, create a message handler in the parent, and voilĂ !
For further explanations and better examples, check these articles:
Dialog and control design (Your case is explained in the section "Notifications to the environment")
Message management
On Ok button click the below code is running:
CEditorTxView pView;
box2 = (CEdit*)(GetDlgItem(IDC_CAMPI_BOX2));
box2->GetWindowTextW(campo);
pView.inserisciCampo(1, campo);
Note that, you are creating the new pView in stack and it does't attach with any window. You are not actually referring the view that already created and launched your dialog acting a parent. Revisit the above code and try the get the view:
Try the below code, if it is not working (Google it)
CFrameWnd * pFrame = (CFrameWnd *)(AfxGetApp()->m_pMainWnd);
CView * pView = pFrame->GetActiveView();

Get main frame window

I want to get main frame window.
How to get main frame window in WTL-MDI?
class CChildFrame : public CMDIChildWindowImpl<CChildFrame>
{
...
LRESULT OnEdit(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& bHandled)
{
...
mainfrm->FlashWindow(TRUE);
return 1;
}
}
The WTL base classes CMDIChildWindowImpl, CFrameWindowImplBase don't hold a pointer to main frame. It does not mean that you cannot do it yourself, you derive from these classes so you can pass the pointer explicitly as a part of initialization, and use it from there. After all you always have parent HWND in the child window class, so you can send an application defined message to exchange with certain information with the parent window.

Can't get the handle of a Cwnd Class in MFC Windowless Activex?

I have asked two questions earlier about this and for each post there was some solutions i tried them, but the problem still exist.
My first question was : why a windowless Activex does not return the Handle. the suggestion was "change the creation setting an make windowless activate off, i have tried it but still m_hWnd property has returned zero as GetSafeHwnd() method has did.
the second one was the same question this one focused on COleControl class and it's ancestor CWnd. the solution was as this "Create invisible window somewhere in your control initialization code. Handle the messages sent to this window, and call controls methods directly". so i did that but the created class still returns zero handle.
here is my new invisible class source:
// moWind.cpp : implementation file
//
#include "stdafx.h"
#include "PINActive.h"
#include "moWind.h"
#include "include\xfspin.h"
#include <math.h>
// moWind
IMPLEMENT_DYNAMIC(moWind, CWnd)
moWind::moWind(){}
moWind::~moWind(){}
//=============================================================
LRESULT moWind::OnExecuteEvent (WPARAM wParam, LPARAM lParam)
{
WFSRESULT *pResult = (WFSRESULT *)lParam;
CString EK=_T("");
CString str;
int reskey=0;
if (pResult->u.dwEventID=WFS_EXEE_PIN_KEY)
{
LPWFSPINKEY pressedkey;
pressedkey=(LPWFSPINKEY)pResult->lpBuffer;
reskey = log10((double)pressedkey->ulDigit) / log10((double)2);
EK.Format("%d",reskey);
xfsOnKeyEvent->OnKeyRecieved(reskey);
}
else
{
str.Format("ExecuteEvent: ID = %d\r\n", pResult->u.dwEventID);
}
MessageBox("a Execute message Recieved");
return 0;
}
BEGIN_MESSAGE_MAP(moWind, CWnd)
ON_MESSAGE(WFS_EXECUTE_EVENT,OnExecuteEvent)
END_MESSAGE_MAP()
and this is .h file of the class:
// moWind.h
class IXFSEvents
{
protected:
IXFSEvents(){};
virtual ~IXFSEvents(){};
public:
virtual void OnKeyRecieved(int key)=0;
};
class moWind : public CWnd
{
DECLARE_DYNAMIC(moWind)
public:
moWind();
virtual ~moWind();
void Register(IXFSEvents* obj)
{
xfsOnKeyEvent= obj;
}
protected:
IXFSEvents* xfsOnKeyEvent;
LRESULT OnExecuteEvent (WPARAM wParam, LPARAM lParam);
DECLARE_MESSAGE_MAP()
};
and at the end here this the way I've used this class in my Activex:
in the myActivex.h file:
include "moWind.h"
class CmyActivexCtrl : public COleControl, public IXFSEvents
{
...
Class definition
...
protected:
moWind tmpWind;
.
.
};
finally in the creation method of myActivex i have initialized the component callback method an wanted to get it's Handle as this:
CmyActivexCtrl::CmyActivexCtrl()
{
InitializeIIDs(&IID_DmyActivex, &IID_DmyActivexEvents);
tmpWind.Register(this);
myOtherComponent.WindowsHandle=tmpWind.GetSafeHwnd(); //here my Cwnd derived class returns zero
//my other component gets the handle and call an API with it to register
//the given handle and force the API to send the messages to that handle.
}
As you mentioned you need a window handle to be able to receive user messages through it, you always have an option of creating a helper window, such as message only window, see Using CreateWindowEx to Make a Message-Only Window.
For your windowless control it is okay to not have any window handle at all, so you cannot really rely on handle availability unless you own a window yourself.

How to create a hidden window in C++

How to create a hidden window ?
The purpose of this window is to receive some messages.
When you create the window, omit the WS_VISIBLE flag and don't call ShowWindow.
In a win32/mfc environment what you need to do is create a class and inherit from CWnd like this:
class HiddenMsgWindow : public CWnd
{
...
}
in the constructor of that class you would instantiate a window like this:
HiddenMsgWindow::HiddenMsgWindow()
{
CString wcn = ::AfxRegisterWndClass(NULL);
BOOL created = this->CreateEx(0, wcn, _T("YourExcellentWindowClass"), 0, 0, 0, 0, 0, HWND_MESSAGE, 0);
}
This gets you a hidden window with a message pump almost ready to rock and roll.
the rest of the story is to provide the linkage between the window messages and the handlers for those messages.
This is done by adding a few macros and a message handler to your implementation file (.cpp) like this:
BEGIN_MESSAGE_MAP(HiddenMsgWindow, CWnd)
ON_MESSAGE(WM_USER + 1, DoNOOP)
END_MESSAGE_MAP()
LRESULT HiddenMsgWindow::DoNOOP(WPARAM wParam, LPARAM lParam)
{
AfxMessageBox(_T("Get Reaaady for a Ruuummmmmmmbllllle!"));
return LRESULT(true);
}
Then you need to fill in the rest of the glue in the header file like this:
class HiddenMsgWindow : public CWnd
{
public:
HiddenMsgWindow();
protected:
afx_msg LRESULT DoNOOP(WPARAM wParam, LPARAM lParam);
DECLARE_MESSAGE_MAP()
}
And just like magic, you have a hidden window all ready to pump your messages.
In order to use this message window you would instantiate the class retrieve it's handle and send or post messages as desired. Just like this:
HiddenMsgWindow *hmw = new HiddenMsgWindow();
SendMessage(hmw->m_hWnd, WM_USER + 1, 0, 0);
You call CreateWindow() or CreateWindowEx() as usual but don't specify the WS_VISIBLE window style. Of course ShowWindow() should also not be called.
If you don't need to show the window,
as some suggest create the window, omit the WS_VISIBLE flag and don't call ShowWindow.
If not when you call showWindow(), add SW_HIDE parameter.
ShowWindow(hWnd, SW_HIDE);
Ahh, Just came across an issue. If u fail to specify HWND_MESSAGE unexpected behaviours could occur. I have used NULL which is NOT correct. In my case it caused that MS Excel took 10s or more to load an xls file, while it normally takes less then a second when my app was not running!
MSDN says this as someone mentioned before.

Intercepting messages from a child of a child with MFC

I have a CListCtrl class and at the moment when a user selects one of the sub items I am displaying a CComboBox over the subitem which the user can then make a selection from.
However I have a problem. When the user has made a selection i need the combo box to disappear (ie intercept CBN_SELCHANGE). The problem is that I need to make the CComboBox a child of the CListCtrl (Otherwise I get weird problems with the list drawing over the combo box even if i set the combo box to be topmost). So the CBN_SELCHANGE message gets sent to the list view which, understandably, ignores it. How can I get the list view to pass that message up to the parent window.
Do I really need to derive my own CListCtrl class that simply intercepts the CBN_SELCHANGE message and passes it up to the parent window? Is there a better way to do this than creating an OnWndMsg handler?
Thanks for any help!
Edit: This code works
class CPassThroughListCtrl : public CListCtrl
{
protected:
virtual BOOL OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
if ( message == WM_COMMAND )
{
GetParent()->SendMessage( message, wParam, lParam );
}
return CListCtrl::OnWndMsg( message, wParam, lParam, pResult );
}
public:
CPassThroughListCtrl()
{
};
};
But i'd really like to know if there is a nicer way to do this.
You can subclass CComboBox such that it will handle CBN_CLOSEUP message.
Your custom Combo will know about the manager i.e. the object that created it in the first place and will have to destroy it upon close up (top level window or whatever, should be provided as an argument to your custom combobox constructor)...
So when you create combobox on a top of the list item you will create instance of this customized combobox instead of the MFC default one.
Combobox event handler could look like that:
BEGIN_MESSAGE_MAP(CNotifyingComboBox, CComboBox)
ON_CONTROL_REFLECT(CBN_CLOSEUP, OnCloseUp)
END_MESSAGE_MAP()
void CNotifyingComboBox::OnCloseUp()
{
// _manager is pointer to the object that created this combobox,
// and is responsible for its destruction,
// should be passed into CNotifyingComboBox cosntructor
if( NULL != _manager )
{
_manager->OnCloseUpComboBox(this);
}
}