PRB: OnCtlColor Not Called When Using CTL3D.DLLID: Q109264
|
The Microsoft Developer Network (MSDN) CD contains a dynamic-link library
(DLL) called CTL3D.DLL. This DLL provides functions that allow 3-D painting
of Windows controls to give applications a 3-D look. If an application uses
the auto-subclassing feature of CTL3D.DLL, the OnCtlColor() handlers for
CDialogs and CFormViews won't be called.
Failure to handle the WM_CTLCOLOR message can cause problems with controls,
especially VBX controls.
When an application uses Ctl3dAutoSubclass(), a Windows hook is created to trap the creation process for dialog boxes. This hook will subclass the window procedure for the dialog box and call the Ctl3dDlgProc() function. The following are a few lines of code from the hook function as shown in the CTL3D documentation:
BOOL fSubclass;
fSubclass=fTrue;
SendMessage((HWND) hwndHookDlg, WM_DLGSUBCLASS,0,
(LPARAM)(WORD FAR *) &fSubclass);
if (fSubclass)
{
SubclassWindow((HWND) hwndHookDlg, (FARPROC) Ctl3dDlgProc);
}
NOTE: A WM_DLGSUBCLASS message is sent to a dialog box before it is
subclassed. If the message returns a FALSE value in the address pointed to
by the LPARAM parameter, the dialog box is not subclassed by CTL3D and thus
the dialog box doesn't automatically get a 3-D look. If the fSubclass value
is not changed, the dialog box is automatically subclassed and receives a 3-
D look.
case WM_CTLCOLOR:
(FARPROC) lpfnDlgProc = (FARPROC)
GetWindowLong(hwnd, DWL_DLGPROC);
if (lpfnDlgProc == NULL) {
hBrush = Ctl3dCtlColorEx(wm,wParam,lParam);
}
else {
hbrush = (HBRUSH) (*lpfnDlgProc)(hwnd,wm,wParam,lParam);
if (hbrush == (HBRUSH) fFalse ||
hbrush == (HBRUSH) 1)
hbrush = Ctl3dCtlColorEx(wm, wParam, lParam);
}
if (hbrush != (HBRUSH) fFalse)
return (LRESULT) hbrush;
The code that handles the WM_CTLCOLOR message doesn't call the window
procedure for the dialog box, but instead calls the dialog box procedure.
That is, GetWindowLong() is called with DWL_DLGPROC rather than
GWL_WNDPROC. CDialog and CFormView objects trap most dialog box messages by
subclassing the window procedure (not dialog box procedure) associated with
a dialog box. Because CTL3D's window procedure is called before CDialog's
or CFormView's window procedure, the WM_CTLCOLOR is never sent to the
CDialog's or CFormView's window procedure because CTL3D's WM_CTLCOLOR
message handling code calls the dialog box procedure for the dialog box and
returns without calling the next window procedure in the chain. The next
window procedure for the dialog box is CDialog's or CFormView's window
procedure. Thus, CDialog or CFormview doesn't receive the OnCtlColor()
message.
There are several techniques that can be used to prevent the problem of not
receiving the WM_CTLCOLOR message:
This section describes in more detail the last two techniques in the RESOLUTION section.
LRESULT CAboutDlg::OnDlgSubclass(UINT wParam, LONG lParam)
{
*((int FAR *)lParam)= CTL3D_NOSUBCLASS;
return 0;
}
BOOL CAboutDlg::OnInitDialog()
{
CDialog::OnInitDialog();
Ctl3dSubclassDlg(m_hWnd,CTL3D_ALL);
return TRUE;
}
HBRUSH CAboutDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
if( nCtlColor == CTLCOLOR_EDIT)
{
pDC->SetBkColor(RGB(127,127,127));
return (HBRUSH)::GetStockObject(GRAY_BRUSH);
}
CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
const MSG * pMsg=GetCurrentMessage();
return Ctl3dCtlColorEx(pMsg->message,pMsg->wParam,pMsg->lParam);
}
void CAboutDlg::OnNcPaint()
{
const MSG * pMsg=GetCurrentMessage();
::SetWindowLong(pMsg->hwnd, DWL_MSGRESULT,
Ctl3dDlgFramePaint(pMsg->hwnd,pMsg->message,
pMsg->wParam, pMsg->lParam));
}
BOOL CAboutDlg::OnNcActivate(BOOL bActive)
{
const MSG * pMsg=GetCurrentMessage();
::SetWindowLong(pMsg->hwnd, DWL_MSGRESULT,
Ctl3dDlgFramePaint(pMsg->hwnd, pMsg->message,
pMsg->wParam, pMsg->lParam));
return TRUE;
}
// The CDialog's OnCtlColor() function needs to be
// modified a little. The base class CDialog::OnCtlColor() must not
// be called (like shown above). CDialog::OnCtlColor() calls
// Default() which can get the program into a recursive loop.
// The revised version of OnCtlColor shown below must use afxDlgBrush,
// which requires the AUXDATA.H file from the \MSVC\MFC\SRC directory.
#include "c:\msvc\mfc\src\auxdata.h"
HBRUSH CAboutDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
if( nCtlColor == CTLCOLOR_EDIT)
{
pDC->SetBkColor(RGB(127,127,127));
return (HBRUSH)::GetStockObject(GRAY_BRUSH);
}
LRESULT lResult;
if (pWnd->SendChildNotifyLastMsg(&lResult))
return (HBRUSH)lResult; // eat it
if (!GrayCtlColor(pDC->m_hDC, pWnd->GetSafeHwnd(), nCtlColor,
afxDlgBkBrush, afxDlgTextClr))
return NULL; // Don't call Default() because that will
// get us into a recursive loop. The
// dialog window procedure calls the dialog
// procedure.
return afxDlgBkBrush;
}
// This is the new dialog box procedure that will be used.
// Call OnCtlColor() for any dialog boxes that need to have it.
// Be sure to define the OnCtlColor() function as public.
LRESULT FAR PASCAL _export MyDlgProc(HWND hWnd, UINT msg,
WPARAM wParam,LPARAM lParam)
{
CAboutDlg* pWnd = (CAboutDlg*)CWnd::FromHandlePermanent(hWnd);
if(msg==WM_CTLCOLOR)
return (LRESULT) (UINT) pWnd->OnCtlColor(
CDC::FromHandle((HDC)wParam),
CWnd::FromHandle((HWND)LOWORD(lParam)),
(UINT)HIWORD(lParam));
else
return ::CallWindowProc(pWnd->m_oldDlgProc, hWnd, msg,
wParam, lParam);
}
// This is a sample of how to subclass the dialog box procedure for
// a dialog box so that the dialog box procedure above can be used to
// forward the WM_CTLCOLOR message to CDialogs or CFormViews.
// m_oldDlgProc can be defined as a data member of the dialog box or
// CFormView. It is defined as:
// WNDPROC m_oldDlgProc;
BOOL CAboutDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Subclass the dialog procedure and save original procedure.
m_oldDlgProc = (WNDPROC)::SetWindowLong(GetSafeHwnd(),
DWL_DLGPROC,(LONG)&MyDlgProc);
return TRUE;
}
Additional query words: 7.00 1.00 1.50 2.00 2.50 CTRL3D
Keywords : kb16bitonly
Version :
Platform :
Issue type :
Last Reviewed: July 28, 1999