PostMessage在线程中返回"无效窗口句柄"

6 delphi messaging multithreading delphi-2010

背景:我正在使用OmniThreadLibrary在后台加载批处理模式ADO存储过程.我打开SP之后通过交换连接做了一些略显狡猾的事情,但这看起来非常可靠.我正在使用PostMessage将消息发送回调用表单,这在我的测试应用程序中有效.Primoz的comms频道为我工作,我正在使用它们进行线程间通信,但对于我们的主应用程序,我试图通过使用标准的PostMessage调用来避免这种依赖,就像我们在app中的其他地方一样.

问题:不幸的是,当我把它放到我们的主应用程序中时,PostMessage调用线程开始失败1400:无效的窗口句柄.

我已经自由地添加了额外的PostMessage调用和日志代码来尝试找到问题,但我现在已经没有想法了.代码是样板:

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));
Run Code Online (Sandbox Code Playgroud)

但是一系列电话的日志对我来说并不是很有启发性.请注意,时间之后的四个十六进制数字是来自GetCurrentThreadID的线程ID.

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
Run Code Online (Sandbox Code Playgroud)

任何人都可以对此有所了解吗?当我看着窗户把手变得无效时,我很困惑,但这就是我的样子.

我能想到的一件事是,我在这里展示的表单不处理消息,而是看到"消息队列已满"失败,而不是看起来像IsWindow(句柄)失败.我该怎么测试呢?

Lie*_*ers 5

在某些情况下,将重新创建句柄,尤其是在更改窗口标志时。这可能就是您的应用程序中正在发生的事情。

到目前为止,我发现有关重新创建Windows句柄的所有内容都是Allen Bauer撰写的这篇文章,但是我可以肯定阅读了Peter Under撰写的更详细的文章。不幸的是,我似乎找不到那个。

最后,您需要注意可能需要重新创建句柄的情况。如果周围的窗体或父组件的句柄经过重新创建过程,则可能会发生这种情况。直到Windows的最新发行版,更改某些窗口标志的唯一方法是销毁该句柄并在CreateWindowEx()调用中使用新标志重新创建。仍有许多组件可以执行此操作。通过检查(在ControlState中的cRecreating),可以知道您是否处于重新创建状态。

编辑

我实际上没有想到Peter的帖子,但是它可能会给您一些新的想法。

直到您第一次显示该表单时,它才会具有一个句柄(除非表单加载序列中的某些内容请求了该句柄),但是隐藏表单时,除非您执行了强制该表单重新创建该句柄的操作,否则该句柄不会被销毁,例如更改其边框样式或边框图标,或者自己调用RecreateWnd,手柄将保持不变。

可能不希望但不能避免,至少不是当前实现Delphi Drag&Dock的方式。当您将拖动的表单停靠到另一个表单时,它将成为一个控件(具有WS_CHILD窗口样式),这意味着必须销毁其窗口句柄并使用新样式重新创建它。销毁容器控件的窗口句柄也会自动销毁所有子控件的句柄。

还有一个事实是,当您将窗体窗口句柄分配给它的Parent属性时,它会被破坏并重新创建。这也会破坏并重新创建窗体上所有控件的句柄。

  • @Jeroen,至少,您不应该存储来自* VCL控件*的窗口句柄的副本。其他窗口句柄也很好。特别地,安全地存储分配有**`AllocateHWnd` **的句柄是安全的。在VCL线程中分配该句柄。另一个线程可以在此处发布其消息。使其将相关消息转发到VCL控件。 (2认同)