Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

WTL bugs

0.00/5 (No votes)
27 Nov 2000 8  
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




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 + Samples
IDevResource.Com WTL Bugs and Issues
World of ATL, bugs and fixes page
DISCUSS.MICROSOFT.COM Mailing List Archives
microsoft.public.vc.atl newsgroup

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here