Click to See Complete Forum and Search --> : Updating and Displaying Dialog variables from Threads


cromagnon
November 10th, 2004, 09:48 AM
I added a new dialog variable to my original dialog Application. This new dialog has an edit control. I have a button in the original dialog which, when clicked, should display and update the new dialog, while I'm doing other tasks. When I use the the UpdateData() function, the program compiles but I get an assertion failure during execution. I've also tried using GetDlgItemText, etc. but nothing seems to work. Could someone please help?
Below is a short code I've written to perform this task:

void CtestDlg::OnBnClickedButton1()
{

// Now I'm starting a thread to display the dialog and update its contents
AfxBeginThread(testFunc, (LPVOID)this );

// I'll be performing other tasks here.

}



UINT CtestDlg::testFunc(void* pVoid)
{
CtestDlg* pThis = (CtestDlg*) pVoid;

CString myStr;

pThis->m_myDialog.DoModal();

for(int i = 0; i < 10; i++)
{ Sleep(1000);
myStr += "hello\r\n";
pThis->m_myDialog.m_editVariable = myStr;
pThis->UpdateData();
}

return 0;
}

cilu
November 10th, 2004, 10:01 AM
You cannot call UpdateData from other thread. Probably there you get the assertion failure. Use messages to communicate between thread. From the scond thread post a meesage to the gui and in the handled for that message call UpdateData().

JohnCz
November 10th, 2004, 10:02 AM
There is about gazillion questions of that type posted on this forum.

Where this assertion happens? Most likely, if you followed a code there is comment explaining why assertion fired. In your case (again most likely) it says that you are trying to access CWnd derived object that was created in different thread.

You should use handle of the dialog instead pointer. WHY?
There is very good explanation regarding MFC objects and thread safety in MSDN and this forum’s FAQ. Read about it.

cromagnon
November 10th, 2004, 11:01 AM
Dear Cilu,
How do you do implement what you've just described? I'm very new to Visual c++ programming? thanks

MrViggy
November 10th, 2004, 11:58 AM
You cannot access GUI C++ objects from another thread. There are a couple ways to do this. (1) Use messaging, and send your dialog a message:
#define WM_MY_MESSAGE (WM_USER+1000)
// In your message map:
ON_MESSAGE(WM_MY_MESSAGE, OnMyMessage)
// Later on...
LRESULT CtestDlg::OnMyMessage(WPARAM, LPARAM lpString)
{
CString *pMyString = (CString *)lpString;
m_editVariable = *pMyString ;
UpdateData();
}
To launch the thread, pass in the HANDLE to the dialog:
void CtestDlg::OnBnClickedButton1()
{
// Now I'm starting a thread to display the dialog and update its contents
AfxBeginThread(testFunc, (LPVOID)GetSafeHwnd());
...
}
Then, in your thread, you just do:

UINT CtestDlg::testFunc(void* pVoid)
{
HWND hDlg = (HWND )pVoid;

CString myStr;

for(int i = 0; i < 10; i++)
{
Sleep(1000);
pStr = new CString;
myStr += "hello\r\n";
::SendMessage(WM_MY_MESSAGE, 0, (LPARAM)(&myStr));
}

return 0;
}

The second option is to use the standard Windows API functions, and again, only use the handle to the dialog.

Viggy

JohnCz
November 10th, 2004, 01:24 PM
The sample shows everything. Since I am using message parameter to send a string I had to use SendMessage instead PostMessage.

MrViggy
November 10th, 2004, 01:47 PM
Oh, yeah, another thing. First off, you're getting an assertion because "UpdateData" is operating on an invalid dialog! You created the dialog as a modal dialog. DoModal doesn't return until the user closes the dialog.

Second, I'm not sure you can create dialogs inside a worker thread. However, I think that modal dialogs have their own message pump, so you might be able to get away with that. I'm sure someone else can confirm this for me.

Anyhow, here's another thread about accessing GUI objects from worker threads:

http://www.codeguru.com/forum/showthread.php?s=&threadid=245043&highlight=thread+mfc

Viggy

cromagnon
November 10th, 2004, 06:24 PM
Thanks JohnCZ,
It appears that the edit box is attached to the main dialog. What I'm trying to do is have that edit box in a separate dialog. Can you please help?
I'll try to see if I can modify the code to produce the intended result
Thanks

cromagnon
November 10th, 2004, 08:46 PM
thnaks Viggy,
I'll try it, and see if it works

JohnCz
November 10th, 2004, 10:02 PM
It does not make any difference what dialog contains a control.

The principal is to have a handle of this dialog and pass it to a thread.
Of course dialog has to handle custom message.

Andreas Masur
November 11th, 2004, 03:14 AM
[ Merged threads ]

Andreas Masur
November 11th, 2004, 03:15 AM
The answer could have been found in the following FAQ (http://www.codeguru.com/forum/showthread.php?t=312454) as being mentioned...

cromagnon
November 11th, 2004, 06:42 AM
Dear JohnCz and MrViggy,
I extracted the important parts of your code but The edit box in the second dialog box is not updating on the screen. The first edit box in the original dialog updates. The original dialog has a variable which is the second dialog, and it is this second dialog's edit box that is not updating on the screen.
What could be wrong? I just wanted to click a button from the first dialog, and have the second dialog show up and then have its edit box updated on the screen. Below is more or less the important 20 lines of code.

void CTestDlg::OnBnClickedButton1()
{
AfxBeginThread(DialogDisplay, (LPVOID)GetSafeHwnd());
this->m_DldDlg.DoModal(); /* display the second dialog */
}

UINT CTestDlg::DialogDisplay(void* pVoid)
{
HWND hDlg = (HWND)pVoid;
CString myStr;
for(int i = 0; i< 5; i++)
{
myStr.Format("Number %d", i);
::SendMessage(hDlg, WM_MY_MESSAGE, 0, (LPARAM)(LPSTR)(LPCSTR)myStr);
Sleep(500);
}
return 0;
}

LRESULT CTestDlg::OnMyMessage(WPARAM wParam, LPARAM lParam)
{
m_editVariable = (LPSTR) lParam; /* set the first edit box */
m_DldDlg.m_dlgdlgEditBoxVariable = (LPSTR) lParam; /* set the second dialog edit box*/
UpdateData(FALSE);
return LRESULT();
}

JohnCz
November 11th, 2004, 09:46 AM
You know you could use tags like to make code snippet easier to read.

You call UpdateData member of the first dialog only. Do not expect that this will update controls I other dialog without calling other dialogs UpdateData.



LRESULT CTestDlg::OnMyMessage(WPARAM wParam, LPARAM lParam)
{
m_editVariable = (LPSTR) lParam; /* set the first edit box */
m_DldDlg.m_dlgdlgEditBoxVariable = (LPSTR) lParam; /* set the second dialog edit box*/
UpdateData(FALSE);
if(::IsWindow(m_DldDlg.m_hWnd))
{
m_Dlg.UpdateData(FALSE);
}
return LRESULT();
}


Besides:


void CTestDlg::OnBnClickedButton1()
{
AfxBeginThread(DialogDisplay, (LPVOID)GetSafeHwnd());
m_DldDlg.DoModal(); /* display the second dialog */
}
can create race condition, thread starts before second dialog window is not created yet. That is why you have to check if window exists (dialog has valid Window handle).

In addition:
1. this pointer is not needed to call member function or access member variables of the class.
2. GetSafeHwnd is equivalent to m_hWnd member of the class. It is used mainly when you need other’s window handle. It returns NULL if window is not attached to an object or you are using NULL pointer to call GetSafeHwnd. It does not mean "get safe handle" but "get window handle safe way".
Since you pass dialog’s handle you know that this pointer is not NULL therefore passing m_hWnd is safe. GetSafeHwnd returns m_hWnd anyway.

cromagnon
November 11th, 2004, 01:29 PM
Thank you soooo much JohnCz. It works now!!!!
Thanks again. for your help, and others (Andreas Masur, Mr Viggy).

JingleLee
April 29th, 2005, 09:43 AM
A more question, can I use SendMessage?

JohnCz
April 29th, 2005, 08:58 PM
Of course you can as I have indicated in my previous posts.
SendMessage vs. PostMessage dilemma is important only in hooks.
SendMessage will wait for return since windows procedure is called directly, PostMessage returns immediately since message is placed in a message queue.
If you need to send some data as parameter PostMessage is not a good choice.

JingleLee
April 29th, 2005, 09:17 PM
Of course you can as I have indicated in my previous posts.
SendMessage vs. PostMessage dilemma is important only in hooks.
SendMessage will wait for return since windows procedure is called directly, PostMessage returns immediately since message is placed in a message queue.
If you need to send some data as parameter PostMessage is not a good choice.

So clear, I see.
In fact, I indeed met such problem. When I used PostMessage and send a pointer, the problem occurred. Because when the function ends, the pointer would change to null. So when I used SendMessage, this problem was solved. :)