Discussion:
CEdit Control - Exception calling setwindowtext
(too old to reply)
Carl Hartman
2015-02-11 19:21:18 UTC
Permalink
I am having an issue with a CEdit control and using setwindowtext() to put text in the box. I have tried this in VS 2010, 2012, and 2013. I have a simple windows form program with an edit box. During the OnChange call I want to grab the text out of the edit box, change everything to upper case and put it back into the box. The control has been assigned to a variable:

CEdit m_edit1;

added to DoDataExchange

DDX_Control(pDX, IDC_EDIT1, m_edit1);


In the FormView cpp file example:

void CTestView::OnEnChangeEdit1()
{
CString str;
m_edit1.GetWindowTextW(str); // get the string
if(!str.IsEmpty()) // string not empty
{
str = str.MakeUpper(); // change to upper case
m_edit1.SetWindowTextW(str); // put it back
}
}

The line:

m_edit1.SetWindowTextW(str); // put it back

causes this:

First-chance exception at 0x5082D7A3 (mfc120ud.dll) in Test.exe: 0xC00000FD: Stack overflow (parameters: 0x00000001, 0x00322FFC).
Unhandled exception at 0x5082D7A3 (mfc120ud.dll) in Test.exe: 0xC00000FD: Stack overflow (parameters: 0x00000001, 0x00322FFC).

It doesn't matter if is x86, x64, debug, release compiled, it always fails. Desktop is running Win 7 Pro x64.

Is this not the correct way to set and reset text in an edit box?

CH
Woody
2015-02-12 05:17:11 UTC
Permalink
I think you have to match the string type. This is done either implicitly ("A CString object supports either the char type or the wchar_t type. Which one it supports depends on which one of the symbols, MBCS or UNICODE, is defined at compile time.") or explicitly (by CStringW str).

If this doesn't fix your problem, you should use the debugger to trace where in the CString code the error occurs.

Also, be sure you are using the correct "toolset" for compilation. VS2013 compiles for Windows 7, and won't run under Windows XP.
Uwe Kotyczka
2015-02-17 10:29:18 UTC
Permalink
Post by Carl Hartman
CEdit m_edit1;
added to DoDataExchange
DDX_Control(pDX, IDC_EDIT1, m_edit1);
void CTestView::OnEnChangeEdit1()
{
CString str;
m_edit1.GetWindowTextW(str); // get the string
if(!str.IsEmpty()) // string not empty
{
str = str.MakeUpper(); // change to upper case
m_edit1.SetWindowTextW(str); // put it back
}
}
m_edit1.SetWindowTextW(str); // put it back
First-chance exception at 0x5082D7A3 (mfc120ud.dll) in Test.exe: 0xC00000FD: Stack overflow (parameters: 0x00000001, 0x00322FFC).
Unhandled exception at 0x5082D7A3 (mfc120ud.dll) in Test.exe: 0xC00000FD: Stack overflow (parameters: 0x00000001, 0x00322FFC).
I guess you really cause a stack overflow.

In your OnEnChangeEdit1 function you get notified that the text has
been changed. The you modifiy it yourself (to upper case) and call
SetWindowsText. This causes another notification that the text has
changed. Again you modify the text (which is not really a modifaction)
and call SetWindowsText. Then you get another notification...

Just put a breakpoint into your OnEnChangeEdit1 function and watch
what happens.

I would check if the text has changed by making it uppercase
and call SetWindowText only if it really is differnt from the
original string. Something like this:

void CTestView::OnEnChangeEdit1()
{
CString strOrig, strUpperCase;
m_edit1.GetWindowTextW(strOrig); // get the string
if(!strOrig.IsEmpty()) // string not empty
{
strUpperCase = strOrig.MakeUpper(); // change to upper case
if (strUpperCase != strOrig)
m_edit1.SetWindowTextW(strUpperCase); // put it back if changed
}
}

HTH
Woody
2015-02-18 05:40:16 UTC
Permalink
Ah, yes, Uwe is correct. I have fallen for this one many times before.

While his solution will work for your case, I generally do something like

OnEditChange()
{
if(!ignoreChange)
{
...
ignoreChange=true;
SetWindowText(...);
ignoreChange=false;
}
}

and have a bool ignoreChange, initially set to false. This prevents the infinite recursion which causes the stack overflow.
Uwe Kotyczka
2015-02-18 13:26:06 UTC
Permalink
Post by Woody
Ah, yes, Uwe is correct. I have fallen for this one many times before.
While his solution will work for your case, I generally do something like
OnEditChange()
{
if(!ignoreChange)
{
...
ignoreChange=true;
SetWindowText(...);
ignoreChange=false;
}
}
and have a bool ignoreChange, initially set to false. This
prevents the infinite recursion which causes the stack overflow.
I've thought of the pattern you describe above too.
Depends of the scope, where ignoreChange "lives".

You can make ignoreChange a (C style) static variable in
OnEditChange. Or you could make it a (C++ style) static
variable of the class CTestView. Or you could make it a
true member variable of CTestView.

Consider the implcations of those three approaches.
Assuming that you have more than one instance of CTestView
both static approaches might cause strange behaviour
if the content of both edit controls change at (almost)
the same time. Or think of multitreading - changing the
content of the very same edit control from two different
threads might also lead to strange behaviour.

In general you should try to avoid raise conditions. (I
know that this is not very likely in the OP's example.)
Therefore I think my idea of breaking the stack overflow
is more straight forward.

HTH
Woody
2015-02-19 05:48:53 UTC
Permalink
Uwe, I should have been more clear; 'ignoreChange' in this method is a member of the dialog class, and you need one such variable for each edit control to be treated this way. I left out these details to make the principle of the technique clear.

As far as multi-threading, none of the proposed solutions will work anyways without additional serialization mechanisms. This is why when using MFC, you're well-advised to have threads send messages to the GUI thread, which forces the updates to be serial.

The reason I mentioned the solution at all is because in most of my cases, your technique won't work unless the change you're making has certain properties: as a counter-example, suppose you're inverting the case of the text. Then each recursive edit change sees different text (A->a->A...).
Loading...