Search code examples
c++messagebox

How do I use SetTimer() to create certain number of message boxes after another in c++?


I am trying to make it so when a messagebox opens another one opens a couple seconds later without any user input (pressing the OK button). I want the old one to stay open, but a new one to appear. I also want to be able to use SetWindowsHookEx and set a limit on how many message boxes are created. I know this uses the function SetTimer() and needs some kind of if statement with the current number of dialogs that equal the limit and stop when the number is reached. So how do I make this work? I tried to research, but nothing seemed to work.

My attempt:

#include <iostream>
#include <Windows.h>
#include <string>
using namespace std;

thread_local int MsgBox_X;
thread_local int MsgBox_Y;
thread_local int Limit = 10;

static LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode == HCBT_CREATEWND)
    {
        CBT_CREATEWND* s = (CBT_CREATEWND*)lParam;
        if (s->lpcs->hwndParent == NULL)
        {
            s->lpcs->x = MsgBox_X;
            s->lpcs->y = MsgBox_Y;
        }
    }
    return CallNextHookEx(NULL, nCode, wParam, lParam);
}

int MessageBoxPos(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType, int X, int Y)
{
    HHOOK hHook = SetWindowsHookEx(WH_CBT, &CBTProc, NULL, GetCurrentThreadId());
    MsgBox_X = X;
    MsgBox_Y = Y;
    int result = MessageBox(hWnd, lpText, lpCaption, uType);
    if (hHook) UnhookWindowsHookEx(hHook);
    return result;
if (<# of dialogs> == Limit) {
    std::terminate()
    }
}

void _MessageBox()
{
    HWND HWND1;
    MessageBox(HWND1, TEXT("Message"), TEXT("MsgBox"), MB_ICONWARNING | MB_OK);
    SetTimer(HWND1, 
        TIMER1,
        2000,
        (TIMERPROC)NULL);
    case WM_TIMER:

        switch (wParam)
        {
        case TIMER1:
            MessageBoxPos(NULL, TEXT("Message 2"), TEXT("MsgBox 2"), MB_ICONWARNING | MB_OK, 50, 50);

            return 0;
        }
    }
}
int main()
{
    ShowWindow(::GetConsoleWindow(), SW_HIDE);
    _MessageBox();
}

Solution

  • You are passing an uninitialized HWND to MessageBox().

    Also, MessageBox() is a blocking function, it doesn't exit until the dialog is closed, so you need to create the timer before you call MessageBox(). Unless you use SetWindowsHookEx() or SetWinEventHook() to hook the creation of the dialog, then you can create the timer when the dialog's HWND is created, and you pass that HWND to SetTimer().

    Also, your syntax to handle the WM_TIMER message is just plain wrong.

    That being said, MessageBox() displays a modal dialog and runs its own message loop, so you don't need to handle WM_TIMER manually at all. You can assign a callback to the timer, and let the modal loop dispatch the callback events for you.

    Try something more like this:

    #include <Windows.h>
    
    thread_local int MsgBox_X;
    thread_local int MsgBox_Y;
    thread_local int NumMsgBoxes = 0;
    
    static LRESULT CALLBACK CBTSetPosProc(int nCode, WPARAM wParam, LPARAM lParam)
    {
        if (nCode == HCBT_CREATEWND)
        {
            CBT_CREATEWND* s = (CBT_CREATEWND*)lParam;
            if (s->lpcs->hwndParent == NULL)
            {
                s->lpcs->x = MsgBox_X;
                s->lpcs->y = MsgBox_Y;
            }
        }
        return CallNextHookEx(NULL, nCode, wParam, lParam);
    }
    
    int MessageBoxPos(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType, int X, int Y)
    {
        HHOOK hHook = SetWindowsHookEx(WH_CBT, &CBTSetPosProc, NULL, GetCurrentThreadId());
        MsgBox_X = X;
        MsgBox_Y = Y;
        int result = MessageBox(hWnd, lpText, lpCaption, uType);
        if (hHook) UnhookWindowsHookEx(hHook);
        return result;
    }
    
    static void CALLBACK TimerProc(HWND, UINT, UINT_PTR, DWORD)
    {
        if (NumMsgBoxes > 0)
        {
            --NumMsgBoxes;
            MessageBoxPos(NULL, TEXT("Message 2"), TEXT("MsgBox 2"), MB_ICONWARNING | MB_OK, 50, 50);
        }
    }
    
    void _MessageBox(int NumberOfMsgBoxes)
    {
        NumMsgBoxes = NumberOfMsgBoxes;
        if (NumMsgBoxes > 0)
        {
            --NumMsgBoxes;
            UINT timer = SetTimer(NULL, 0, 2000, &TimerProc);
            if (timer) {
                MessageBox(NULL, TEXT("Message"), TEXT("MsgBox"), MB_ICONWARNING | MB_OK);
                KillTimer(NULL, timer);
            }
        }
    }
    
    int main()
    {
        ShowWindow(GetConsoleWindow(), SW_HIDE);
        _MessageBox(5);
    }
    

    Alternatively:

    #include <Windows.h>
    
    thread_local int MsgBox_X;
    thread_local int MsgBox_Y;
    thread_local int NumMsgBoxes = 0;
    
    static LRESULT CALLBACK CBTSetPosProc(int nCode, WPARAM wParam, LPARAM lParam)
    {
        if (nCode == HCBT_CREATEWND)
        {
            CBT_CREATEWND* s = (CBT_CREATEWND*)lParam;
            if (s->lpcs->hwndParent == NULL)
            {
                s->lpcs->x = MsgBox_X;
                s->lpcs->y = MsgBox_Y;
            }
        }
        return CallNextHookEx(NULL, nCode, wParam, lParam);
    }
    
    int MessageBoxPos(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType, int X, int Y)
    {
        HHOOK hHook = SetWindowsHookEx(WH_CBT, &CBTSetPosProc, NULL, GetCurrentThreadId());
        MsgBox_X = X;
        MsgBox_Y = Y;
        int result = MessageBox(hWnd, lpText, lpCaption, uType);
        if (hHook) UnhookWindowsHookEx(hHook);
        return result;
    }
    
    static void CALLBACK TimerProc(HWND hwnd, UINT message, UINT_PTR idTimer, DWORD dwTime)
    {
        KillTimer(hwnd, idTimer);
        if (NumMsgBoxes > 0)
            MessageBoxPos(NULL, TEXT("Message 2"), TEXT("MsgBox 2"), MB_ICONWARNING | MB_OK, 50, 50);
    }
    
    static LRESULT CALLBACK CBTSetTimerProc(int nCode, WPARAM wParam, LPARAM lParam)
    {
        if (nCode == HCBT_CREATEWND)
        {
            CBT_CREATEWND* s = (CBT_CREATEWND*)lParam;
            if (s->lpcs->hwndParent == NULL)
            {
                if (--NumMsgBoxes > 0)
                    SetTimer((HWND)wParam, 1, 2000, &TimerProc);
            }
        }
        return CallNextHookEx(NULL, nCode, wParam, lParam);
    }
    
    void _MessageBox(int NumberOfMsgBoxes)
    {
        HHOOK hHook = SetWindowsHookEx(WH_CBT, &CBTSetTimerProc, NULL, GetCurrentThreadId());
        NumMsgBoxes = NumberOfMsgBoxes;
        if (NumMsgBoxes > 0)
            MessageBox(NULL, TEXT("Message"), TEXT("MsgBox"), MB_ICONWARNING | MB_OK);
        if (hHook) UnhookWindowsHookEx(hHook);
    }
    
    int main()
    {
        ShowWindow(GetConsoleWindow(), SW_HIDE);
        _MessageBox(5);
    }