Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. C / C++ / MFC
  4. Calling GUI functions from a worker thread in MFC??

Calling GUI functions from a worker thread in MFC??

Scheduled Pinned Locked Moved C / C++ / MFC
c++helpquestion
11 Posts 5 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • K Kiran Satish

    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

    P Offline
    P Offline
    pasztorpisti
    wrote on last edited by
    #2

    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.

    _ K 2 Replies Last reply
    0
    • P pasztorpisti

      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.

      _ Offline
      _ Offline
      _Superman_
      wrote on last edited by
      #3

      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.

      _Microsoft MVP (Visual C++) (October 2009 - September 2013)

      Polymorphism in C

      P 1 Reply Last reply
      0
      • _ _Superman_

        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.

        _Microsoft MVP (Visual C++) (October 2009 - September 2013)

        Polymorphism in C

        P Offline
        P Offline
        pasztorpisti
        wrote on last edited by
        #4

        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.

        1 Reply Last reply
        0
        • K Kiran Satish

          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

          D Offline
          D Offline
          David Crow
          wrote on last edited by
          #5

          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

          1 Reply Last reply
          0
          • P pasztorpisti

            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.

            K Offline
            K Offline
            Kiran Satish
            wrote on last edited by
            #6

            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

            P 1 Reply Last reply
            0
            • K Kiran Satish

              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

              P Offline
              P Offline
              pasztorpisti
              wrote on last edited by
              #7

              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
              ...
              }

              K 1 Reply Last reply
              0
              • P pasztorpisti

                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
                ...
                }

                K Offline
                K Offline
                Kiran Satish
                wrote on last edited by
                #8

                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

                P 1 Reply Last reply
                0
                • K Kiran Satish

                  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

                  P Offline
                  P Offline
                  pasztorpisti
                  wrote on last edited by
                  #9

                  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.

                  1 Reply Last reply
                  0
                  • K Kiran Satish

                    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

                    E Offline
                    E Offline
                    Erudite_Eric
                    wrote on last edited by
                    #10

                    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.

                    K 1 Reply Last reply
                    0
                    • E Erudite_Eric

                      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.

                      K Offline
                      K Offline
                      Kiran Satish
                      wrote on last edited by
                      #11

                      Thanks, that's exactly how I did it over the weekend and it works well.

                      PKNT

                      1 Reply Last reply
                      0
                      Reply
                      • Reply as topic
                      Log in to reply
                      • Oldest to Newest
                      • Newest to Oldest
                      • Most Votes


                      • Login

                      • Don't have an account? Register

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • World
                      • Users
                      • Groups