|
You should not get an assertion failure. I tried it and it works fine. Maybe your dialog isn'n created when you call UpdateDate. Do you have more details?
Enis Arif
-----------
"I am enough of an artist to draw freely upon my imagination. Imagination is more important than knowledge. Knowledge is limited. Imagination encircles the world." (Albert Einstein)
|
|
|
|
|
I re-downloaded your example (i.e. started from scratch) and made the while loop in the StartThread function look like this:
while (ts->_this->m_ctrl_progress.GetPos () < 1000)
{
Sleep(500);
ts->_this->m_ctrl_progress.StepIt ();
ts->_this->UpdateData( TRUE );
}
Running the program (in debug mode) gives an assertion failure. Underneath the assertion failure is the message (in wincore.cpp):
// Note: if either of the above asserts fire and you are
// writing a multithreaded application, it is likely that
// you have passed a C++ object from one thread to another
// and have used that object in a way that was not intended.
// (only simple inline wrapper functions should be used)
//
// In general, CWnd objects should be passed by HWND from
// one thread to another. The receiving thread can wrap
// the HWND with a CWnd object by using CWnd::FromHandle.
//
// It is dangerous to pass C++ objects from one thread to
// another, unless the objects are designed to be used in
// such a manner.
Doesn't the ts->_this-> portion take care of the window handle?
|
|
|
|
|
I tried this in VC6, and it does not seem to work, no update occurs.
Jens
|
|
|
|
|
I also have the same problem. I'm trying to insert text into an edit control and UpdateData via the "ts" pointer and I get an assertion failure. Has anyone come up with a solution yet?
|
|
|
|
|
Assert error is a must for high VC liks VC7.
CDialog is base CWnd, it has own thread.
Now you access CDialog member function in other working thread.
If the member function allows you to read things. It is OK.
But if it changes things or affect the CDialog variables which are under the scope of its own thread. Then you will get coredump, memory leak or assertion error (depends on your compiler and debugger settings)
Also, MFC class is not thread safe.
Thread to MFC Thread.
The easy way is to send message.
Let the MFC Thread to handle the changes.
Or you have to create critical section and comparment which are not easy to made.
The concepts and the mistakes here are indeed quite common mistake for those who understands MFC clearly.
A reference book : Visual C++.Net Bible - Chapter for thread
|
|
|
|
|
I am new to MFC, just trying out with some self-projects related to file transfer using serial communication. when I tried using Threads, I have same problem with "UpdataData". I referred the above posted code. So is there any solution in VC++ 6.0?
Pls. let me know.
|
|
|
|
|
To solve this problem with calling UpdateData(); you must create CWnd* pionter using methids this->FromHandle(this->m_phWnd); then convert pointer to you window class . It happen becuse you must get window objet nit a pinter to class associatet with window
Gress
|
|
|
|
|
You should not be using UpdateData() , let alone in a secondary thread! To properly update the UI from a secondary thread, the only safe way is to post a message back to the primary thread (the one that owns the UI) indicating what needs to be updated. Read this article for more.
"When I was born I was so surprised that I didn't talk for a year and a half." - Gracie Allen
|
|
|
|
|
Ok.. with all said... What is the correct way to update a dialog from within a worker thread..
I've tried creating a refresh() function conected to the dialogs class.. Works fine from any CDialog:: classes.. but not from within workor thread.. I've tried pointers to the Cdialog function to get access from within the worker thread but UpdateData does not work..
Cstring variable;
CTestDlg * pointer = new CTestDlg
BOOL CMainDlg::OnInitDialog()
{
CDialog::OnInitDialog();
AfxBeginThread(workThread ,NULL);
return TRUE;
}
void CTestDlg::Refresh() //different dialog box
{
UpdateData(TRUE);
m_Listbox.SetWindowTextW(variable);
}
UINT workThread()
{
while(programisrunning){
variable = "text"; //<-- changeing variable throughout program actually..
pointer.Refresh();
}
}
This is pretty much the setup I need in my program.. I just can't get things workin no matter what I try...
Any Ideas.. ??
-- modified at 11:50 Monday 23rd October, 2006
|
|
|
|
|
aquawicket wrote: What is the correct way to update a dialog from within a worker thread..
By posting a message to the thread that owns the UI.
"Approved Workmen Are Not Ashamed" - 2 Timothy 2:15
"Judge not by the eye but by the heart." - Native American Proverb
|
|
|
|
|
Ok... Finally figured it out..
You are not suppose to make any calls to the dialog objects while in the worker thread.. Instead.. You must have your worker thread call another thread that will call it through a windows message Example using SendMessage()...
#define UWM_DOSOMETHING WM_APP+30 //define the User Windows Message
BEGIN_MESSAGE_MAP(CRetriggerDlg, CDialog)
ON_MESSAGE(UWM_RETRIGREFRESH, DoSomething) //Link the UWM to a class function
END_MESSAGE_MAP()
BOOL CMainDlg::OnInitDialog()
{
CDialog::OnInitDialog();
AfxBeginThread (WorkerThread, this); //call the worker thread
return TRUE;
}
UINT CMainDlg::WorkerThread( LPVOID pParam )
{
//Do your work in here
CMainDlg * me = (CMainDlg *)pParam;
me->MsgPump();
return 0;
}
void CMainDlg::MsgPump()
{
SendMessage(UWM_DOSOMETHING);
}
LRESULT CMainDlg::DoSomething(WPARAM, LPARAM)
{
//Play with the dialog window as you please here...
}
This snip of code should be enough to get anyone goin on using their working thread to talk to the dialog.. And yes.. UpdateData(); works just fine.. Just keep calls to the dialog outa the workerthread and the msgpump functions.. let DoSomething function do the talking..
-- modified at 19:19 Tuesday 24th October, 2006
|
|
|
|
|
How can I terminate this type of thread early?
|
|
|
|
|
Normally, you can call AfxEndThread() in the controlling function.
The MSDN documentation says: Call this function [AfxEndThread] to terminate the currently executing thread. Must be called from within the thread to be terminated. I hope this helps.
Enis Arif
|
|
|
|
|
Thanks for your help. In my case, I have I very long process working on my
worker thread, and I need to terminated the thread from the calling module.
Any idea how to do this ?
Thanks
|
|
|
|
|
You have to call AfxEndThread from the worker thread. If you have let's say a Stop button on your calling module, you can set a bool member var in your OnStop() method and check this var in your thread. If it is true then call AfxEndThread().
This should solve the problem, but be carefull what happens with the data your thread has already modified.
Enis Arif
|
|
|
|
|
|
//In .h of the class
///////////////////////
class CToto
{
// Implementation
protected:
struct SToto
{
BOOL m_bToto;
};
static UINT ThreadToto(LPVOID pParam);
};
//In .cpp of the class
////////////////////////
CToto::CToto()
{
SToto stToto;
SToto.m_bToto = TRUE;
AfxBeginThread(CToto::ThreadToto, this);
}
UINT CToto::ThreadToto(LPVOID pParam)
{
CToto* pToto = (CToto*)pParam;
pToto->SToto.m_bToto = FALSE;
return 0;
}
Enjoy!!!;)
|
|
|
|
|
|
Indeed there is no delete. There are two situations here, and I'm not sure yet which one is better:
1) you can destroy the pointer in the worker thread
2) you can destroy the pointer in the calling function after the thread exits
In both situations, I don't really now what happens with thread safety. Actually, I'm trying to get this now. This was just an example of how to create such a worker thread, because MS doesn't say a word about it.
Enis Arif
|
|
|
|
|
Enis wrote:
you can destroy the pointer in the worker thread
That's fine, though if you had passed the class instance pointer, deletion would have been unnecessary!
Enis wrote:
you can destroy the pointer in the calling function after the thread exits
This would mean waiting for the thread to finish, in which case a stack variable of local scope would actually suffice!
Nish
Author of the romantic comedy
Summer Love and Some more Cricket [New Win]
|
|
|
|
|
The stack variable is out of the question... you will get in very nasty problems when you have multiple threads. Passing the class instance pointer makes the deletion unnecessary (so I though, I don't know if I was wrong...though I didn't get a memory leak in my tests)
Enis Arif
|
|
|
|
|
Enis wrote:
The stack variable is out of the question... you will get in very nasty problems when you have multiple threads
That's a very confusing statement! If the stack variable is in scope as long as the thread executes, it won't cause any problems at all. Of course it's a bad idea to do that, but it won't cause any thread issues. Since in your particular case the stack variable is simply s struct with one member that points to a class instance.
Enis wrote:
Passing the class instance pointer makes the deletion unnecessary
Yes, since the class gets destructed when it goes out of scope, usually when the program exits or when the window is closed, if it is a window class like say CWnd.
Author of the romantic comedy
Summer Love and Some more Cricket [New Win]
|
|
|
|
|
IF your instantiating func intends to wait around for any threads it starts to terminate, then its actually a better idea to pass on the stack than on the heap (i.e. with new/delete).
BTW, passing the class instance pointer doesn't always protect you from stack issues. Consider this code:
void Foo()
{
MYCLASS MyClass;
MyClass.DoBackgroundProcess();
return;
}
void MYCLASS::DoBackgroundProcess()
{
AfxBeginThread(StartThread, (PVOID)this)
}
// worker thread func
UINT MYCLASS::StartThread(PVOID pThis )
{
(MYCLASS*)pThis->m_membervar++;
}
Any heap or stack based object passed to a worker thread will require you to enforce a proper lifespan for the object, either by thread synchronization or (for heap objects) by handing off the cleanup responsibility to the thread itself...the old hari-kari "delete this;".
|
|
|
|
|
The example you showed would be coded only by a bad programmer in my opinion. And if that's so no amount of safety precautions will save his code
But I agree with you that passing this does not ensure safety by any means!
Nish
Author of the romantic comedy
Summer Love and Some more Cricket [New Win]
|
|
|
|
|
You can't guarantee that the Dlg class or its members will be threadsafe. In other words, you should probably use a mutex or something to protect two threads from accessing/changing the class or its members at the same time. Just FYI.
|
|
|
|