Calling GUI functions from a worker thread in MFC??
-
Hello, I am trying to call a GUI function from a worker thread in a Doc/View application in MFC in VS2008. It was working fine on XP 32bit, but throws error in Windows 7 32bit. I am calling the following function within a worker thread-
parent->UpdateAllViews(NULL);
The error that it throws is at Line 900 in wincore.cpp which is
ASSERT(pMap != NULL);
Also my other function which was working fine under XP is giving fatal error
AfxGetMainWnd()->PostMessage(WM_CLOSE, 0, 0);
The above function is also called in the same worker thread. Any suggestions/ideas? thanks
PKNT
Every normal gui framework is single threaded that means: you can access gui related data and functions only from the gui thread (that is the main thread in case of MFC). Its only blind luck that your app worked on WinXP most of the time. Multithreaded programs become quite non-deterministic and the same is true for the occurrence/reproduction of their bugs. Now you are "lucky" that you have a quite reliable repro case for your bug that was a bug even on WinXP! 1. AfxGetMainWnd() is a gui related call so I would call it only from the main/gui thread. Call AfxGetMainWnd() from the gui thread when you create the worker thread and pass the window pointer to the thread as a CreateParam. Since SendMessage() and PostMessage() are thread safe WinAPIs the same is true for the SendMessage() and PostMessage() methods of the window. 2. On the worker thread use the PostMessage() or SendMessage() method on the window pointer you received as a ThreadCreateParam. PostMessage(WM_CLOSE, 0, 0) should work if you use directly the window pointer without AfxGetMainWnd(). 3. To call UpdateAllViews() do the following: Define your own window message. Either WM_APP+(0..1000 or whatever) or WM_USER+... Then use again PostMessage() on the window pointer to post your own window message. In the message map of your window receive your user defined message with ON_MESSAGE(WM_MYMESSAGE, OnMyMessageMethod). Your OnMyMessageMethod() will be called from the gui thread so it will be safe to call UpdateAllViews() from there. Use PostMessage() instead of SendMessage() if you don't want the worker thread to block until the end execution of OnMyMessageMethod() on the gui thread. BTW: Here is the implementation of AfxGetMainWnd() of VS2010:
_AFXWIN_INLINE CWnd* AFXAPI AfxGetMainWnd()
{ CWinThread* pThread = AfxGetThread();
return pThread != NULL ? pThread->GetMainWnd() : NULL; }If your worker thread was created with non-mfc api then this AfxGetMainWindow() returns NULL always if called from your worker thread.
-
Every normal gui framework is single threaded that means: you can access gui related data and functions only from the gui thread (that is the main thread in case of MFC). Its only blind luck that your app worked on WinXP most of the time. Multithreaded programs become quite non-deterministic and the same is true for the occurrence/reproduction of their bugs. Now you are "lucky" that you have a quite reliable repro case for your bug that was a bug even on WinXP! 1. AfxGetMainWnd() is a gui related call so I would call it only from the main/gui thread. Call AfxGetMainWnd() from the gui thread when you create the worker thread and pass the window pointer to the thread as a CreateParam. Since SendMessage() and PostMessage() are thread safe WinAPIs the same is true for the SendMessage() and PostMessage() methods of the window. 2. On the worker thread use the PostMessage() or SendMessage() method on the window pointer you received as a ThreadCreateParam. PostMessage(WM_CLOSE, 0, 0) should work if you use directly the window pointer without AfxGetMainWnd(). 3. To call UpdateAllViews() do the following: Define your own window message. Either WM_APP+(0..1000 or whatever) or WM_USER+... Then use again PostMessage() on the window pointer to post your own window message. In the message map of your window receive your user defined message with ON_MESSAGE(WM_MYMESSAGE, OnMyMessageMethod). Your OnMyMessageMethod() will be called from the gui thread so it will be safe to call UpdateAllViews() from there. Use PostMessage() instead of SendMessage() if you don't want the worker thread to block until the end execution of OnMyMessageMethod() on the gui thread. BTW: Here is the implementation of AfxGetMainWnd() of VS2010:
_AFXWIN_INLINE CWnd* AFXAPI AfxGetMainWnd()
{ CWinThread* pThread = AfxGetThread();
return pThread != NULL ? pThread->GetMainWnd() : NULL; }If your worker thread was created with non-mfc api then this AfxGetMainWindow() returns NULL always if called from your worker thread.
Passing a window pointer between threads is not a good idea in MFC. Instead, you must pass the window handle behind the pointer. You can convert the handle to a pointer using the
CWnd::FromHandle
static method. Rest of the things are spot on. :thumbsup:«_Superman_» _I love work. It gives me something to do between weekends.
-
Passing a window pointer between threads is not a good idea in MFC. Instead, you must pass the window handle behind the pointer. You can convert the handle to a pointer using the
CWnd::FromHandle
static method. Rest of the things are spot on. :thumbsup:«_Superman_» _I love work. It gives me something to do between weekends.
If you can guarantee that the lifetime of the window spans that of the worker thread activity (and it does since it is the main window) then its OK to pass the pointer but it has no advantage over your solution because PostMessage and SendMessage is available in both cases.
-
Hello, I am trying to call a GUI function from a worker thread in a Doc/View application in MFC in VS2008. It was working fine on XP 32bit, but throws error in Windows 7 32bit. I am calling the following function within a worker thread-
parent->UpdateAllViews(NULL);
The error that it throws is at Line 900 in wincore.cpp which is
ASSERT(pMap != NULL);
Also my other function which was working fine under XP is giving fatal error
AfxGetMainWnd()->PostMessage(WM_CLOSE, 0, 0);
The above function is also called in the same worker thread. Any suggestions/ideas? thanks
PKNT
Kiran Satish wrote:
I am trying to call a GUI function from a worker thread...
Bad idea. See here for why.
"One man's wage rise is another man's price increase." - Harold Wilson
"Fireproof doesn't mean the fire will never come. It means when the fire comes that you will be able to withstand it." - Michael Simmons
"Show me a community that obeys the Ten Commandments and I'll show you a less crowded prison system." - Anonymous
-
Every normal gui framework is single threaded that means: you can access gui related data and functions only from the gui thread (that is the main thread in case of MFC). Its only blind luck that your app worked on WinXP most of the time. Multithreaded programs become quite non-deterministic and the same is true for the occurrence/reproduction of their bugs. Now you are "lucky" that you have a quite reliable repro case for your bug that was a bug even on WinXP! 1. AfxGetMainWnd() is a gui related call so I would call it only from the main/gui thread. Call AfxGetMainWnd() from the gui thread when you create the worker thread and pass the window pointer to the thread as a CreateParam. Since SendMessage() and PostMessage() are thread safe WinAPIs the same is true for the SendMessage() and PostMessage() methods of the window. 2. On the worker thread use the PostMessage() or SendMessage() method on the window pointer you received as a ThreadCreateParam. PostMessage(WM_CLOSE, 0, 0) should work if you use directly the window pointer without AfxGetMainWnd(). 3. To call UpdateAllViews() do the following: Define your own window message. Either WM_APP+(0..1000 or whatever) or WM_USER+... Then use again PostMessage() on the window pointer to post your own window message. In the message map of your window receive your user defined message with ON_MESSAGE(WM_MYMESSAGE, OnMyMessageMethod). Your OnMyMessageMethod() will be called from the gui thread so it will be safe to call UpdateAllViews() from there. Use PostMessage() instead of SendMessage() if you don't want the worker thread to block until the end execution of OnMyMessageMethod() on the gui thread. BTW: Here is the implementation of AfxGetMainWnd() of VS2010:
_AFXWIN_INLINE CWnd* AFXAPI AfxGetMainWnd()
{ CWinThread* pThread = AfxGetThread();
return pThread != NULL ? pThread->GetMainWnd() : NULL; }If your worker thread was created with non-mfc api then this AfxGetMainWindow() returns NULL always if called from your worker thread.
Thanks for your reply. Probably I should show how I am currently defining my thread and how I am using it. Declaration:
static DWORD WINAPI ThreadProcess(LPVOID pParam);
Definition:
DWORD WINAPI CMainDoc::ThreadProcess(LPVOID pParam)
{
CMainDoc *parent = (CMainDoc *)pParam;while(TRUE) { switch(::WaitForSingleObject(2, parent->m\_eMsg, FALSE, INFINITE)) {//Process the message case WAIT\_OBJECT\_0:
//access some variables in the document using parent pointer and update them such as parent->var1, parent->var2 etc.
parent->UpdateAllViews(NULL);
break;
case WAIT_OBJECT_1://process close msg
pObject->PostMessage(WM_CLOSE, 0, 0);
break;
}
}Now if I do the way you suggested, do I need to send the handle returned from AfxGetMainWnd() along with a pointer to the document in 'pParam'?
PKNT
-
Thanks for your reply. Probably I should show how I am currently defining my thread and how I am using it. Declaration:
static DWORD WINAPI ThreadProcess(LPVOID pParam);
Definition:
DWORD WINAPI CMainDoc::ThreadProcess(LPVOID pParam)
{
CMainDoc *parent = (CMainDoc *)pParam;while(TRUE) { switch(::WaitForSingleObject(2, parent->m\_eMsg, FALSE, INFINITE)) {//Process the message case WAIT\_OBJECT\_0:
//access some variables in the document using parent pointer and update them such as parent->var1, parent->var2 etc.
parent->UpdateAllViews(NULL);
break;
case WAIT_OBJECT_1://process close msg
pObject->PostMessage(WM_CLOSE, 0, 0);
break;
}
}Now if I do the way you suggested, do I need to send the handle returned from AfxGetMainWnd() along with a pointer to the document in 'pParam'?
PKNT
If you don't call MFC functions from your worker thread (its best not to call them) then its OK to go with non MFC threads so the kind of your thread doesn't matter. As I see you have already used up your pParam to send the doc pointer. You can send any number of parameters to your thread this way:
struct MyThreadParamStruct
{
CMainDoc* parent;
CWnd* mainwnd;
};void StartThreadTask()
{
MyThreadParamStruct* thread_param = new MyThreadParamStruct;
// TODO: filling thread_param
CreateThread(..., thread_param, ...)
...
}DWORD WINAPI CMainDoc::ThreadProcess(LPVOID pParam)
{
std::unique_ptr thread_param((MyThreadParamStruct*)pParam);
// TODO: use thread_param
...
} -
If you don't call MFC functions from your worker thread (its best not to call them) then its OK to go with non MFC threads so the kind of your thread doesn't matter. As I see you have already used up your pParam to send the doc pointer. You can send any number of parameters to your thread this way:
struct MyThreadParamStruct
{
CMainDoc* parent;
CWnd* mainwnd;
};void StartThreadTask()
{
MyThreadParamStruct* thread_param = new MyThreadParamStruct;
// TODO: filling thread_param
CreateThread(..., thread_param, ...)
...
}DWORD WINAPI CMainDoc::ThreadProcess(LPVOID pParam)
{
std::unique_ptr thread_param((MyThreadParamStruct*)pParam);
// TODO: use thread_param
...
}Looks like std::unique_ptr is not defined in VS2008. I got it working using standard form like
ThreadNetMsgParamStruct *pThreadParams = (ThreadNetMsgParamStruct *)pParam;
But the problem is, a CDocument class can not process windows messages. Is there any other straight forward way to replicate UpdateAllViews() command without calling it directly? thanks
PKNT
-
Looks like std::unique_ptr is not defined in VS2008. I got it working using standard form like
ThreadNetMsgParamStruct *pThreadParams = (ThreadNetMsgParamStruct *)pParam;
But the problem is, a CDocument class can not process windows messages. Is there any other straight forward way to replicate UpdateAllViews() command without calling it directly? thanks
PKNT
unique_ptr is like std::auto_ptr (its younger brother in stl). It deletes the pointer automatically when it goes out of scope (in this case at the end of the function or if you return from the func). The struct instance must be deleted otherwise you cause a memory leak. You have to send a message to a window and not to a document object. If you can't solve the problem using the info I provided (theory + 90% of the code needed) then either ask a colleague to do it for you or start learning about multithreading+gui and their relationship.
-
Hello, I am trying to call a GUI function from a worker thread in a Doc/View application in MFC in VS2008. It was working fine on XP 32bit, but throws error in Windows 7 32bit. I am calling the following function within a worker thread-
parent->UpdateAllViews(NULL);
The error that it throws is at Line 900 in wincore.cpp which is
ASSERT(pMap != NULL);
Also my other function which was working fine under XP is giving fatal error
AfxGetMainWnd()->PostMessage(WM_CLOSE, 0, 0);
The above function is also called in the same worker thread. Any suggestions/ideas? thanks
PKNT
Just dont do it. Think of your thread as being in a different process, how would you communicate between them? With PostMessage and SendMessage. So create some custom IDs, handle them in your main window proc and post or send using those IDs from the thread. You have to keep it clean on Windows, you cant bodge stuff. It might work for a bit, but eventually it will break.
-
Just dont do it. Think of your thread as being in a different process, how would you communicate between them? With PostMessage and SendMessage. So create some custom IDs, handle them in your main window proc and post or send using those IDs from the thread. You have to keep it clean on Windows, you cant bodge stuff. It might work for a bit, but eventually it will break.
Thanks, that's exactly how I did it over the weekend and it works well.
PKNT