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 Offline
    K Offline
    Kiran Satish
    wrote on last edited by
    #1

    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 D E 3 Replies 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

      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