【发布时间】:2015-09-07 13:15:50
【问题描述】:
我们在 Delphi 应用程序中托管 .NET 4.0 和许多 WinForms 窗口。
我们发现,每当我们从 Delphi 最终在 .NET 表单上调用 ShowDialog 时,当表单关闭时,SynchronizationContext.Current 会重置回使用线程池的 System.Threading.SynchronizationContext。
有没有办法让我们强制不发生这种情况,或者欺骗代码将其重置回WindowsFormsSynchronizationContext,而不是向每个 打电话给ShowDialog?
我们已经托管 .NET 好几年了,但直到最近才开始从事涉及异步代码的工作,但在我们打开的第二个窗口中失败了。
您可以使用这个简单的程序重现我们所看到的内容,只需创建一个新的 WinForms 项目并将此代码粘贴到 Program.cs 中:
using System;
using System.Diagnostics;
using System.Threading;
using System.Windows.Forms;
namespace WindowsFormsApplication4
{
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Debug.WriteLine("P1: " + SynchronizationContext.Current);
using (var fm = new Form())
{
fm.Load += (s, e) => Debug.WriteLine("P2: " + SynchronizationContext.Current);
fm.ShowDialog();
}
Debug.WriteLine("P3: " + SynchronizationContext.Current);
}
}
}
输出:
P1:
P2: System.Windows.Forms.WindowsFormsSynchronizationContext
P3: System.Threading.SynchronizationContext
基本上:
- 在打开表单之前,
SynchronizationContext.Current是null。 - 在 Form.Load 事件中,
SynchronizationContext.Current是WindowsFormsSynchronizationContext的一个实例 - 表单关闭后,
SynchronizationContext.Current已重置回System.Threading.SynchronizationContext
我们尝试了各种技巧来欺骗这段代码:
- 构造隐藏表单
- 构造一个隐藏表单并在调用
ShowDialog时使其成为所有者 - 构造一个可见的表单(显示为
Show,而不是ShowDialog)
在查看参考源后,我们认为我们已经确定了可能的原因,this line: Applicationcs#3445 及以后:
messageLoopCount--;
if (messageLoopCount == 0) {
// If last message loop shutting down, install the
// previous op [....] context in place before we started the first
// message loop.
WindowsFormsSynchronizationContext.Uninstall(false);
}
由于主消息循环是从 Delphi 完成的,.NET 对此一无所知,因此第一个弹出窗口将在关闭时最终摧毁世界,因为 .NET 认为世界即将结束。
【问题讨论】:
标签: .net asynchronous