COM object methods causing threaded message pump freezing
Posted on 2004-04-18
I have a query from one of my team members I thought I'd post as a question here. It's a gnarly one and a bounty of 1000 points is posted for it:
Here it is:
I'm currently performing maintainence on a single threaded application
that makes extensive use of legacy libraries and 3rd party DLLs for file
import/export/conversion, operations that can sometimes take rather a
We currently have a 'busy' dialog withn a cute animation, progress
bar, status message and cancel button that we show when running a
lengthy conversion, however it's user responsiveness is rather poor as
it relies on application.ProcessMessage calls from callbacks in the
conversion libraries, which are not provided in all libraries and even
when available are not always called as often as one would like.
In order to improve the perceived responsiveness of the Busy dialog I have taken
the rather hazardous approch of developing a multi-threaded implementation in
which the dialog is implemented within a DLL (work-around for thread-unsafeness
of the vCL) which creates a seperate thread to run a message pump for the dialog.
NOTE: due to the huge existing codebase the ideal approch of writing the import
routines to run in worker threads is undesirable, it's much more managable to
improve the user experience by making some localised changes to the "Busy" dialog
than head off into a huge refactor of all the import routines.
The core of the dll is as follows...
// Up thread's priority a little so that BusyForm is responsive even when
// when application main thread is bogged down
Priority := tpHigher;
// Critical that <fBusyForm> created in context of <fBusyFormContextThread>
fBusyForm := TBusyForm.Create(nil);
// Run message pump loop for <fBusyForm>
while GetMessage( msg, 0, 0, 0 ) do
TranslateMessage( msg );
DispatchMessage( msg );
IsMultiThread := True;
fBusyFormContextThread := TBusyFormContextThread.Create(False);
... communication between the Application and the Busy form is implemented via. custom
messages posted to the Busy form.
This works just fine normally, the application main thread can head off into a lengthy
computation and the busy form remains responsive.
However things break when the application main thread makes a call to a COM object to
run a lengthy conversion. My busy form thread then block/stalls intil the COM object method call
returns, apparently within a call to Dispach() within TControl.WndProc() - Callstack shown below...
TControl.WndProc((3024, 0, 0, 0, 0, 0, 0, 0, 0, 0))
TWinControl.WndProc((3024, 0, 0, 0, 0, 0, 0, 0, 0, 0))
TCustomForm.WndProc((3024, 0, 0, 0, 0, 0, 0, 0, 0, 0))
TWinControl.MainWndProc((3024, 0, 0, 0, 0, 0, 0, 0, 0, 0))
My busy form doesn't explicitly use any COM objects, the only suspect item might be a TAnimate
I'm not at all famaliar with COM programming and the on-line help files (Delphi and MSDN) reading I've been doing has quickly resulted in information overload. I've been overwhelmed by info. on how to write in-process server COM DLLs and been unable to filter out the small nugget on what I need to do to my vanilla non-COM DLL to prevent it being messed with.
Anyone out there have any idea what I need to do to make things play nicely together???