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.