A part of my Delphi application accesses information from another program. Because it can take some time to execute, and sometimes the other program may be unresponsive, I run that part of my code in a separate thread.
To keep my users informed of the progress of the background thread, I execute a TaskDialog after I start the thread. I pass a pointer to the TaskDialog to my thread so that the thread can synchronize with the TaskDialog to update it as the thread progresses. When the thread finishes, I send a close message to the TaskDialog using PostMessage(CurrentTaskDialog.Handle, WM_CLOSE, 0, 0);
Everything is working so far, but I want to cancel the process if the user clicks the Cancel button on the TaskDialog (it is the only button on the TaskDialog). I cannot figure-out how to do it. No matter whether the user clicks the button or if the TaskDialog receives the close message, the ModalResult is always mrCancel. I have tried assigning a different ModalResult in the thread, but it still evaluates as mrCancel.
Is there some way to accomplish this?
What you describe is normal behavior, per the TaskDialogIndirect() documentation:
pnButton
Type: int*
Address of a variable that receives either:
one of the button IDs specified in the pButtons member of the pTaskConfig parameter
one of the following values:
0: Function call failed. Refer to return value for more information.
IDCANCEL: Cancel button was selected, Alt-F4 was pressed, Escape was pressed, or the user clicked on the close window button.
IDNO: No button was selected.
IDOK: OK button was selected.
IDRETRY: Retry button was selected.
IDYES: Yes button was selected.
Since you only have a Cancel button, and are simulating a close button press, then TaskDialogIndirect() always reports IDCANCEL, which is why the ModalResult is always mrCancel. There is no way to differentiate the actual cause (short of hooking the dialog window itself).
In any case, you don't actually need to use the ModalResult at all to accomplish what you want.
When the dialog closes for any reason, simply signal the thread to end, and then wait for the thread to fully exit. If the thread had already ended before the dialog was closed, the signal will be ignored, and the wait will be satisfied immediately.
You could then take this a step further. Instead of using tcbCancel in the TaskDialog's CommonButtons property, you could use a custom button in the TaskDialog's Buttons property, and then use the TaskDialog's OnButtonClicked event to signal the thread to exit. Block the dialog from closing automatically (by setting CanClose=False in the event handler) and wait for the thread to close the dialog when finished. This will allow you to continue displaying feedback inside the TaskDialog while waiting for the thread to end, such as displaying a "Waiting" message. Whereas with the other approach, the TaskDialog window is closed when the user clicks on the standard Cancel button, so you can't display feedback anymore even though the thread may still be running.
Related
When I press a button in VC++, the program starts reading data from USB which takes several minutes to be completed. During this operation I want to show the status of the progress in an edit box in the same Dialog using m_editCtrl.SetWindowTextW(output1); But during the transfer the contents of the Edit box are not changed. How can we change it?
Thanks
If the main thread is busy like in a loop, it will not update GUI. You should create a thread which does the heavy lifting and the thread, in turn, should post update messages to GUI.
Here is an example to get started and will do the job but it can be improved. In this example, the thread is calling the GUI API directly but it will be even better if you post a message to parent window using HWND and it will update itself.
I have read that I should only use PostMessage() to tell the UI thread to access the UI controls.
There is no problem in following this approach if I am for example setting the text of an "EDIT" control. However, what if I want to get the text of an "EDIT" control, if I send a message to the UI thread using PostMessage(), then PostMessage() will return immediately before the text is set into the buffer, so how should I solve this problem?
You cannot use PostMessage for this purpose for the reasons that you identify. WM_GETTEXT is a synchronous message. What you should do is:
If the window is in your process, then you should use GetWindowText.
If the window is in a different process, then you should call SendMessageTimeout.
Why SendMessageTimeout rather than SendMessage? Well, if the other process has hung and is not responding, then using SendMessage would never return.
There is more discussion of this topic here: The secret life of GetWindowText.
I am working with Dialog based application.
My Question is that, I want to show Waiting dialog, until some database operation carried out.
i Used Derived class from CWinThread, but problem is that, when this thread close, the background (Main application dialog) remains at deactivated means( it hide behind another window).
i am thinking that, this is happening because of WaitDialog used CWinThread class.
The problem is not unique to a dialog based application. Creating windows of any kind in more than one thread is difficult and not recommended. In your case it sounds like your wait dialog is modal, while its parent dialog is in another thread. That is even worse and can lead to deadlocks between threads.
The reliable solution is to put the wait dialog (and all other GUI) in the main thread, and the lengthy database processing in a secondary thread.
Another alternative would be to use a Modeless Dialogbox which can also optionally show the status and call the DestroyWindow function when the database operation is completed -- you may need to disable some operations of the main window while the Modeless Dialogbox is visible, though.
From the comments on my previous answer, it looks like that alternative is not viable in this situation.
Maybe a better way would be to create a normal modal "wait" dialog box, start the background thread in the dialog's InitDialog, periodically check the status of the thread using a timer and end the dialog when the thread completes?
I have a TThread which receives and sends to a device on a COM port. After I read the data, I want to activate the GUI (not in the same thread) using Synchronize(function name). However, when I call the GUI's form function to perform a button click, I get an access violation. I checked to see if the form's value is null and it is not, since this would be an obvious reason for access violation. Right now, I am setting global flags and using a timer that continuously checks to see if a certain condition is met and if so, then I fire off the button click event in that form. That seems to be the only way to avoid getting the access violation.
I really don't like timers so is there a way to avoid having to use a timer on the form?
You can post a message to the window in question. The timer works in a similar manner. It just fires off a windows message inside the form. You obviously have a handle to the window.
CWnd::PostMessage(...) Don't use send message, it gets processed inline and could cause your thread to stop working.
Typically when you have a worker thread that attempts to access Guithread, they conflict. It's been a while since I've used MFC and threading but that's what I remember. I believe it's documented to work that way.
I found the problem. I thought I was checking if my Form was null, but I was not. I fixed it making sure the form I was referencing is not null.
Edit: Turns out that one of the forms that is called when I call Fbutton1Click() is Modal so it blocks my thread. I ended having to go back to a timer to call the button click instead.. oh well.
I am coding up a program for automated testing which randomly clicks an open application window using various User32.dll library calls. My current problem is this, if a click would open a dialog, using Process.WaitForInputIdle() does not wait long enough for that dialog to be detected the next trip around the loop, which means several clicks get cued and if those clicks happen to be on something in the dialog I want to avoid (say an exit button) there is no way of telling that in advance. My question is this. Is there a way of waiting for the process or thread to finish all processing and only be waiting in the message loop again?
I hope that made sense.
Cheers
Ross
EDIT
Failing this, would it be somehow possible to set the process / threads of the target program and my program to both use the same processor and adjust the prioritorys of each so that the target program gets preference?
WaitForInputIdle will unfortunately return as soon as the app is in a message loop with no input messages waiting.
If you own the code to the dialog, you could have the dialog call SetEvent in its WM_INITDIALOG to signal your automation that it is ready for testing. Alternatively, you could look at using SetWinEventHook on the process and wait for the dialog to actually be created before sending input events to it.
The way around this it seems is to use the SendMessage API instead of the mouse_event or SendInput API. The reason for this is that SendMessage blocks until it has been processed. Just make sure you always get the handle of the window immediatly under where you want to click (using WindowFromPoint) and convert the mouse coordinates from screen to client coords using ScreenToClient. Pack the coordinates into the lParam parameter by using ((pt.Y << 16) + pt.X). This will block until processed and so any modal dialogs shown will block this call.