Solutions for Developers, Services for Companies, Software for Users
 



bottrap link

A blog is now available Here.

Interested in becoming an Affiliate and earning 25% of all generated sales?  Click Here.

Handmade Art and
Treasures!

Handmade Art and Treasures: Fannys Follies

25% off for first-time customers at VistaPrint!

 

  

MFC No-No #1: Using CWnd::GetDlgItem Incorrectly
(or, what the hell is a CTempWnd object?)

By James R. Twine (Copyright 1998-2003, James R. Twine)

[Previous Article In Segment] [Next Article In Segment]

Given the following classes:

class    CBase
{
public:
    /**/ CBase() {};

    void BaseFunc() {};
};

class    CDerived : public CBase
{
public:
    /**/ CDerived() {};

    void DerivedFunc() { m_iDerivedValue = 0xFFFFFFFF };

    int  m_iDerivedValue;
}

Would you ever write code like this:

void CallDerived( CBase *pBase )
{
    CDerived *pDerived = (CDerived*)pBase;

    pDerived -> DerivedFunc();
    return;
}

Of course not.  And why not?  Because you have not guaranteed that the CBase pointer you received is REALLY a CDerived pointer before you casted it into one.  If the CBase object you were passed was never a CDerived object in the first place, the call to CDerived::DerivedFunc would likely corrupt memory or crash because it tries to manipulate the memory of a variable (CDerived::m_iDerivedValue) that never existed!

However, in the world of MFC, it is very common to see something like:

void OnButtonPushed()
{
    CEdit *pEdit = (CEdit*)GetDlgItem( IDC_EDIT );

    pEdit ->SetWindowText( "Button Pushed!" );   
    return;
}

So what is the deal?  The problem here is more evidence of the "it works, so it must be correct!" syndrome.  Basically, whenever you do this with a CWnd-derived class, you are basically getting lucky.  Functions like SetWindowText(...) do not manipulate the state (data members) of the object, and are actually just inline wrappers for a call to SendMessage(...) with the appropriate parameters.

Want proof that the pointer returned by GetDlgItem(...) is not a "real" CEdit?  QuickWatch it and see what kind of instance it points to.  Or, turn on RTTI and try this:

void OnButtonPushed()
{
    CEdit *pEdit = NULL;
    CWnd  *pWnd = GetDlgItem( IDC_EDIT );

    pEdit = dynamic_cast< CEdit* >( pWnd );

    return;
}

-And what do you think the value of pEdit is going to be after the use of the dynamic_cast operator?  If you guessed NULL, you are correct!  I am sure you can figure out why...! :)

Actually, the above code may return a "real" CEdit object under certain situations.  When you call GetDlgItem(...), MFC searches the handle map (for the current thread) for an existing object that is bound to the handle in question (in this case, the HWND of the control whose ID was passed to the GetDlgItem(...) function).

If one is not found, a temporary object is created (which in the case of HWNDs is a CTempWnd object), and this object is returned.  If an object is already bound to it, a pointer to the existing object is returned.  This temporary (heap allocated) CTempWnd object is cleaned up when the message pump starts pumping again (basically when you return from whatever function you are in).

This means that if you had already bound a CEdit class to the IDC_EDIT control (like via ClassWizard), and then tried the above code that uses GetDlgItem(...), you would get back a a pointer to a "real" CEdit object, the one that is already bound to the control.

The fact that GetDlgItem(...) can return a pointer to a temporary object raises one important tip: Do not store the pointer returned from GetDlgItem(...) in anything like a member variable or some other variable that may be used at a later time: by the time something tries to use that value, the object may have already been cleaned up.

 

 

[Previous Article In Segment] [Next Article In Segment]

The contents of this web site are Copyright ©1998-2006 by JRTwine Software, LLC. All Rights Reserved. 
Legal and Copyright Information.
bottrap link