Background: I am using OmniThreadLibrary to load batch mode ADO stored procedures in the background. I am doing some slightly dodgy stuff by swapping the connection after opening the SP but that seems to be quite reliable. I'm using PostMessage to send messages back to the calling form and that works in my test applications. Primoz' comms channels work for me, I'm using those for inter-thread comms but for our main application I'm trying to avoid that dependency by using standard PostMessage calls as we do elsewhere in the app.
Problem: Unfortunately when I put this into our main application the PostMessage calls in the thread start failing with 1400:invalid window handle.
I have liberally added extra PostMessage calls and logging code to try to locate the problem, but I'm out of ideas now. The code is boilerplate:
const WM_PW_ADLQUEUEEMPTY = WM_USER + 11;
...
if PostMessage (OwnerHandle, WM_PW_ADLPROGRESS, QueueID, 10) then
pwDebugLog ('TADLQueue.Run WM_PW_ADLPROGRESS send to ' + IntToHex (OwnerHandle, 8) + ' (IsWindow '+BoolToStr(IsWindow(OwnerHandle),true)+') OK for Queue ' + IntToStr (QueueID))
else
pwDebugLog ('TADLQueue.Run WM_PW_ADLPROGRESS send to ' + IntToHex (OwnerHandle, 8) + ' (IsWindow '+BoolToStr(IsWindow(OwnerHandle),true)+') failed for Queue ' + IntToStr (QueueID));
But the log for a series of calls is not very revealing to me. note that the four hex digits after the time is the thread id from GetCurrentThreadID.
15:41:53.221 1614 TpwAsyncDataLoader.RunQueue WM_PW_ADLPROGRESS send to 00A5110C (IsWindow True) OK for Queue -6
15:41:53.265 13B4 TADLQueue.Run WM_PW_ADLPROGRESS send to 00A5110C (IsWindow True) OK for Queue -6
15:41:53.554 13B4 TADLQueueManager.WriteSysErrorMessageToDatabase Postmessage 00A5110C (IsWindow False) failed with 1400 Invalid window handle
Can anyone shed some light on this? I'm confused at how a window handle can become invalid while I'm looking at it, but that's what it looks like to me.
The one thing I can think of is that the form I'm showing here isn't processing messages and I'm seeing a "message queue full" failure rather than the IsWindow(handle) failure that it looks like. How can I test for that?
There are cases where a handle gets recreated, most notably when you change window flags. This might be what's happening in your application.
All I found so far about recreating windows handle is this post from Allen Bauer but I'm certain reading a more detailed one written by Peter Below. Unfortunatly I can't seem to find that one.
Finally, you need to be aware of cases
where your handle may need to get
recreated. This can happen if the
surrounding form or the parent
component's handle goes through a
recreate process. Up until more recent
releases of Windows, the only way to
change some window flags was to
destroy the handle and recreate with
new flags in the CreateWindowEx()
call. There are many components that
still do this. You know if you're in a
recreate situation by checking
(csRecreating in ControlState).
Edit
Not actually the posts from Peter I had in mind but it might give you some fresh ideas.
The form will not have a handle until
you show it the first time (unless
something in the form load sequence
request the handle) but the handle is
not destroyed when you hide the form
and unless you do something that
forces the form to recreate the
handle, e.g. change its border style
or border icons, or call RecreateWnd
yourself the handle will stay the
same.
It may not be desirable but it cannot
be avoided, at least not the way
Delphi drag&dock is currently
implemented. When you dock the dragged
form to another form it becomes a
control (with WS_CHILD window style)
and that means that its window handle
has to be destroyed and recreated with
the new style. And destroying the
window handle of a container control
automatically destroys the handles for
all child controls as well.
and
There is also the fact that the forms
window handle is destroyed and
recreated when you assign to its
Parent property. This also destroys
and recreates the handles for all
controls on the form.
I had a similar issue (but in VC++2010), and I did not find the solution on any forum, so I post it here, hope this will help:
Issue:
- Creating a thread,
- Passing the HWnd handle
- In the thread, PostMessage
throws a 1400 error (invalid handle), although the pointer was equal
with the handle as seen from UI thread (with GetSafeHWnd()).
Solution:
- Do not pass the handle, but the parent CDialog(Ex) class
- This class has a m_hWnd member that will do the job
Here is a (Cpp) example, sorry for the cast mess.
// In the worker thread
ThreadParam *threadParam = (ThreadParam*)param
// This is ugly because my pointer is a void *, to avoid one more forward declaration
CCoreGenDlg *dlg = static_cast<CCoreGenDlg *>(threadParam->ptr);
// Post
bool b = PostMessage(dlg->m_hWnd ,1221,0,(LPARAM)message);
Cheers'