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

Un Abbonamento per Tutti i Software

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

10 mesi ago

Software di Magazzino Gratuito

MerciGest è un software per la gestione del magazzino completamente gratuito. Continua a leggere→

11 mesi ago

Mettere il PC in Lock Screen

In ufficio può capitare di doversi allontanare dal proprio posto di lavoro, ecco che allora…

3 anni ago

Fare il reset togliendo la corrente

In questo articolo vedremo quando è più o meno utile togliere la corrente ad un…

3 anni ago

Prossimi Aggiornamenti Software

Dopo la pausa invernale dovuta al lavoro che devo fare per sostentarmi, eccomi di nuovo…

3 anni ago

Come Eliminare i Files in Windows

Vediamo come eliminare i files direttamente da Windows senza utilizzare il cestino. Continua a leggere→

3 anni ago