Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

CButton casts

10 views
Skip to first unread message

Stephen Howe

unread,
Mar 25, 2004, 3:10:46 PM3/25/04
to
In the Microsoft help for CWnd::GetDlgItem() I see

>>>>>>>>>>>>>>>>>>>
Remarks
The pointer returned is usually cast to the type of control identified by
nID.

Example
// uses GetDlgItem to return a pointer to a user interface control
CEdit* pBoxOne;
pBoxOne = (CEdit*) GetDlgItem(IDC_EDIT1);
GotoDlgCtrl(pBoxOne);

>>>>>>>>>>>>>>>>>>>

yet in Jeff Prosise's MFC book, 2nd edition on page 403, I see Jeff damning
Microsfot's cast technique.

>>>>>>>>>>>>>>>>>>>
But consider the following code sample:

CListBox* pListBox = (CListBox*)GetDlgItem (IDC_LIST)*
pListBox->AddString (_T("One"));
pListBox->AddString (_T("Two"));
pListBox->AddString (_T("Three"));

This code works, but only because MFC is specifically architectured to make
it work.
Because GetDlgItem returns a CWnd pointer, casting it CListBox pointer and
calling a CListBox function through it it, is poor programming practice at
best and dangerous at worst. In fact, in some situations, this technique
simply won't work.
>>>>>>>>>>>>>>>>>>>

I don't like the casts either and I am wondering under what circumstances
"this technique" will fail. Obviously, if the item is not a ListBox at all,
then it will fail. But what if it is a ListBox?

Last night I was inspecting the CWnd derived classes to see if they carry
any class-specific data and I could not see any (because if they did, where
would that be stored?)

Any advice on this matter?

Thanks

Stephen Howe


Jonathan Wood

unread,
Mar 25, 2004, 3:23:57 PM3/25/04
to
I understand Jeff's point about the dangers of this approach. But it seems
that MFC will ensure you get a CListBox pointer when the control is a
listbox. I've used this technique for many years and never had a problem.

--
Jonathan Wood
SoftCircuits
http://www.softcircuits.com
Available for consulting: http://www.softcircuits.com/jwood/resume.htm


"Stephen Howe" <stephenPOINThoweATtns-globalPOINTcom> wrote in message
news:e$iUEVqEE...@tk2msftngp13.phx.gbl...

Frank Hickman

unread,
Mar 25, 2004, 3:34:39 PM3/25/04
to
"Stephen Howe" <stephenPOINThoweATtns-globalPOINTcom> wrote in message
news:e$iUEVqEE...@tk2msftngp13.phx.gbl...
> In the Microsoft help for CWnd::GetDlgItem() I see
>
> >>>>>>>>>>>>>>>>>>>
> Remarks
> The pointer returned is usually cast to the type of control identified by
> nID.
>
> Example
> // uses GetDlgItem to return a pointer to a user interface control
> CEdit* pBoxOne;
> pBoxOne = (CEdit*) GetDlgItem(IDC_EDIT1);
> GotoDlgCtrl(pBoxOne);
>
> >>>>>>>>>>>>>>>>>>>
>
> yet in Jeff Prosise's MFC book, 2nd edition on page 403, I see Jeff
damning
> Microsfot's cast technique.

There are 6 in one hand, half-dozen in the other. I don't necessarily like
casting either but sometimes it's just the easiest way to accomplish the
task. Remember that CWnd is just a class that basically wraps an HWND
providing easier methods of invoking Window's APIs. Don't get me wrong,
that's not all it does but it is basically true.

>
> >>>>>>>>>>>>>>>>>>>
> But consider the following code sample:
>
> CListBox* pListBox = (CListBox*)GetDlgItem (IDC_LIST)*
> pListBox->AddString (_T("One"));
> pListBox->AddString (_T("Two"));
> pListBox->AddString (_T("Three"));
>
> This code works, but only because MFC is specifically architectured to
make
> it work.
> Because GetDlgItem returns a CWnd pointer, casting it CListBox pointer
and
> calling a CListBox function through it it, is poor programming practice at
> best and dangerous at worst. In fact, in some situations, this technique
> simply won't work.
> >>>>>>>>>>>>>>>>>>>

What?!? If the programmer does not know that IDC_LIST is a ListBox then
perhaps they shouldn't be programming. What is dangerous about this cast?

>
> I don't like the casts either and I am wondering under what circumstances
> "this technique" will fail. Obviously, if the item is not a ListBox at
all,
> then it will fail. But what if it is a ListBox?

Then there is no problem. I have never run accross any situation where this
would fail...that does not mean there isn't one out there, perhaps a low
memory condition (it's a temporary pointer) or something like that.

>
> Last night I was inspecting the CWnd derived classes to see if they carry
> any class-specific data and I could not see any (because if they did,
where
> would that be stored?)
>
> Any advice on this matter?

Casting a CWnd pointer to an appropriate derived class is okay as long as
the cast represents the proper window class.

>
> Thanks
>
> Stephen Howe
>
>

My 2 cents,
Frank


Doug Harrison [MVP]

unread,
Mar 25, 2004, 3:34:07 PM3/25/04
to
Stephen Howe wrote:

It the control is bound to an MFC object of type X, GetDlgItem will return a
pointer to the CWnd part of that object, which you can safely cast to the
derived type X. Otherwise, it will create a temporary CWnd and return a
pointer to it.

>Last night I was inspecting the CWnd derived classes to see if they carry
>any class-specific data and I could not see any (because if they did, where
>would that be stored?)

If the derived class did define new data members or virtual functions, and
you haven't bound the corresponding Windows control to an MFC object of the
right type, and you cast the temporary CWnd* returned by GetDlgItem to that
type, and you access those variables or call those new virtual functions,
you will have a type of buffer overrun.

>Any advice on this matter?

Here are excerpts from past messages on the subject:

*****

Also, you'd likely find things easier if you used ClassWizard to establish a
control variable for your combobox instead of using GetDlgItem and casting.
(The cast, BTW, is bogus unless a CComboBox object is bound to your control,
because otherwise, GetDlgItem returns a CWnd* which points to a temporary
CWnd, not a CComboBox at all. The cast (mostly) works here because the
layouts of CComboBox and CWnd are sufficiently compatible.)

To be clear, I'm talking about treating a CWnd as a CComboBox, when it's
just a CWnd. You can expose this "fraud" through RTTI, either MFC-style or
language-level, or by sending messages which CComboBox handles differently
than CWnd, if there are any. That's my "mostly". Other things such as
accessing data members or calling virtual functions do work, because AFAIK,
CComboBox adds no data members, nor does it add virtual functions, but of
course you have potential differences in function behavior, just like with
messages. So the object layouts are sufficiently compatible for the hack to
mostly work, modulo things like the RTTI and SendMessage issues. However,
you can quickly get into trouble when you start treating your own classes or
3rd party classes which aren't such thin wrappers so cavalierly. So I urge
anyone who must use GetDlgItem for some reason to turn on C++ RTTI and use
dynamic_cast or the analogous MFC feature to ensure the downcast is
legitimate. Of course, to be legitimate, the control has to be bound to a
variable of the proper type, and if you're already binding most or all of
your controls, the need to use GetDlgItem mostly evaporates.

--
Doug Harrison
Microsoft MVP - Visual C++

Scott McPhillips [MVP]

unread,
Mar 25, 2004, 3:39:17 PM3/25/04
to
Stephen Howe wrote:
> ...GetDlgItem...

> I don't like the casts either and I am wondering under what circumstances
> "this technique" will fail. Obviously, if the item is not a ListBox at all,
> then it will fail. But what if it is a ListBox?
>
> Last night I was inspecting the CWnd derived classes to see if they carry
> any class-specific data and I could not see any (because if they did, where
> would that be stored?)
>
> Any advice on this matter?
>
> Thanks
>
> Stephen Howe

Casting GetDlgItem is error-prone and not typesafe. In addition to the
obvious opportunities to fail, we often get posts here from someone who,
despite the documented warning, saved the returned pointer for use later
in other functions. But the CWnd being pointed to is deleted behind
your back at a rather unpredictable time. This leads to exciting symptoms!

One also sees examples of folks trying to validate the cast, such as
dynamic_cast or IsKindOf. These fail. The cast is actually invalid by
the rules of C++, and only works due to MFC implementation details.

The "advice on this matter" is don't use GetDlgItem, use control member
variables. They are elegant, typesafe, more efficient, and they make
the code much more readable.

--
Scott McPhillips [VC++ MVP]

James Ryan

unread,
Mar 25, 2004, 4:01:18 PM3/25/04
to

"Scott McPhillips [MVP]" <scot...@mvps.org.nothere> wrote in message
news:%23SMxomq...@TK2MSFTNGP11.phx.gbl...
>
...

> One also sees examples of folks trying to validate the cast, such as
> dynamic_cast or IsKindOf. These fail. The cast is actually invalid by
> the rules of C++, and only works due to MFC implementation details.
>

...


> --
> Scott McPhillips [VC++ MVP]
>

Now that is interesting! Can you explain further?

James


Stephen Howe

unread,
Mar 25, 2004, 4:48:20 PM3/25/04
to
> The "advice on this matter" is don't use GetDlgItem, use control member
> variables. They are elegant, typesafe, more efficient, and they make
> the code much more readable.

Right. I have been exploring the possibility of using this. I imagine you
are talking about using DDX_Control, right?

My snag with all this DDX stuff is this:

I have a fairly complex dialog box where different bits are disabled/enabled
or change depending on what is chosen. I created various CString and int
member variables which mirror the dialog controls and are in
DoDataExchange(). Obviously they are sync'ed in the right direction in
OnInitDialog() and OnOk(). But in other OnXXX() message handlers, I only
need to enable/disable 1 or 2 members and calling UpdateData() seems
overkill for 40-50 controls every time 1-2 of them change. DDX seems fine if
your dialog controls have no interaction, and in that scenario, it is a 100%
winner. But throw in some complex dialog interaction, and it no longer clear
what is "best practice".

What to do?

So I manually updated member variables where they changed and the control.
And that is where GetDlgItem() (and SetDlgItemText()) came in use.

But this seems hard work and not elegant. These member variables which
"shadow" the contents of controls seem more of a burden than a help.
Thinking about it, I marginally prefer the non-DDX approach in directly
reading/writing to controls and not bothering with any CString and int
member variables to reduce the lines of code. I only have 1 thing to update
rather than 2. Of course, if use UpdateData(), then it reduces to 1 thing,
but now, all controls are touched.

It seems you are shafted, whichever route you go :-(
What a bummer.

UpdateData(FALSE) seems least of the evils, as no users I have seen runs
stopwatches on how fast dialog boxes run :-). So it is acceptable.

Now these DDX Controls. If I update one of the DDX Controls, the dialog on
screen won't change, will it? I assume, as before, it will only change if I
call UpdateData(FALSE);
Which means, apart from type-safety (I don't think badly of this - I would
like if possible as well as succinct code), I have not gained anything.

Cheers

Stephen Howe


Stephen Howe

unread,
Mar 25, 2004, 4:54:56 PM3/25/04
to
> Now that is interesting! Can you explain further?

Read the end of Doug Harrison's reply. The CButton, CEdit casts are relying
on layout-compatibility with CWnd. If there was multiple inheritance or
virtual base classes involved, MS's technique would fail as a C pointer cast
is equivalent to reinterpret_cast(), i.e. all bets are off.

Stephen Howe


Dan Bloomquist

unread,
Mar 25, 2004, 5:22:52 PM3/25/04
to

You have a misconception about a control member. It is the control. Take:
CEdit m_edit1;

if you m_edit.SetWindowText( "text" ); it will show up immediately in
the dialog. And you didn't have to jump through the GetDlgItem() stuff.

Don't use UpdateData(), that quickly turns into a can of worms. If you
need to change states, add event handlers for the controls that trigger
a change of state. It keeps your 'state machine' compartmentalized, so,
much easier to maintain.

Yes, there are times that not using UpdateData() is applicable. But if
used it should only be as intended. See Joe's web pages. I'm not partial
to his backgrounds but the content is excellent.

Best, Dan.

--
http://lakeweb.net
http://ReserveAnalyst.com

Scott McPhillips [MVP]

unread,
Mar 25, 2004, 11:06:16 PM3/25/04
to
James Ryan wrote:
>>One also sees examples of folks trying to validate the cast, such as
>>dynamic_cast or IsKindOf. These fail. The cast is actually invalid by
>>the rules of C++, and only works due to MFC implementation details.
>>
>
> Now that is interesting! Can you explain further?
>
> James

CEdit and CWnd objects are indistinguishable in memory. They each
consist only of a member HWND and they each implement their member
functions by calling SendMessage with the HWND. So you can "get away"
with calling a CEdit member function with a CWnd pointer. The CWnd
object has the same data in the same place that a CEdit object would if
it existed. The (CEdit*) cast does nothing except tell the compiler to
shut up and do as it's told.

Scott McPhillips [MVP]

unread,
Mar 25, 2004, 11:28:03 PM3/25/04
to
Stephen Howe wrote:
> Now these DDX Controls. If I update one of the DDX Controls, the dialog on
> screen won't change, will it? I assume, as before, it will only change if I
> call UpdateData(FALSE);
> Which means, apart from type-safety (I don't think badly of this - I would
> like if possible as well as succinct code), I have not gained anything.
>
> Cheers
>
> Stephen Howe
>
>

You misunderstand the difference between DDX_Control and the DDX
functions that map to variables like DDX_Text. DDX_Control does not use
UpdateData. Calling a control member variable, such as
m_edit.SetWindowText WILL change the screen.

Your criticisms of UpdateData are valid. These criticisms do not apply
to DDX_Control variables.

Doug Harrison [MVP]

unread,
Mar 26, 2004, 1:03:20 AM3/26/04
to
Stephen Howe wrote:

>Right. I have been exploring the possibility of using this. I imagine you
>are talking about using DDX_Control, right?
>
>My snag with all this DDX stuff is this:
>
>I have a fairly complex dialog box where different bits are disabled/enabled
>or change depending on what is chosen. I created various CString and int
>member variables which mirror the dialog controls and are in
>DoDataExchange(). Obviously they are sync'ed in the right direction in
>OnInitDialog() and OnOk(). But in other OnXXX() message handlers, I only
>need to enable/disable 1 or 2 members and calling UpdateData() seems
>overkill for 40-50 controls every time 1-2 of them change. DDX seems fine if
>your dialog controls have no interaction, and in that scenario, it is a 100%
>winner. But throw in some complex dialog interaction, and it no longer clear
>what is "best practice".
>
>What to do?

UpdateData has got nothing to do with enabling/disabling controls. See this
message for an explanation of UpdateData and DDX/DDV:

http://groups.google.com/groups?selm=2gadivke7i40q160u450tg2fprda23besl%404ax.com

>So I manually updated member variables where they changed and the control.
>And that is where GetDlgItem() (and SetDlgItemText()) came in use.

Unfortunately, that's what you need to do in order to manipulate individual
controls. MFC's DDX/DDV system is not friendly to that; it's pretty much
limited to UpdateData, which is all controls at once or nothing.

>But this seems hard work and not elegant. These member variables which
>"shadow" the contents of controls seem more of a burden than a help.

Thinking of them as shadowing the controls implies a far closer relationship
than is usually practical to maintain. It's better to think of the data
variables as valid at certain well-defined points in the life cycle of the
dialog; I discuss that more in the message I linked to above. The values are
quite, well, valuable, because they allow you to write:

if (dlg.DoModal() == IDOK)
{
// Access the final values
}

>Thinking about it, I marginally prefer the non-DDX approach in directly
>reading/writing to controls and not bothering with any CString and int
>member variables to reduce the lines of code. I only have 1 thing to update
>rather than 2. Of course, if use UpdateData(), then it reduces to 1 thing,
>but now, all controls are touched.
>
>It seems you are shafted, whichever route you go :-(
>What a bummer.
>
>UpdateData(FALSE) seems least of the evils, as no users I have seen runs
>stopwatches on how fast dialog boxes run :-). So it is acceptable.

As discussed in the message I linked to above, UpdateData(false) should only
be called when you know for sure all the data variables are valid. During
the lifetime of the dialog, it's possible for the member variables to get
out of sync with the controls, possibly due to validation failing part of
the way through, leaving the variables with a mix of old and new values.

>Now these DDX Controls. If I update one of the DDX Controls, the dialog on
>screen won't change, will it? I assume, as before, it will only change if I
>call UpdateData(FALSE);
>Which means, apart from type-safety (I don't think badly of this - I would
>like if possible as well as succinct code), I have not gained anything.

I'm not sure what you mean by a "DDX Control". The DDX functions you find in
DoDataExchange do two things. Some associate MFC objects with HWNDs, and
others transfer data between member data and the controls. The MFC objects
have types like CEdit, CListBox, etc, and they're members of your dialog
class. So, when you call m_edit.SetWindowText("whatever"), you set the text
for the control, and the control will update the screen the next time it
paints itself.

Joseph M. Newcomer

unread,
Mar 26, 2004, 11:30:56 AM3/26/04
to
I consider using GetDlgItem to be a design error. You can avoid the whole problem by using
control variables. See my essay "Avoiding GetDlgItem". So the answer is, you don't have a
problem if you don't use that technique.

They store information (see IsKindOf, DECLARE_DYNAMIC, DECLARE_DYNCREATE), but you should
not be using it, because you should not be using GetDlgItem.
joe

On Thu, 25 Mar 2004 20:10:46 -0000, "Stephen Howe" <stephenPOINThoweATtns-globalPOINTcom>
wrote:

Joseph M. Newcomer [MVP]
email: newc...@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm

Stephen Howe

unread,
Mar 27, 2004, 9:07:51 AM3/27/04
to
> I consider using GetDlgItem to be a design error. You can avoid the whole
problem by
> using control variables. See my essay "Avoiding GetDlgItem".

I have seen, and swapped. Thanks

> So the answer is, you don't have a problem if you don't use that
technique.

Oh yes, I do. It has it's problems.

In the traditional

>>>>>>>>>>>>>>>>>>>>>>>>>
CMyDialog dlg;

// Set dialog members here (or develop the constructor with extra
parameters - the C++ way: why bother initialising things twice?)
dlg.DoThis(var1);
dlg.DoThat(var2);
:

if (dlg.DoModal() == IDOK)
{
newvar1 = dlg.GetThis();
newvar2 = dlg.GetThat();
:
}
>>>>>>>>>>>>>>>>>>>>>>>>>

with

dlg.DoThis(var1);

I cannot directly intialise a control variable, because DoDataExchange()
which calls DDX_Control() has not been called; DoDataExchange() has not
been called because OnInitDialog() has not been called; OnInitDialog() has
not been called because it is only called when DoModal() is called. You
can't set any of this earlier as the hWnd of the control member variables
like CEdit, ClistBox is still 0.

So it seems it is necessary to intialise some temporary member variables
using
dlg.DoThis(var1);
so that in OnInitDialog() I can now initialise the _real_ control member
variables in OnInitDialog() - in essence a piggy back operation is required.

This seems a complete botch up.

What would be ideal would be

>>>>>>>>>>>>>>>>>>>>>>>>>
CMyDialog dlg(var1, var2, var3, ..., pParentWnd);

// and at this point, anything static in the dialog box is already
intialised, including all member controls. That would mean, that in your
article, there would be no need to test in On... Message handlers and
returning - if it is still in the initalisation phase, it would have been
done. The OnInitDialog() would be reserved for anything dynamic, not yet
initialised - it would be mostly empty, only CDialog::OnInitDialog() would
need calling.

if (dlg.DoModal() == IDOK)
{
newvar1 = dlg.GetThis();
newvar2 = dlg.GetThat();
:
}
>>>>>>>>>>>>>>>>>>>>>>>>>

Stephen Howe


Stephen Howe

unread,
Mar 27, 2004, 9:09:49 AM3/27/04
to
> UpdateData has got nothing to do with enabling/disabling controls.

I know. Sorry for the confusion. It is purely a mechansism for sync'ing
dailog box members and associated CString,int variables.

Thanks for the help. Appreciated.

Stephen Howe


Jeff Partch [MVP]

unread,
Mar 27, 2004, 11:33:27 AM3/27/04
to
I'm not sure if I understand your current complaint, Stephen, so perhaps Joe
can do a better job. What it seems like you're missing is that you can have
both a DDX_Control and DDX_<somekindof>Data for the same child. Whatever
initialization and retrieval you were using DDX for without DDX_Control can
still be used, and you lose nothing in that respect. What you gain from
DDX_Control is that you no longer need to call GetDlgItem in those places
where you previously did so.
--
Jeff Partch [VC++ MVP]


Stephen Howe

unread,
Mar 27, 2004, 1:39:29 PM3/27/04
to
> I'm not sure if I understand your current complaint, Stephen, so perhaps
Joe
> can do a better job.

It is just my gripes :-).
Griping is a way of life in England.
It is no big deal, I can live with it.

> What it seems like you're missing is that you can have
> both a DDX_Control and DDX_<somekindof>Data for the same child.

Which is effectively what I have done. Mind you, most of the time, you don't
need
<somekindof>Data variable, as you can get things of out the equivalent
control (and unless you have called UpdateData, you had better as
<somekindof>Data won't be uptodate until IDOK has been clicked).

Stephen

Stephen Howe

unread,
Mar 28, 2004, 4:43:12 PM3/28/04
to
> I consider using GetDlgItem to be a design error.
> You can avoid the whole problem by using
> control variables. See my essay "Avoiding GetDlgItem". So the answer is,
you don't have a
> problem if you don't use that technique.

I have seen your essay. And this Sunday night I would say, "I consider using
Control variables gives you very little, almost nothing".

In the design idiom

>>>>>>>>>>>>>>>>>>>>>>>>>
CMyDialog dlg;

// Set dialog members here (or develop the constructor with extra
parameters - the C++ way: why bother initialising things twice?)
dlg.DoThis(var1);
dlg.DoThat(var2);
:

if (dlg.DoModal() == IDOK)
{
newvar1 = dlg.GetThis();
newvar2 = dlg.GetThat();
:
}
>>>>>>>>>>>>>>>>>>>>>>>>>

dlg.GetThis(); cannot be written in terms of retrieving from Control
variables, because by the time DoModal() returns, all of them are destroyed,
the hWnds are now 0 again.

Question: What does control variables give you?
Answer: Only cast-free, type safe easy manipulation of controls (and only
after Dialog::OnInitDialog has been called). But _THAT_ is all.

(i) You cannot write to these controls or statically bind them before
DoModal is called
(ii) You cannot read from these controls after DoModal is called (as I found
out tonight)

I don't think these are unreasonable expections as I am doing so within the
scope of the dialog's lifetime. It is not normal to have a C++ object where
effectively it is only "partially- alive" and if effect, members of the
object are alive, just for a limited period of time (while DoModal() is
active).

So that means, I am forced to copy the contents out every control I am
interested in, into CString and int variables. Both

dlg.DoThis(var1);
dlg.DoThat(var2);
:


newvar1 = dlg.GetThis();
newvar2 = dlg.GetThat();

have to be written in terms of CString and int variables. This is is
inefficient.

Stephen Howe


0 new messages