Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

DLG activity interrupts thread postmessage 1

Status
Not open for further replies.

bluecrush

Programmer
Mar 26, 2004
61
US
I have a thread that periodically uses PostMessage() to update the main dlg with its progress. It works wonderfully until you click on a button or scrollbar on the main dlg and then the main dlg stops receiving (?) the msgs -- or at least the display isn't updated (i.e. an edit box with text messages and a progress ctrl). The thread continues to run to completion, however. Here is a snippet of code that I'm working with:
Code:
/*This is where we start processing the source file*/
UINT CLexNexView::LexNexFileProcessorThread(LPVOID pParam)
{
    //do some stuff

	while(sourceFile.ReadString(line))
	{
		mainView->PostMessage(WM_MY_STEPIT,0,0);
                //do some stuff
        }
   
    //do some stuff
}
/////////////////////////////////////////////////////
BOOL CLexNexView::PreTranslateMessage(MSG* pMsg)
{
	CLexNexView* view = (CLexNexView*)pMsg->wParam;
	CMessageString* msg = (CMessageString*) pMsg->lParam;
	int returns = pMsg->wParam;

	switch(pMsg->message)
	{
		case WM_MY_UPDATE_DISPLAY:
			
			Display(*msg,returns);
			UpdateData(FALSE);
			break;
		case WM_MY_STEPIT:
			m_Progress.StepIt();
			break;
	}

	return CView::PreTranslateMessage(pMsg);
}
Any ideas as to how I can have the messages handled even while other actions are being executed on the main dlg?

Thank you in advance for any ideas as to how to correct this problem!
 
if I understand you correctly then, when you start some new thread, the new thread has its own message queue. To post in the correct thread you should use function PostThreadMessage.
Another problem is the priority of messages. Use PeekMessage to be sure of message you want to receive. GetMessage may process not all of messages, because message with greater priority would be processed firstly.

Ion Filipski
1c.bmp
 
>To post in the correct thread you should use function PostThreadMessage.

PostThreadMessage is used to post to the thread rather than to the window. PostMessage (and SendMessage) are fine for posting to windows between threads.

>UINT CLexNexView::LexNexFileProcessorThread(LPVOID pParam)
<snip>
>mainView->

Are you sharing the view between the threads? That isn't thread safe.

>Any ideas as to how I can have the messages handled even while other actions are being executed on the main dlg?

See faq116-5162 [Threads in a GUI (MFC) app the easy way]


/Per
[sub]
&quot;It was a work of art, flawless, sublime. A triumph equaled only by its monumental failure.&quot;[/sub]
 
>PostMessage (and SendMessage) are fine for posting to windows between threads.

Clarification:
That is the API functions PostMessage/SendMessage. Not the CWnd methods (as accessing the CWnd itself isn't thread safe).

/Per
[sub]
&quot;It was a work of art, flawless, sublime. A triumph equaled only by its monumental failure.&quot;[/sub]
 
AH! Wonderful! I've just learned a lot! Thank you for the response and for the reference to the FAQ, Per.

I am embarrassed to say that my thread is about as safe as running with scissors! Consequently, I am rewriting my code to take advantage of this new enlightenment. If y'all don't mind and if you happen to return to this thread I would welcome your thoughts on what I am doing:

Based on the FAQ, I have declared a struct that will contain all the data I need in the thread that is set in the GUI (Yes, Perf, I was sharing the view between threads[blush]):
Code:
struct ThreadParam
{
  HWND m_hDlg; //safe dlg handle
  CLexNexSet* m_pSet; //CRecordSet
  CLexNexMainSet* m_pMainSet; //CRecordSet
  CLexNexPrimSet* m_pPrimSet; //CRecordSet
  CString m_sSourceFile; //source file name
  CString m_sDestinationFile; //dest file name
};
Is it kosher to pass a pointer to a CRecordSet object to my thread?

Here's where I set the values of my struct and launch the thread:
Code:
void CLexNexView::OnProcessbuttonClicked() 
{
  Log("Thread launched");
  ThreadParam* param = new ThreadParam;
  param->m_hDlg = m_hWnd;
  param->m_pMainSet = m_pMainSet;
  param->m_pPrimSet = m_pPrimSet;
  param->m_pSet=m_pSet;
  param->m_sDestinationFile=m_DestinationFile;
  param->m_sSourceFile=m_SourceFile;
  CWinThread* lexNexThread = AfxBeginThread(LexNexFileProcessorThread,param,THREAD_PRIORITY_NORMAL);	
}
How's it looking so far? Am I on the right road now? I am going to have to set up some message mapping and revise the code in my thread because I blatantly violated every rule in the FAQ!

Please let me know if you see any issues with my plan of attack!
 
Note that you may share stuff among threads, but they should then be mutexed so that only one thread access them at the time. The FAQ describes how you can avoid using mutexes but still be thread safe. Ie multithreading "the easy way". If you really need to share stuff among thread the easy way FAQ sin't applicable (as there isn't any easy way).

>Is it kosher to pass a pointer to a CRecordSet object to my thread?

Only if you're 100% sure other thread won't fiddle with the same object, which you can only be if you use thread locking and stuff. For example, what would happen if the CRecordSet got deleted in thread X. How would thread Y react to that?

>Please let me know if you see any issues with my plan of attack!

Right! Assuming you want to do it according to the FAQ.

Code:
  param->m_pMainSet = m_pMainSet;
  param->m_pPrimSet = m_pPrimSet;
  param->m_pSet=m_pSet;
  param->m_sDestinationFile=m_DestinationFile;
  param->m_sSourceFile=m_SourceFile;
all violates rule #1 as both threads will fiddle with the same (non thread-safe) variables.

For the CString members do a LPCTSTR cast instead of plain copy. See bottom note in the FAQ

For the m_pXXXX pointers, either
1) Make full, independant, copies of the variables
or
2) Do all manipulation/access of them via messages (rule #3) to the CLexNexView so there will be only 1 thread that access them.
or
3) Don't have them at all in the view. Instantiante and use them in the 2nd thread only.

/Per
[sub]
&quot;It was a work of art, flawless, sublime. A triumph equaled only by its monumental failure.&quot;[/sub]
 
A short explanation on what its all about. It all bottles down to:
If you share a variable among threads without threadsafety mechanisms, you can never trust the variable.

An extreme but trivial example to point out the problem:
Code:
  if (sharedVariable!=0)
	result = someThing / sharedVariable;
would never ever fail due to division by zero. Right?

But what if some other thread sets sharedVariable=0 just after the !=0 test was made (but before the division)?
*boom*

One way to adress it would be to
Code:
  TakeSomeThreadLock();
  if (sharedVariable!=0)
	result = someThing / sharedVariable;
  ReleaseThreadLock();
Where TakeSomeThreadLock would make sure no other thread fiddles with sharedVariable. That's when we talk about mutexes, semaphores and whatnot.

Another way would be to "simply" not share any variables (as described in the FAQ).


/Per
[sub]
&quot;It was a work of art, flawless, sublime. A triumph equaled only by its monumental failure.&quot;[/sub]
 
This is some GREAT information! Thanks, Per!

So, I've instantiated instances of my CRecordSet objects within my second thread and did an LPCTSTR cast on the CString members. So far, so good. I then did as the FAQ recommends for message and am getting a confusing error:
Code:
error C2660: 'PostMessageA' : function does not take 4 parameters
Error executing cl.exe.
I receive this error on this line:
Code:
PostMessage(p->m_hDlg,WM_MY_STEPIT,0,0);
I've done some research, both online and off, in the msdn library and the information I'm getting is conflicting. My offline msdn library says:
Code:
BOOL PostMessage( UINT message, WPARAM wParam = 0, LPARAM lParam = 0 );
and the online msdn library says:
Code:
BOOL PostMessage(          HWND hWnd,
    UINT Msg,
    WPARAM wParam,
    LPARAM lParam
);
Hmmmm....[ponder]
Any ideas?
 
AHA! I've discovered the solution!
Code:
::PostMessage(p->m_hDlg,WM_MY_STEPIT,0,0);
 
Great that you figered it out, but from what class are you calling the function? Do you have another window class within the thread?

/Per
[sub]
&quot;It was a work of art, flawless, sublime. A triumph equaled only by its monumental failure.&quot;[/sub]
 
Nope, I don't have another window class within the thread. It's a worker thread that is launched from my CRecordView class.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top