Categories: VC/C++

Come Estendere le MessageBox con il Filtro CBT

Creare una “MessageBox” o finestra dei messaggi, è la prima nozione che si impara nella programmazione a finestre. Queste semplici finestre si utilizzano per svariati motivi, anche per il classico “Hello World”, vediamone i misteri e come è possibile estenderle oltre ogni limite. La funzione per creare una finestra dei messaggi è:

INT MessageBox(HWND hWndOwner, LPCTSTR lpszText, LPCTSTR lpszTitle, UINT uFlags);

Il primo parametro è lo handle della finestra padre, il secondo è il testo da visualizzare, il terzo è il titolo della finestra di dialogo ed il quarto è una sorta di jolly per specificare lo stile, il numero dei bottoni, l’icona, lo stato di selezione dei controlli ed i rapporti tra dialogo e resto dell’ambiente Windows.

// File di intestazione CorsoVC.h

class CMyApp : public CWinApp
{
public:
   virtual BOOL InitInstance();
};

class CMainWnd : public CFrameWnd
{
public:
   CMainWnd();

   DECLARE_MESSAGE_MAP();
   afx_msg void OnPaint();
   afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
};

// Prima Finestra con le MFC

#include <afxwin.h>
#include "CorsoVC.h"

CMainWnd::CMainWnd()
{
   Create(NULL, "Creare Message Box", WS_OVERLAPPEDWINDOW);
}

BOOL CMyApp::InitInstance()
{
   m_pMainWnd = new CMainWnd();
   m_pMainWnd->ShowWindow(m_nCmdShow);
   m_pMainWnd->UpdateWindow();

   return TRUE;
}

CMyApp theApp;

BEGIN_MESSAGE_MAP(CMainWnd, CFrameWnd)
   ON_WM_PAINT()
   ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()

void CMainWnd::OnPaint()
{
   CPaintDC dc(this);

   CRect rcWin; CPoint ptCenter;
   GetClientRect(&rcWin);
   ptCenter = rcWin.CenterPoint();

   CString strMessage(_T("Fare click all'interno \
      di questa finestra \
      per visualizzare la MessageBox"));
   CSize szText = dc.GetTextExtent(strMessage);

   dc.TextOut(ptCenter.x - szText.cx / 2,
      ptCenter.y, strMessage);
}

void CMainWnd::OnLButtonDown(UINT nFlags, CPoint point)
{
   ::MessageBox(GetSafeHwnd(),
       _T("Ciao, sono arrivata in tempo!"),
       _T("Prova MessageBox"), MB_ICONEXCLAMATION);

    CFrameWnd::OnLButtonDown(nFlags, point);
}

Come potete notare dall’esempio tutto è molto semplice, ma i problemi arrivano quando si vuole andare oltre lo standard, ad esempio creare una MessageBox con quattro pulsanti “Si/No/Annulla/Tutti”.
L’unico modo per modificare le caratteristiche interne di questo tipo di finestre è appoggiarsi ad un filtro di tipo CBT, si tratta di una funzione callback che viene invocata da Windows prima di ogni operazione fondamentale sulle finestre: creazione, spostamento, dimensionamento e distruzione. La funzione callback ha questo scheletro:

LRESULT CALLBACK CBTProc(INT iHookCode, WPARAM wParam, LPARAM lParam);

iHookCode indica il tipo di evento, wParam ed lParam puntano a dati aggiuntivi dipendenti dal tipo di evento.

// File di intestazione CorsoVC.h

...
class CMainWnd : public CFrameWnd
{
public:
   CMainWnd();

protected:
   INT MyMessageBox(HWND hWnd, LPCTSTR szText,
      LPCTSTR szTitle, UINT uFlags, bool b4 = false);
   static LRESULT CALLBACK MyBoxProc(INT iHookCode,
      WPARAM wParam, LPARAM lParam);

   DECLARE_MESSAGE_MAP();
   afx_msg void OnPaint();
   afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
};

// Prima Finestra con le MFC

...
BEGIN_MESSAGE_MAP(CMainWnd, CFrameWnd)
    ON_WM_PAINT()
   ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()

...
void CMainWnd::OnLButtonDown(UINT nFlags, CPoint point)
{
   int nRet = MyMessageBox(GetSafeHwnd(),
      _T("Ciao, provami pure!"), _T("Prova"),
      MB_YESNOCANCEL|MB_ICONEXCLAMATION, true);
   switch (nRet)
   {
   case IDYES:
      AfxMessageBox(_T("Hai premuto Si"),
          MB_ICONINFORMATION);
      break;
   case IDNO:
      AfxMessageBox(_T("Hai premuto No"),
          MB_ICONINFORMATION);
       break;
   case IDCANCEL:
      AfxMessageBox(_T("Hai premuto Annulla"),
        MB_ICONINFORMATION);
      break;
    case IDABORT:
      AfxMessageBox(_T("Hai premuto Tutti"),
        MB_ICONINFORMATION);
      break;
   }

   CFrameWnd::OnLButtonDown(nFlags, point);
}

LRESULT CMainWnd::MyBoxProc(INT iHookCode,
     WPARAM wParam, LPARAM lParam)
{
   if (HCBT_CREATEWND)
   {
       HWND hWndBox = (HWND)wParam;
       HWND hWnd = ::GetDlgItem(hWndBox, IDHELP);
       ::SetWindowText(hWnd, _T("&Tutti"));
       ::SetWindowLong(hWnd, GWL_ID, IDABORT);
   }

   return 0;
}

INT CMainWnd::MyMessageBox(HWND hWnd,
   LPCTSTR szText, LPCTSTR szTitle, UINT uFlags, bool b4)
{
   if (!b4)
      return ::MessageBox(hWnd, szText, szTitle, uFlags);

   HHOOK hook = SetWindowsHookEx(WH_CBT,
      (HOOKPROC)MyBoxProc, NULL, GetCurrentThreadId());
   int nRet = ::MessageBox(hWnd, szText, szTitle,
      uFlags|MB_HELP);
   UnhookWindowsHookEx(hook);

   return nRet;
}

Come potete notare dal codice sopra, abbiamo inserito una funzione callback che alla creazione della MessageBox viene richiamata, quindi una volta ottenuto lo handle della finestra è possibile operare come vogliamo, in particolare, in questo semplice esempio, ho cambiato l’ID del comando IDHELP e cambiato la scritta sul pulsante “Aiuto”, in pratica ho cambiato il pulsante di aiuto nel pulsante “Tutti”, non disponibile di default.

Share
Giampaolo Rossi

Sviluppatore di software gestionale da oltre 28 anni.

Recent Posts

MerciGest: il gestionale di magazzino semplice e gratuito

MerciGest è il software gestionale gratuito per Windows che semplifica la gestione del magazzino: giacenze,…

2 giorni ago

Software gestionali con SQL Server: un archivio unico, accessibile ovunque

Tutti i nostri software gestionali possono essere collegati a un database SQL Server, consentendo l’accesso…

4 giorni ago

Come iniziare ad usare MerciGest in 5 minuti

MerciGest è semplice e immediato: basta scaricare e installare il programma per iniziare subito. Al…

6 giorni ago

Cos’è MerciGest e a chi serve

MerciGest è un gestionale di magazzino per Windows pensato per negozi, piccole imprese e artigiani…

1 settimana ago

Introduzione alla Standard Template Library (STL)

La Standard Template Library (STL) è una delle componenti più potenti del C++. Fornisce una…

7 mesi ago

Un Abbonamento per Tutti i Software

Sono arrivato alla convinzione che un abbonamento per tutti i miei software gestionali sia il…

2 anni ago