WTL bugs





1.00/5 (1 vote)
Jun 1, 2000

302897
Known WTL & ATL bugs
This article illustrates bugs that are present in the current WTL (3.1) & ATL (3.0) implementation.
Last Modified : 11/29/2000
- WTL::CDC::DrawDragRect()
- WTL::CPropertyPageImpl::OnNotify()
- WTL::CPropertySheetImpl::OnCommand()
- WTL::CRichEdit::CharFromPos()
- WTL::DDX_TEXT and WTL::DDX_TEXT_LEN
- WTL::CCommandBarCtrlImpl::OnHookKeyDown()
- WTL::CFrameWindowImplBase::UpdateBarsPosition()
- WTL::CMDIWindow::GetStandardWindowMenu()
- ATL::CComPtr::IsEqualObject()
- ATL::IDispEventSimpleImpl::InvokeFromFuncInfo()
- ATL::CComContainedObject::QueryInterface()
- ATL::CAxHostWindow::GetDC()
AtlGDI.h, line 2355-2414, instead of:
void DrawDragRect(LPCRECT lpRect, SIZE size, LPCRECT lpRectLast, SIZE sizeLast,
HBRUSH hBrush = NULL, HBRUSH hBrushLast = NULL)
...
if(hBrush == NULL)
hBrush = CDCHandle::GetHalftoneBrush();
...
...
// cleanup DC
if(hBrushOld != NULL)
SelectBrush(hBrushOld);
SelectClipRgn(NULL);
It can be something like
void DrawDragRect(LPCRECT lpRect, SIZE size, LPCRECT lpRectLast, SIZE sizeLast, HBRUSH hBrushIn = NULL, HBRUSH hBrushLast = NULL) ... if(hBrushIn == NULL) hBrush = CDCHandle::GetHalftoneBrush(); else hBrush = hBrushIn; ... ... // cleanup DC if(hBrushOld != NULL) SelectBrush(hBrushOld); SelectClipRgn(NULL); if(NULL == hBrushIn) DeleteObject(hBrush); //Free our halftone brush DeleteObject(hRgnNew); DeleteObject(hRgnOutside); DeleteObject(hRgnInside); if (NULL != hRgnLast) DeleteObject(hRgnLast); if (NULL != hRgnUpdate) DeleteObject(hRgnUpdate);
Six memory leaks in one place.
The bug has been posted to wtl@egroups.com by Peter Datsichin
AtlDlgs.h, line 2362, instead of:
case PSN_WIZFINISH: lResult = !pT->OnWizardFinish();
It can be something like
case PSN_WIZFINISH: lResult = pT->OnWizardFinish();
If one looks at the MS documentation on PSN_WIZFINISH it is said that with version 5.80 of "comctl32.dll" you can return a window handle to 1) prevent the wizard from finishing and 2) set the focus on the window handle returned by the function. However, WTL negates the result so that returning TRUE allow the wizard to finish while returning FALSE prevent it. By doing so it is not possible to return a window handle.
Thanks to Simon-Pierre Cadieux. See comment "Wizard Property Sheet " below
AtlDlgs.h, line 2146-2153, instead of:
LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
{
LRESULT lRet = DefWindowProc(uMsg, wParam, lParam);
if(HIWORD(wParam) == BN_CLICKED && (LOWORD(wParam) == IDOK ||
LOWORD(wParam) == IDCANCEL) &&
((m_psh.dwFlags & PSH_MODELESS) != 0) && (GetActivePage() == NULL))
DestroyWindow();
return lRet;
}
It can be something like
LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { LRESULT lRet = DefWindowProc(uMsg, wParam, lParam); if(HIWORD(wParam) == BN_CLICKED && ((m_psh.dwFlags & PSH_MODELESS) != 0) && ((LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) || #if (_WIN32_IE >= 0x0500) && defined(PSH_WIZARD_LITE) ((m_psh.dwFlags & (PSH_WIZARD | PSH_WIZARD97 | PSH_WIZARD_LITE)) != 0)) && #elif (_WIN32_IE >= 0x0400) && defined(PSH_WIZARD97) ((m_psh.dwFlags & (PSH_WIZARD | PSH_WIZARD97)) != 0)) && #else ((m_psh.dwFlags & PSH_WIZARD) != 0)) && #endif (GetActivePage() == NULL)) DestroyWindow(); return lRet; }
For modeless wizard property sheet, once you clicked on the "Terminate" button the sheet should be destroyed by calling DestroyWindow. However the WTL instructions that handle that only check for IDOK and IDCANCEL not the identifier of the "Terminate Button"
Thanks to Simon-Pierre Cadieux. See comment "Wizard Property Sheet " below
AtlCtrls.h, Lines 5807-5811 instead of:
int CharFromPos(POINT pt) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, EM_CHARFROMPOS, 0, MAKELPARAM(pt.x, pt.y)); }
It can be something like
int CharFromPos(POINTL pt) const { ATLASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); }
The bug was published by Richard L. Melton
See help on EM_CHARFROMPOS for details
AtlDdx.h, lines 39-51 instead of:
#define DDX_TEXT(nID, var) \ if(nCtlID == (UINT)-1 || nCtlID == nID) \ { \ if(!DDX_Text(nID, var, sizeof(var), bSaveAndValidate)) \ return FALSE; \ } #define DDX_TEXT_LEN(nID, var, len) \ if(nCtlID == (UINT)-1 || nCtlID == nID) \ { \ if(!DDX_Text(nID, var, sizeof(var), bSaveAndValidate, TRUE, len)) \ return FALSE; \ }
It can be something like
#define DDX_TEXT(nID, var) \ if(nCtlID == (UINT)-1 || nCtlID == nID) \ { \ if(!DDX_Text(nID, var, sizeof(var)/sizeof(var[0]), \ bSaveAndValidate)) \ return FALSE; \ } #define DDX_TEXT_LEN(nID, var, len) \ if(nCtlID == (UINT)-1 || nCtlID == nID) \ { \ if(!DDX_Text(nID, var, sizeof(var)/sizeof(var[0]), \ bSaveAndValidate, TRUE, len)) \ return FALSE; \ }
It is much better to use CString version of DDX_Text
AtlCtrlw.h Line 1639, instead of:
AtlGetCommCtrlVersion(&dwMajor, &dwMinor);
It can be something like
#ifndef _ATL_DLL AtlGetCommCtrlVersion(&dwMajor, &dwMinor); #else // Do it in some other way, there is no AtlGetCommCtrlVersion in atl.dll #endif
It is recommended to not use _ATL_DLL anyway.
The bug was published by Peter N Burgess
AtlFrame.h Lines 571-592, instead of:
void UpdateBarsPosition(RECT& rect, BOOL bResizeBars = TRUE) { // resize toolbar if(m_hWndToolBar != NULL && ((DWORD)::GetWindowLong(m_hWndToolBar, GWL_STYLE) & WS_VISIBLE)) { if(bResizeBars) ::SendMessage(m_hWndToolBar, WM_SIZE, 0, 0); RECT rectTB; ::GetWindowRect(m_hWndToolBar, &rectTB); rect.top += rectTB.bottom - rectTB.top; } // resize status bar if(m_hWndStatusBar != NULL && ((DWORD)::GetWindowLong(m_hWndStatusBar, GWL_STYLE) & WS_VISIBLE)) { if(bResizeBars) ::SendMessage(m_hWndStatusBar, WM_SIZE, 0, 0); RECT rectSB; ::GetWindowRect(m_hWndStatusBar, &rectSB); rect.bottom -= rectSB.bottom - rectSB.top; } }
It can be something like
void UpdateBarsPosition(RECT& rect, BOOL bResizeBars = TRUE) { // resize toolbar if(m_hWndToolBar != NULL && ((DWORD)::GetWindowLong(m_hWndToolBar, GWL_STYLE) & WS_VISIBLE)) { if(bResizeBars) ::SendMessage(m_hWndToolBar, WM_SIZE, 0, 0); RECT rectTB; ::GetWindowRect(m_hWndToolBar, &rectTB); if( dwStyles & CCS_VERT ) rect.left += rectTB.right - rectTB.left; else rect.top += rectTB.bottom - rectTB.top; } // resize status bar if(m_hWndStatusBar != NULL && ((DWORD)::GetWindowLong(m_hWndStatusBar, GWL_STYLE) & WS_VISIBLE)) { if(bResizeBars) ::SendMessage(m_hWndStatusBar, WM_SIZE, 0, 0); RECT rectSB; ::GetWindowRect(m_hWndStatusBar, &rectSB); rect.bottom -= rectSB.bottom - rectSB.top; // Force redraw of statusbar on top of possible vertical toolbar. if( dwStyles & CCS_VERT ) ::SetWindowPos(m_hWndStatusBar , HWND_TOP, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE ); } }
Otherwise your vertical toolbars+rebars will not showing up properly
The bug was published by Carlos A. Ferraro Cavallini
See comment below.
AtlFrame.h Lines 1073-1087, instead of:
static HMENU GetStandardWindowMenu(HMENU hMenu)
{
int nCount = ::GetMenuItemCount(hMenu);
if(nCount == -1)
return NULL;
int nLen = ::GetMenuString(hMenu, nCount - 2, NULL, 0, MF_BYPOSITION);
if(nLen == 0)
return NULL;
LPTSTR lpszText = (LPTSTR)_alloca((nLen + 1) * sizeof(TCHAR));
if(::GetMenuString(hMenu, nCount - 2, lpszText, nLen + 1, MF_BYPOSITION) != nLen)
return NULL;
if(lstrcmp(lpszText, _T("&Window")))
return NULL;
return ::GetSubMenu(hMenu, nCount - 2);
}
It can be something like
static HMENU GetStandardWindowMenu(HMENU hMenu) { int nCount = ::GetMenuItemCount(hMenu); if(nCount == -1) return NULL; int nLen = ::GetMenuString(hMenu, nCount - 2, NULL, 0, MF_BYPOSITION); if(nLen == 0) return NULL; LPTSTR lpszText = (LPTSTR)_alloca((nLen + 1) * sizeof(TCHAR)); if(::GetMenuString(hMenu, nCount - 2, lpszText, nLen + 1, MF_BYPOSITION) != nLen) return NULL; if(lstrcmp(lpszText, LOCALE_INDEPENDED_STRING)) return NULL; return ::GetSubMenu(hMenu, nCount - 2); }
or just
static HMENU GetStandardWindowMenu(HMENU hMenu) { int nCount = ::GetMenuItemCount(hMenu); if(nCount == -1) return NULL; return ::GetSubMenu(hMenu, nCount - 2); }
Similar that the author has overlooked that there are other languages except for English.
The bug was published by Toshihiro Sato
See comment below.
AtlBase.h, lines 498-511, 652, 798 instead of:
bool IsEqualObject(IUnknown* pOther) { if (p == NULL && pOther == NULL) return true; // They are both NULL objects if (p == NULL || pOther == NULL) return false; // One is NULL the other is not CComPtr<IUnknown> punk1; CComPtr<IUnknown> punk2; p->QueryInterface(IID_IUnknown, (void**)&punk1); pOther->QueryInterface(IID_IUnknown, (void**)&punk2); return punk1 == punk2; }
It can be something like
bool IsEqualObject(IUnknown* pOther) { if (p == pOther) return true; // They are both NULL objects or the same object! if (p == NULL || pOther == NULL) return false; // One is NULL the other is not CComPtr<IUnknown> punk1; CComPtr<IUnknown> punk2; p->QueryInterface(IID_IUnknown, (void**)&punk1); pOther->QueryInterface(IID_IUnknown, (void**)&punk2); return punk1 == punk2; }
It's not a real bug, but it hits the performance.
Anyway you can code something like this:
if (m_pObject == pOtherObject || m_pObject.IsEqualObject(pOtherObject))
{
// Do smth
}
AtlCom.h, lines 3731-3756 instead of:
//Helper for invoking the event
HRESULT InvokeFromFuncInfo(void (__stdcall T::*pEvent)(), _ATL_FUNC_INFO& info,
DISPPARAMS* pdispparams, VARIANT* pvarResult)
{
T* pT = static_cast<T*>(this);
VARIANTARG** pVarArgs = info.nParams ?
(VARIANTARG**)alloca(sizeof(VARIANTARG*)*info.nParams) : 0;
for (int i=0; i<info.nParams; i++)
pVarArgs[i] = &pdispparams->rgvarg[info.nParams - i - 1];
CComStdCallThunk<T> thunk;
thunk.Init(pEvent, pT);
CComVariant tmpResult;
if (pvarResult == NULL)
pvarResult = &tmpResult;
HRESULT hr = DispCallFunc(
&thunk,
0,
info.cc,
info.vtReturn,
info.nParams,
info.pVarTypes,
pVarArgs,
pvarResult);
ATLASSERT(SUCCEEDED(hr));
return hr;
}
It can be something like
//Helper for invoking the event
HRESULT InvokeFromFuncInfo(void (__stdcall T::*pEvent)(),
_ATL_FUNC_INFO& info, DISPPARAMS* pdispparams,
VARIANT* pvarResult)
{
T* pT = static_cast<T*>(this);
VARIANTARG** pVarArgs = info.nParams ?
(VARIANTARG**)alloca(sizeof(VARIANTARG*)*info.nParams) : 0;
VARTYPE * pVarTypes = info.nParams ?
(VARTYPE *)alloca(sizeof(VARTYPE)*info.nParams) : 0;
for (int i=0; i<info.nParams; i++)
{
pVarArgs[i] = &pdispparams->rgvarg[info.nParams - i - 1];
pVarTypes[i] = info.pVarTypes[info.nParams - i - 1];
}
CComStdCallThunk<T> thunk;
thunk.Init(pEvent, pT);
CComVariant tmpResult;
if (pvarResult == NULL)
pvarResult = &tmpResult;
HRESULT hr = DispCallFunc(
&thunk,
0,
info.cc,
info.vtReturn,
info.nParams,
pVarTypes,
pVarArgs,
pvarResult);
ATLASSERT(SUCCEEDED(hr));
return hr;
}
Atlcom.h, Line 2604, instead of:
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject) { HRESULT hr = OuterQueryInterface(iid, ppvObject); if (FAILED(hr) && _GetRawUnknown() != m_pOuterUnknown) hr = _InternalQueryInterface(iid, ppvObject); return hr; }
It can be something like
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject) { return OuterQueryInterface(iid, ppvObject); }
CComContainedObject shouldn't call _InternalQueryInterface()
The bug was published by World Od ATL
ATLHOST.H Line 1489, instead of:
STDMETHOD(GetDC)(LPCRECT /*pRect*/, DWORD /*grfFlags*/, HDC* phDC) { if (phDC) return E_POINTER; *phDC = CWindowImpl<CAxHostWindow>::GetDC(); return S_OK; }
It can be something like
STDMETHOD(GetDC)(LPCRECT /*pRect*/, DWORD /*grfFlags*/, HDC* phDC) { if (!phDC) return E_POINTER; *phDC = CWindowImpl<CAxHostWindow>::GetDC(); return S_OK; }
No comments. Seems to be a mistype
The bug was published by Claus Michelsen
Some useful links
Clipcode.com and Clipcode.com WTL Doc + SamplesIDevResource.Com WTL Bugs and Issues
World of ATL, bugs and fixes page
DISCUSS.MICROSOFT.COM Mailing List Archives
microsoft.public.vc.atl newsgroup