timed MessageBox in console application - winapi

I use MessageBox function in Win32 console application.
Application does not not use MFC, not even event loop.
I need to make a wrapper, MessageBoxTimed(), that exits
(and dialog box disappears) after N seconds, if user did not press any button.
Is there more or less simple way to do this ?

This will not be trivial. Since the MessageBox() function itself is modal, you will likely need to start another thread that waits for the predefined number of seconds, and is interrupt-able if the message box is dismissed manually.
If the timer expires, use the FindWindow() API to find the handle of the message box and then simulate a click of the OK button, or perhaps more appropriately a keypress of the ESC button.
EDIT: Actually, not too bad. This isn't fully tested, may need some additional cleanup, but is enough to get you started.
#include <Windows.h>
class TimedMB
{
public:
TimedMB() : timeout_(0), caption_(0)
{
interrupt_ = CreateEvent(NULL, FALSE, FALSE, NULL);
}
~TimedMB()
{
CloseHandle(interrupt_);
}
static DWORD WINAPI timer(LPVOID param)
{
TimedMB* mb = reinterpret_cast<TimedMB*>(param);
if(WAIT_TIMEOUT == WaitForSingleObject(mb->interrupt_, mb->timeout_))
{
HWND message_box = FindWindow(NULL, mb->caption_);
if(::IsWindow(message_box))
{
PostMessage(message_box, WM_COMMAND, IDCANCEL, 0);
}
}
return 0;
}
void DisplayMessageBox(const char* msg, const char* caption, DWORD timeout)
{
timeout_ = timeout;
caption_ = caption;
CreateThread(NULL, 0, &TimedMB::timer, this, 0, NULL);
::MessageBox(NULL, msg, caption, MB_OKCANCEL);
::SetEvent(interrupt_);
}
private:
HANDLE interrupt_;
DWORD timeout_;
const char* caption_;
};
int main()
{
TimedMB mb;
mb.DisplayMessageBox("Hello There!", "My Message Box", 5000);
}

If you need to dismiss it automatically, I'd avoid using MessageBox at all. Instead, I'd just put together a dialog that closes itself after the specified period of time. If memory serves, you can do this pretty easily by setting a time when you display the pseudo-message box dialog. When the time goes off or the user clicks "ok" (or "close", etc.) you close the window and cancel the timer.

Don't do this. Modal dialogs should be closed by user intervention. Deviating from this pattern is just confusing and non-standard. If you want a message windows that closes itself, then use a balloon window.

Related

SetWindowsHookEx for WH_KEYBOARD_LL handler PostThreadMessage message not received?

I setup a handler to catch keystrokes for a short time while using a private message loop for the window. And while it works, if I try the PostThreadMessage the window never receives the message?
The callback looked like (I tried other messages to, this one sample was with a private message):
LRESULT CALLBACK LLKeyboardHookProc(int ncode, WPARAM wp, LPARAM lp)
{
if (ncode == HC_ACTION) {
if (wp==WM_KEYDOWN) {
KBDLLHOOKSTRUCT *kbs=reinterpret_cast<KBDLLHOOKSTRUCT*>(lp);
if (kbs->vkCode==VK_ESCAPE) {
PostThreadMessage (GetCurrentThreadId(), UWM_MYCLOSEMESSAGE, 0, 0);
}
}
}
return CallNextHookEx(0, ncode, wp, lp);
}
HHOOK kbhook = SetWindowsHookEx(WH_KEYBOARD_LL, LLKeyboardHookProc, GetModuleHandle(NULL), 0);
The message loop for the WS_EX_LAYERED window looks like:
while (IsWindow(hwnd)) {
MSG msg;
while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
// let MFC do its idle processing
LONG lIdle=0;
while (AfxGetApp()->OnIdle(lIdle++));
WaitMessage();
}
// clean up
UnhookWindowsHookEx(kbhook);
I changed it to use a volatile variable for now to close the window but I'd really like to send it a message. Using a global HWND wouldn't be thread-safe without locks so didn't want to go that way.
P.S. I did check the PostThreadMessage result and it was not FALSE (no debug messages when I added them) so it seemed to post it somewhere.
PostThreadMessage() posts a thread message to the queue, not a window message. Your message loop is ignoring thread messages, only dispatching window messages. A window will NEVER see a thread message, so you need to handle such messages directly in your message loop instead, eg:
MSG msg;
while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.hwnd == NULL && msg.message == UWM_MYCLOSEMESSAGE) { // <-- add this
// do something...
}
else {
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
}
However, as Hans Passant stated in a comment:
Never use PostThreadMessage() when you also created a window, something as simple as resizing the window causes the message to get lost.
Raymond Chen has posted several blog articles on this subject:
Thread messages are eaten by modal loops
Watching thread messages disappear
Rescuing thread messages from modal loops via message filters
Why do messages posted by PostThreadMessage disappear?
You could try using a WH_MSGFILTER hook, like Raymond demonstrated. But the best way to solve this is to simply not post a thread message at all, post a window message instead. Have your keyboard hook use PostMessage() (or even SendMessage()) to an HWND that your app owns, and then you can handle the message in that window's message procedure.

Hide an MFC dialog window

I have written an MFC dialog based application which is launched by some another application. For now, I have not added any code. It is just the default files that I got. The other application can successfully launch my application.
I am trying to hide the window of my application when the other application launches it.
BOOL CMyApp::InitInstance()
{
CMyAppDlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
}
else if (nResponse == IDCANCEL)
{
}
return FALSE;
}
I tried to use:
dlg.ShowWindow(SW_HIDE)
but it still does not hide the window.
How can I accomplish this task?
I'd suggest you have another problem someplace.
If you create a totally new, blank MFC app (Visual Studio 2010) then in App::InitInstance, setting SW_HIDE rather than SW_SHOW does cause the resultant window to be hidden.
BOOL CProj1App::InitInstance()
{
// boilerplate code
. . .
// The one and only window has been initialized, so show and update it
m_pMainWnd->ShowWindow(SW_HIDE); // WORKS!
m_pMainWnd->UpdateWindow();
return TRUE;
}
As soon as you call DoModal your dialog is doomed to be shown. There is only one workaround that successfully avoids focus/flicker problems. See my answer here: Hiding an MFC dialog box
Hence, your code should look like this:
BOOL CMyApp::InitInstance()
{
CMyAppDlg dlg;
dlg.SetVisible(FALSE); // Sets m_visible flag to FALSE.
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
}
else if (nResponse == IDCANCEL)
{
}
return FALSE;
}
Solution to the above issue. The InitInstance code should be as follows:
BOOL CMyApp::InitInstance()
{
CWinApp::InitInstance();
AfxEnableControlContainer();
CMyAppDlg dlg;
dlg.Create(IDD_MyAppUI_DIALOG,NULL);
dlg.ShowWindow(SW_HIDE);
dlg.UpdateWindow();
m_pMainWnd = &dlg;
return TRUE;
}
First of all let me address some issues with previous solutions.
chintan s:
Indeed dialog will be killed when function goes out of scope. It would be a valid solution if dialog was declared as a member variable of the app class.
Vikky:
No need to call Windows API, since dialog is derived from CWnd and it inherits ShowWindow member that take only one parameter: show command.
ixe013:
This solution will work, however, before dialog hides, it will flash, since ShowWindow is called before OnInitDialog is called.
Pete:
This won’t work, since, modal dialog starts before m_pMainWnd has any value assigned to it.
The solution is pointed by ixe013.
This is so far the only solution that works but you will have to declare member variable in you dialog class, as described in the article.
You must hide the dialog from the inside.
Overload OnInitDialog
Call CDialogEx::OnInitDialog()
Hide your window and return
Here is the code
BOOL CMyAppDlg::OnInitDialog()
{
BOOL result = CDialogEx::OnInitDialog();
this->ShowWindow(SW_HIDE);
return result; // return TRUE unless you set the focus to a control
}
There is another method with a sentinel value, YMMV.
The showWindow method has 2 variable.
handle of window
nCmdShow(Controls how the window is to be shown)
BOOL WINAPI ShowWindow(
In HWND hWnd,
In int nCmdShow
);
HWND hWnd = GetSafeHwnd();
ShowWindow(hWnd,SW_HIDE);
See HERE

Shell BrowseForFolder preselected path

I have this call:
oShell.BrowseForFolder(Me.hwnd, "Select path:", 0, "C:\dir\")
This opens a standard file browser dialog with "C:\dir\" as root.
My problem is that you can not browse above the root folder. (as specified in doc http://msdn.microsoft.com/en-us/library/bb774065(v=vs.85).aspx)
Any suggestions on oppening this dialog with a selected path and full browsing posibility?
Thanks
The way to do this involves calling the underlying API, SHBrowseForFolder().
Since you want the entire shell namespace to be available you need to pass NULL as pidlRoot. In order to select your desired folder you will need to provide a callback in lpfn. Make this callback respond to BFFM_INITIALIZED by setting the selected folder. This selection is performed by sending the BFFM_SETSELECTION message to the dialog's window handle (passed to the callback function).
No code because I don't have VB6, but hopefully this outline of the method is enough to get you on your way.
Karl E Peterson's excellent website contains a sample which demonstrates the API call SHBrowseForFolder with a callback, as in David Heffernan's answer.
The KeyStuff project
Look at MFolderBrowse.bas, routine BrowseForFolderByPIDL which passes a callback function BrowseCallbackProc.
Try the old CCRP project. It has a nicely done implementation of the Browse dialog. I used it in several of my projects and it has properties to address the issue you are having.
Here a code ready for copy and paste in a C++ class:
// static
int CALLBACK Func::FolderBrowserCallback(HWND h_Dlg, UINT uMsg, LPARAM lParam, LPARAM lpData)
{
if (uMsg == BFFM_INITIALIZED)
{
// Requires Windows XP or higher
SendMessageW(h_Dlg, BFFM_SETEXPANDED, TRUE, lpData);
}
return 0;
}
// returns an empty string u16_PathOut if an error occurrs or if the user cancels the dialog
void Func::GetOpenFolder(HWND h_Owner,
const WCHAR* u16_Title, // IN: Title at the top of dialog
int s32_CsidlRoot, // IN: Root folder for treeview (CSIDL_DRIVES -> My Computer)
const WCHAR* u16_Preselect, // IN: NULL or the folder to be preselected and expanded
WCHAR* u16_PathOut) // OUT: selected path
{
u16_PathOut[0] = 0;
// CoInitialize(NULL);
// InitCommonControls();
ITEMIDLIST* pk_RootPIDL = NULL; // NULL -> Root = Desktop
SHGetSpecialFolderLocation(h_Owner, s32_CsidlRoot, &pk_RootPIDL);
BROWSEINFOW k_Info = {0};
k_Info.hwndOwner = h_Owner;
k_Info.pidlRoot = pk_RootPIDL;
k_Info.lpszTitle = u16_Title;
k_Info.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI;
if (u16_Preselect)
{
k_Info.lpfn = FolderBrowserCallback;
k_Info.lParam = (LPARAM)u16_Preselect;
}
// DO NOT DISABLE Wow64FsRedirection HERE !!
LPITEMIDLIST pk_IDlist = SHBrowseForFolderW(&k_Info);
if (pk_IDlist)
{
SHGetPathFromIDListW(pk_IDlist, u16_PathOut);
CoTaskMemFree(pk_IDlist);
}
CoTaskMemFree(pk_RootPIDL);
}

Using WM_SHOWWINDOW to Show a Window instead of ShowWindow()

I’m trying to use the SendMessage function of a hotkey utility (or NirCMD, etc.) to get a hidden window to pop up. I can for example get windows to close by sending 0x0010 (WM_CLOSE), but when I try sending 0x0018 (WM_SHOWWINDOW) with a wParam of 1 and an lParam of 0, nothing happens.
I’ve looked around, and the few places where someone complained that WM_SHOWWINDOW did not work, they happily took the suggestion to use ShowWindow() instead.
However I don’t have ShowWindow() available; I can only send Windows messages. But ShowWindow() is not magic, surely it works by SendMessage-ing a WM_SHOWWINDOW or something under the covers.
How can I get a window to display itself by sending it a message?
Thanks.
Try these two messages:
SendMessage(h,WM_SYSCOMMAND,SC_MINIMIZE,0);
SendMessage(h,WM_SYSCOMMAND,SC_RESTORE,0);
Or if using 3rd party apps is ok, try cmdow
WM_SHOWWINDOW is a notification, not a command. From MSDN:
The WM_SHOWWINDOW message is sent to a window when the window is about to be hidden or shown.
I don't believe there is any message that you can use to make a window show itself. Actually, the very idea seems a little strange to me. The window manager is the system component responsible for showing and hiding windows. To show a window, you must use one of the window manager APIs.
I think there is no way to achieve that with SendMessage (WM_SYSCOMMAND didn't work for me). I tried actually in C#. You click the button, the window will be minimized via ShowWindow() and then you can see what messages are sent:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public class Form1: Form
{
[DllImport("user32.dll", SetLastError = true)]
public static extern bool ShowWindow(IntPtr window, int showCommand);
private const int SW_MINIMIZE = 6;
private bool print = false;
public Form1()
{
Button button = new Button();
button.Click += onButtonsClick;
Controls.Add(button);
}
private void onButtonsClick(object sender, EventArgs e)
{
print = true;
ShowWindow(Handle, SW_MINIMIZE);
print = false;
}
protected override void WndProc(ref Message m)
{
if (print)
Console.WriteLine(m.Msg.ToString() + "\t0x" + m.Msg.ToString("x4") + "\t" + m.WParam + "\t" + m.LParam);
base.WndProc(ref m);
}
}
}

MFC - execute code right after dialog is shown (.NET equivalent of Form.Shown)

I'm doing some small changes to C++ MFC project. I'm .NET developer so Windows programming is new to me.
I need to launch some method right after CDialog is completely shown (painted) for the first time, but only once.
How can I do this? In .NET I would handle Form.Shown event.
Do I need to handle some message? Which?
Do I need to override some CDialog method?
Or is there no easy way? I'm thinking of handling WM_ACTIVATE and then using a flag to ensure I call another method only once.
Found the answer here: Waiting until the dialog box is displayed before doing something
Short story:
INT_PTR CALLBACK
DlgProc(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
switch (uiMsg) {
case WM_INITDIALOG:
return TRUE;
case WM_WINDOWPOSCHANGED:
if ((((WINDOWPOS*)lParam)->flags & SWP_SHOWWINDOW) &&
!g_fShown) {
g_fShown = TRUE;
PostMessage(hwnd, WM_APP, 0, 0);
}
break;
case WM_APP:
MessageBox(hwnd,
IsWindowVisible(hwnd) ? TEXT("Visible")
: TEXT("Not Visible"),
TEXT("Title"), MB_OK);
break;
case WM_CLOSE:
EndDialog(hwnd, 0);
break;
}
return FALSE;
}
If you're using MFC like I am you'll need to map WM_WINDOWPOSCHANGED and then use ON_MESSAGE to map WM_APP. See this CodeProject article for more details.
For reference you don't need to override DlgProc to intercept WM_WINDOWPOSCHANGED.
ON_WM_WINDOWPOSCHANGED()
ON_MESSAGE(MyCDialog::MY_USER_MSG, OnDialogShown)
void MyCDialog::OnWindowPosChanged(WINDOWPOS *wndpos)
{
__super::OnWindowPosChanged(wndpos);
if (!mDialogShown && (wndpos->flags & SWP_SHOWWINDOW)) {
PostMessage(MY_USER_MSG);
mDialogShown = true;
}
}
LRESULT MyCDialog::OnDialogShown(WPARAM, LPARAM)
{
...
}
You can implement the handling inline instead of posting another message if appropriate.
Another approach I've used a number of times with great success is to use a timer. Set it for 10m0s. It'll only fire after the dialog is shown.
Hell put the code in OnPaint() and thow a bool m_fullyInitilized in your class.
I like the timer too.. Though I usually go with 100ms. I also move all my initilization code out of the oninit, in these cases.. Just to protect against too much processing in there.

Resources