So I have a thread which is trying to send a user-defined message to one of the 2 views in my SDI application. For some reason the SendMessage() function never returns and I know the message isn't being sent processed or something because the TRACE statements in my handler aren't shown.
I am using PostMessage in my thread also for situations when I don't need a return value and immediate processing of my message and that works w/o a problem. I am using the same HWND that I pass to PostMessage() but SendMessage() won't work for some reason.
The thread is created from within a message handler and it only runs while still in the message handler, i.e. meaning the message handler doesn't return until the thread has terminated.
How can I go about and debug this?
Thanks.
TheCPUWizard
December 5th, 2004, 07:31 PM
Looks like you have a deadlock. SendMessage will not return until the message is processed (compare to PostMessage). The Thread will not exit until SendMessage Returns. The Message Pump will not process messages until the Thread exits....
Bang, you're dead....
robd
December 5th, 2004, 08:19 PM
Lol, thanks. I figured there was something about messages not being processed. It's weird though because someone recommended using SendMessage() even though I had posted code showing I had a thread from which I had to make teh call.
I guess I'll have to look into PeekMessage or something.
robd
December 5th, 2004, 09:43 PM
Ok, so after looking at my code I realize a problem with using SendMessage() because I can't figure out where to make the PeekMessage() call. Please look at my code structure below:
// called when user clicks run button on property page
void CPropPageOne::OnButtonRun()
{
...
m_Redirect.Run(sCmdLine);
}
-----------------------------------------------------
BOOL CRedirect::Run(LPCSTR lpszCmdLine)
{
//launch a console app as a child process w/ redirected stderr/stdout/stdin
hChild = ::CreateProcess();
// Create the thread that reads child's stdout
hStdOut = ::CreateThread();
// Create the thread that reads child's stderr
hStdErr = ::CreateThread();
// wiat for child and threads to terminate
WaitForSingleObject(hChild);
WaitForSingleObject(hStdErr);
WaitForSingleObject(hStdOut);
return TRUE;
}
// this is the thread that reads the child processes' stderror
int CRedirect::StderrThd(HANDLE hStdError)
{
while(TRUE) {
// Read stderror
ReadFile(hStdError...);
/***************************************************/
// Now what I want to do is to show a FileDialog and see if the user selected
// a file and what the filename is
// I don't think I can do this within the thread so I am trying to do it in my view class by sending a message to the view class and having a message handler there
::SendMessage(phObjectHandle,WM_SHOW_FILE_DIALOG,0,0);
// never gets here b/c of deadlock...not sure where to put PeekMessage
}
return 0;
}
Your help would be greatly appreciated. Thanks.
robd
December 6th, 2004, 01:03 AM
So obviously PeekMessage is not the solution because SendMessage doesn't place the message in the message queue. I searched CG forums extensively but can't seem to find an answer to my problem besides the option of using a UI Thread. I need to be able to display the file dialog while my worker thread is running because I need to use the selected file to do something else in the thread.
Any help???
TheCPUWizard
December 6th, 2004, 06:28 AM
What is wrong with PostMessage (which I mentioned in an earlier post) ???
robd
December 6th, 2004, 09:17 AM
Wizard, PostMessage will queue the message and the message only gets dispatched after my worker thread terminates. The problem is I need to display my FileDialog while my worker thread is running because based on the user input to the dialog I need to send the child process some data.
Basically, this:
1)Worker thread receives certain string on STDERR from child process
2)Worker thread somehow displays file dialog to telluser to choose a file
3)Worker thread sends filename to child process on STDIN(redirected)
robd
December 6th, 2004, 11:01 AM
Hi,
I have a worker thread that needs to display a FileDialog. I need to display this dialog immediately because I don't want to proceed in the thread until this dialog is shown. PostMessage() is of no help here because it queues the message and returns immediately.
I need some help on using SendMessage(). When I call it from my worker thread it seems that there is deadlock. I try to send my own message(WM_APP + 0x20) to one of my View classes(SDI app w/ 2 views in a CSplitterWnd).
Can someone please help me here, I'm in a bind?
Thanks.
cilu
December 6th, 2004, 11:05 AM
Did you created a handler for the message? Post some code.
gstercken
December 6th, 2004, 11:07 AM
I need some help on using SendMessage(). When I call it from my worker thread it seems that there is deadlock.Uisng SendMessage() in that situation is fine - what kind of deadlock are you getting?
robd
December 6th, 2004, 11:11 AM
Here is some code and some explanations at the end:
// msg handler called when user clicks run button on property page
void CPropPageOne::OnButtonRun()
{
...
m_Redirect.Run(sCmdLine);
}
-----------------------------------------------------
BOOL CRedirect::Run(LPCSTR lpszCmdLine)
{
//launch a console app as a child process w/ redirected stderr/stdout/stdin
hChild = ::CreateProcess();
// Create the thread that reads child's stdout
hStdOut = ::CreateThread();
// Create the thread that reads child's stderr
hStdErr = ::CreateThread();
// wiat for child and threads to terminate
WaitForSingleObject(hChild);
WaitForSingleObject(hStdErr);
WaitForSingleObject(hStdOut);
return TRUE;
}
// this is the thread that reads the child processes' stderror
int CRedirect::StderrThd(HANDLE hStdError)
{
while(TRUE) {
// Read stderror
ReadFile(hStdError...);
/***************************************************/
// Now what I want to do is to show a FileDialog and see if the user selected
// a file and what the filename is
// I don't think I can do this within the thread so I am trying to do it in my view class by sending a message to the view class and having a message handler there
::SendMessage(phObjectHandle,WM_SHOW_FILE_DIALOG,0,0);
//Deadlock probably because the message pump is not running or soemthing
}
return 0;
}
--------------------------------------------------------------------------------------------
//MyView class
// in the message_map
ON_MESSAGE(WM_SHOW_FILE_DIALOG, OnShowFileDialog)
LRESULT CMyView::OnShowFileDialog(WPARAM wParam, LPARAM lParam)
{
TRACE("Hello from Show File Dialog\n");
return 0;
}
In my StdoutThd which is pretty similar to my StderrThd above, I use PostMessage since I am just updating some control and don't need a return value and immediate processing; that works without problem.
I shoudl mention, changing SendMessage to PostMessage removes teh deadlock but obviously I need to use SendMessage as I explained above.
Thanks.
Andreas Masur
December 6th, 2004, 02:51 PM
[ Moved thread ]
Andreas Masur
December 6th, 2004, 02:54 PM
What is 'phObjectHandle'? Where does it get created? Where does it get assigned?
robd
December 6th, 2004, 03:04 PM
Ok, Thanks for your interest.
First of all, phObjectHandle is an HWND*. It is the window handle for one of the 2 view classes in my SDI App. I obtain the handle using GetSafeHwnd() in my CMyView class. I obtain phObjectHandle then from the CMyViewClass.
Now, as I said before if I change the SendMessage to a PostMessage() call passing in the same window handle as above, there is no problem. SendMessage, however, is giving me a problem and my guess is because the ViewClass is not pumping messages or something.
I have no clue how to proceed and have spent hours looking on the CG forums.
THanks.
MrViggy
December 6th, 2004, 03:44 PM
I don't think you can use SendMessage since this will cause your GUI code to be executed in the context of the thread. In other words, when you do a PostMessage, the message is put into the GUI's message queue, and processed later. When you do a SendMessage, the message is *not* put into the message queue, instead it is executed as if you called the function (for the message) directly.
Viggy
robd
December 6th, 2004, 03:49 PM
I'm not sure myself and I am sure there is probably another way to do what I want becuase this has to be a common problem:
-Use MFC to create a GUI for a command line app.
-Read + parse output from command line application and show appropriate controls.
-In my case the user needs to specify a filename and I thus need to show a FileDialog when I see certain output from the console program that I have spawned with redirected filehandles.
Really frustrating..
MrViggy
December 6th, 2004, 03:56 PM
Well, one way would be to use an event. Reset the event; Post the message; wait for the event to be signaled.
In your thread:
// NOT TESTED!
::ResetEvent(hMyEvent);
::PostMessage(phObjectHandle,WM_SHOW_FILE_DIALOG,0,0);
WaitForSingleObject(hMyEvent, INFINITE);
// At this point, the thread will wait for the GUI to do it's thing!
Another possibility might be to create a GUI thread instead of a worker thread. I've never done that, however.
Viggy
robd
December 6th, 2004, 04:10 PM
Well the problem I have noticed is that as soon as my thread exits only then do the message I posted in the thread get handled. So I fear using the event will just suspend the thread forever.
MrViggy
December 6th, 2004, 04:19 PM
Yes, it will suspend the worker thread. This will allow the GUI thread to continue, and process the message. After the GUI thread processes the message, it needs to Set the event; which will wake up the worker thread. If it doesn't, then the application is not really multi-threaded. You're probably seeing this because your worker thread is fast, so it is able to complete it's work in the time slice provided to it.
Also, the second parameter to WFSO is a timeout value. If you're worried about deadlocking, you can just provide a timeout, and then decide how to continue.
Well...could it be that the view is somehow waiting for the thread to be terminated (in some other function)? This would explain the deadlock with 'SendMessage()' but not with 'PostMessage()'...
robd
December 6th, 2004, 06:50 PM
Well if you look at my code above, I do a WaitForSingleObject in the function the function that spawns the worker thread. This function is in a class, CRedirect, and I have a CRedirectObject in a property page in my 2nd view class-not the view class I am trying to SendMessage() to.
No idea what to do...where are the MVPs when you need em :(?
TheCPUWizard
December 6th, 2004, 09:21 PM
Sounds like you have some serious refactoring to do on your design so the the user thread is exactly that and it no EVER blocked witing for a worker thread. The UI should ALWAYS be responsive.
Once you do this, the worker thread can use PostMessage (tyou still do not want to use send message because the dispatch will occur in the context of the worker thread as previously mentioned).
Additional inter-thread communication via semaphores or the like will complete the design.
robd
December 6th, 2004, 09:27 PM
I actually don't want the UI to be responsive in the sense that users shouldn't be able to click anything or do anything in the GUI. I guess the problem is that all messages even those that I want to Post won't be received by any controls, etc.
I don't want to have to disable buttons, etc. b/c it's a pain since I don't want users to be able to navigate to any other proeprty page or do anything else. Not sure there would be a way to do it.
TheCPUWizard
December 6th, 2004, 09:31 PM
You can always install a custom filter in the message loop to block the messages (either discard or put into a "hold" queue)....
robd
December 6th, 2004, 11:43 PM
Using an event didn't help anything at all.
Andreas,
I think what you're saying is probably right but please make sure my thinking is correct.
This is my the relationship of the different classes/objects in my app:
the redirect class has a function that spawns the worker threads and then waits for them using WaitForSingleObject() so maybe indirectly the GUI thread is being blocked?
I really don't see the point of SendMessage within a thread, it seems like it is impossible to use in this situation.
the redirect class has a function that spawns the worker threads and then waits for them using WaitForSingleObject() so maybe indirectly the GUI thread is being blocked?
Yes...since all of the above belongs to the main thread (the one with the message loop), the call to 'WaitForSingleObject()' will block your message queue from being processed...and thus, deadlocking your thread...
Can you probably get rid of the 'WaitForSingleObject()' call? And instead...let the thread send a message as soon as it is done...
Andreas Masur
December 7th, 2004, 04:10 AM
[ Merged threads ]
robd
December 7th, 2004, 09:17 AM
Well in that function that calls WaitForSingleObject() I just commented all the Waits and replaced it with a while(1) { Sleep(2000);}...shouldn't that allow messages to be processed? Well, they aren't processed and I'm not sure why since that thread should be yielding given that is sleeping.
Andreas Masur
December 7th, 2004, 10:01 AM
Well...yes...actually that should work...can you post your project here (without the debug/release directories)?
MrViggy
December 7th, 2004, 10:54 AM
Well in that function that calls WaitForSingleObject() I just commented all the Waits and replaced it with a while(1) { Sleep(2000);}...shouldn't that allow messages to be processed? Well, they aren't processed and I'm not sure why since that thread should be yielding given that is sleeping.
If this is happening in the GUI thread, then I don't see how this will work. Instead of letting the system (with WaitForSingleObject) wake the GUI thread, you're just sleeping for 2 seconds at a time. You never leave the 'while' loop, thus you never exit the function, and the GUI's message pump never continues.
If you need to wait in the GUI thread, you'll need to take advantage of messaging. You set a "state" in your GUI thread; disable all the controls that you don't want the user to interact with; and just wait (just return from the function that created the worker threads). If nothing is to be done, your app will go into 'idle' mode; waiting for messages. Once your thread(s) is(are) done, then can post a message to the GUI's queue, and it will be handled right away (or pretty close to right away). At this point, you can do a WaitForSingleObject on the thread handle, if you need to wait until it's completely done, as this should happen fairly quickly.
Some pseudo code:
1) Handle some message that says to start threads.
a) Set 'm_bRunningThread' flag to true (optional)
b) Disable controls that shouldn't work while thread is running.
c) Start threads
d) Return from message.
2) Do nothing. Well, okay, handle messages that are allowed while thread is running.
3) Thread is done.
a) Post message to GUI thread that the work is all done.
b) Exit thread function; which terminates thread.
4) Message recieved from worker thread.
a) Call WFSO on thread handle. If the thread is already terminated, WFSO should return immediately.
b) Reset 'm_bRunningThread' flag (optional)
b) Reset all controls to become active again
You should never be waiting in the GUI thread for any extended period of time.
Viggy
robd
December 7th, 2004, 08:11 PM
Thanks to everyone who replied. After Andreas posted his message I realized that I just wasn't thinking clearly. I haven't written too many GUIs but I did write one in Java/Swing and I realized my problem.
Like MrViggy said, even if I put that Sleep() call in the main/GUI thread, it was still the GUI thread that was being put to sleep so obviously nothing was achieved that way.
In case anyone cares, I created a thread that waits for the child process and the stdout and stderr threads to complete using WaitForSingleObject() calls. When the thread knows that the child process and threads have terminated, it sends a message to reenable the controls that I had disabled. This way I never blocked the main thread and the message pump was still running so I could use sendmessage in my worker threads without a problem.
Thanks all for your help.
MrViggy
December 8th, 2004, 11:53 AM
Yep, that sounds right! I'm glad you got it all worked out!
;)
I think the biggest problem people have with worker threads and GUI threads is, "How do I wait for something in my GUI thread?" The light bulb went on for me when I realized waiting for a worker thread in the GUI thread is no different then waiting for the user to press a button; click a menu item; etc. You don't really do anything to wait, the framework takes care of that for you. You just need a message handler; event signal; etc.
Viggy
codeguru.com
Copyright Internet.com Inc., All Rights Reserved.