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.