Clicking on CEdit and CSpinButton causes losing capture in MFC dialog - c++

I have a dialog box which i use for picking colors. It consists of a buttons that represent colors and an edit control with buddy spin button.
When i create the dialog i call SetCapture() in order to get the clicks and whenever they are outside of the dialog it should closes.
Everything works fine until i click on a edit control or on it's buddy. After that CMyDialog::OnLButtonDown() will not be called, because i suppose that the capture is lost.
I tried calling SetCapture() in ON_EN_CHANGE message handler but it does not solve the problem.
My qestion is:
Where should i call SetCapture() in order to close the dialog when i have already clicked on the edit control or it's spin button?

The approach that i have taken is not right at all. Turns out that ON_WM_NCACTIVATE message should be handled and the dialog should be closed if
CDialog::OnNcActivate( bActive ) returns false.
the function looks like this :
BOOL CMyDialog::OnNcActivate( BOOL bActive )
{
BOOL bResult = CDialog::OnNcActivate( bActive );
if( !bActive )
{
PostMessage( WM_CLOSE );
}
return bResult;
}

Related

Programmatically closing a dialog box - win32

I'm working on an application to detect a pop-up dialog and then
automatically dismiss it. I'm writing this as a C++/Win32 app. The
dialog box is generated by IE 7 and I can detect the window, but
several methods to get the OK button to "click" have failed.
Doing searches for other people's solutions, sending these messages to
the button handle seems to have worked in a lot of situations:
PostMessage( handle, WM_LBUTTONDOWN, 0, 0 );
PostMessage( handle, WM_LBUTTONUP, 0, 0 );
PostMessage( handle, BM_SETSTATE, 1, 0 );
It has no effect on the button state in my tests though.
I can send tab characters to the main window and see that the OK
button gets focus, but then sending return characters does nothing.
To learn more about this I used Spy++ to get information about the
window hierarchy and what messages are delievered when I manually
click the OK button.
Looking at the message log and reading about WM_MOUSEACTIVATE seamed
to offer a solution. The log info shows that 0002166C was the button
window. So in my code I tried this:
GetClassNameA( handle, str, str_size );
if( strcmp( str, "Internet Explorer_Server" ) != 0 )
return TRUE; // Not the window we're interested in.
// Send a message to activate the button window and have it process a mouse click.
PostMessage( handle, WM_MOUSEACTIVATE, (WPARAM) dialog_handle, MAKELPARAM( HTCLIENT, WM_LBUTTONDOWN );
Based on the window hierarchy and message log, I think the window with
the class name "Internet Explorer_Server" is the button. Maybe I'm
wrong, because it does seem like an odd class name for a button...
Below is a link to the window hierarchy image, message log when I
manually click the OK button. Last is the code that's executed on a 1
second timer ticket, looking for the window.
Any insight and help is appreciated!
Image of the window hierarchy, source, window messages, and test dialog source are available here:
https://sites.google.com/site/matthewmillersmiscellanea/Home/
Ideally, you should create a DLL which exports a Global CBT Window Hook. This would allow you to get early notification when a dialog is going to be created. This would avoid the need to drain resources by constantly polling.
Once you've detected that a dialog is about to be created, you have two options:
1) Prevent the dialog creation.
I don't recommend this, it causes all sorts of problems with code that was fully expecting a valid HWND to be returned by CreateDialog();
2) Asynchronously control the dialog.
We achieved this by using PostMessage with a Registered user message and picking it up by hooking the WNDPROC. When you get this message, then you have to decide how to kill the dialog that you're in.
There are multiple ways to exit the dialog:
a) Simulate pressing OK, Cancel, Abort, No buttons using WM_COMMAND(BN_CLICKED) (as Chris comments). You can use GetDlgItem(), look for the WindowText and make your choice. However, this doesn't work for non-US-English. There may be some distance in leveraging the Accessibility API here though.
b) Simulate closing the dialog with PostMessage(WM_CLOSE, m_hWnd). This doesn't always work as expected - some dialogs have no [X] close button and their client code is expecting a specific button to be pressed instead.
c) Simulate user input using the SendInput() API. This worked around dialogs that had anti-popup-killer code in them :)
Our final solution was a rule+heuristic-based approach that had a configuration file which we could tweak when the app/IE dialogs changed their ID's, class names or parent class names.
To close continually a specific popup given that you know the window class name and window caption
#define UNICODE
#include <windows.h>
#pragma comment(lib, "user32")
int main (int nn, char ** aa)
{
while (true) {
HWND iHandle = FindWindow (L"theWindowClassName", L"theWindowCaption");
if (iHandle > 0) SendMessage(iHandle, WM_SYSCOMMAND, SC_CLOSE, 0);
Sleep (200); // check 5 times per second
}
return 0;
}
if one is not known or too generic (e.g. "Dialog") you can omit it by passing a null
HWND iHandle = FindWindow (L"theWindowClassName", 0);
or
HWND iHandle = FindWindow (0, L"theWindowCaption");
of course this will close all windows with the given names.

Main dialog destroys before command message handler returns

My program use a modeless dialog to interact with the user and also has a tray icon.
A user can quit the app immediately by using the tray icon.
BOOL OnInitDialog()
{
init data...
}
void OnDestroy()
{
destroy data...
}
void OnSomeButton()
{
CFileDialog dlg;
...
dlg.DoModal(m_hWnd));
access data...
...
}
void OnMenuExit()
{
DestroyWindow();
}
The problem is that when I popup a modal dialog(OnSomeButton), and then quit using the tray icon menu, the main dialog is destroyed first, and then the modal one returns, trying to access some invalid data, causing a crash.
I know i can add some check code before accessing the data, but is there any other way?
Is there any way to ensure OnSomeButton() returns before window destroy?
You need to add your own application level code. There is no system support for this issue primarily because there can be so many pecularities that no generic approach is possible.
Yeah. When you quit from the tray menu, you can send a WM_CLOSE or similar message to your modal dialog that causes it to exit. Even if your main window is destroyed before that OnSomeButton returns you will be okay provided the remainder of that function does not access any class internals (member variables etc). You could ensure this by having the window proc of your modal dialog return an 'abort' code or something when it is closed in this way.

How to close a dialog box using do modal

Okay basically I have an MFC application with lots of dialogs that need to be cycled through. Basically when you click one button to go to another dialog, I want the previous dialog to close. Right now the dialogs are just displayed over the top of each other. How do I get the dialog to close once the new dialog has opened? Here is some example code:
void CMachine2Dlg::OnBnClickedNewmc()
{
NameDlg Dlg;
Dlg.DoModal()
}
What you can do is hide the parent dialog when you DoModal() the new dialog and destroy it after the new dialog ends. I have not tested the ShowWindow() below but you get the idea, if it doesn't hide the dialog look for another similar function.
void CMachine2Dlg::OnBnClickedNewmc()
{
ShowWindow( SW_HIDE);
NameDlg Dlg;
Dlg.DoModal();
EndDialog( 0 );
}
IT will be difficult to chain those dialog the way you mention. Do modal is usually meant to implement exactly what you are experiencing. Ie: dialog pops up over the previous one.
One way to do this is to create the modal dialogs sequence in the class that calls the first dialog and use the return value of the previous dialog to determine if you need to show the second one and so forth.
For ex:
// define a bunch of constants, any number would do,
// I would avoid 0 and 1 as they usually mean success/error
// This code can be returned in the EndDialog call in a method of your choice (say button click handler).
const int c_needNextDialog = 101;
dialog1 dlg1;
if( dlg1.DoModal() == c_needNextDialog )
{
dialog2 dlg2;
if( dlg2.DoModal() == c_needNextDialog )
{
... and so forth
}
}
I'm sure you get the idea...
in your dialog, you return like so (taken directly from msf)
void dialog1::OnSomeAction()
{
// Do something
EndDialog(c_needNextDialog); // This value is returned by DoModal!
// Do something
return; // Dialog closed and DoModal returns only here!
}
I would stay clear of modeless dialog, you will end up with another problem like how to control the flow of dialog and prevent people to click the main window of your application behind.
You could try calling
EndDialog(nResult);
OnOK(), OnCancel() or EndDialog(nResult) will answer your title question.
However, like #tenfour suggested, you should be using a property sheet / wizard. It can also be a single dialog window with several child property page windows that you show or hide depending on what you want to be seen.
For this, you will need:
1 dialog window, maybe with Prev/Next buttons
1 picture box, frame style, not visible, inside the dialog where you want the child windows to appear
n property pages, child style, no border, where you put all the controls.
Create a class for the dialog and each property page, add a member variable of each property page to the dialog, create the property pages and use the frame as a reference to place them. On button click just show/hide the necessary pages.
You could call OnCancel() inside your dialog class. Like: this->OnCancel();
#tenfour suggest a good possible solutions
But if its not possible for you
you should create the dialogs from one basic windows/Dlg
Mydialog dlg1
if(dlg1.DoModal() )
{
//do something
}
else
// do something else
Mydialog dlg2
if(dlg2.DoModal() )
{
//do something
}
else
// do something else
and so on....
This way you don't have easy controll of "what's" going on and you don't have to mess with different windows, messageloops.

How to end a dialog mfc application from CDialog::OnInitDialog or immediately after this function ends?

In one case of my application, I want to end the application in the dialog's OnInitDialog or immediately after this function. Is there any way to do it?
I tried using windows messages – in OnInitDialog calling SendMessage and in the handler of the sent message calling OnClose – but it didn't do what I intended since the dialog still appeared.
PostQuitMessage(0) in the WM_INITDIALOG handler body will do the job.
You can simulate the user clicking the X in the corner of the dialog by sending a WM_SYSCOMMAND message:
PostMessage(WM_SYSCOMMAND, SC_CLOSE, 0);
The easiest solution:
CMyDialog::OnInitDialog()
{
if( "My Condition is invalid" )
{
EndDialog(-1);
return FALSE;
}
}
You have to do "return FALSE" juste after EndDialog because as you can read in the Microsoft documentation CDialog(classe), EndDialog doesn't close the dialog immediately, but generates a notification which when handled by Microsoft has to effect to end the dialog.

Dialog not close on Windows Mobile

I made a very simple MFC application that call a Dialog when I click in a button, and send a MessageBox after 5 seconds.
The problem is, when I was in the second dialog and I dismiss the MessageBox from the parent (not click OK button of MessageBox. I click in a blank part of the second dialog) I cannot close this dialog (The second dialog) when I click OK or CANCEL button.
Why?
Part of Code:
Main Dlg:
BOOL Cmult_rc_testDlg::OnInitDialog()
{
CDialog::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
SetTimer(1, 5000, NULL);
return TRUE; // return TRUE unless you set the focus to a control
}
void Cmult_rc_testDlg::OnBnClickedButton1()
{
CDlg1 a;
a.DoModal();
}
void Cmult_rc_testDlg::OnTimer(UINT_PTR nIDEvent)
{
KillTimer(nIDEvent);
MessageBox(L"oi");
CDialog::OnTimer(nIDEvent);
}
The second Dialog is default code generated by MFC wizard.
Not sure I understand your question entirely . . . it sounds like you're trying to close the parent window when the message box is still shown?
If that's the case, the parent window owns the message box, and is not allowed to acquire focus until the message box is closed. You could try using
::MessageBox(NULL, L"oi", L"MessageBox", MB_OK);
instead of MessageBox, which will create a message box that allows you to focus back on the original window still (The :: means to use the global namespace version of MessageBox, which is the Windows native call as opposed to MFC).