【问题标题】:Hosting .NET and WinForms, SynchronizationContexts is reset when ShowDialog of .NET window returns承载 .NET 和 WinForms,SynchronizationContexts 在 .NET 窗口的 ShowDialog 返回时重置
【发布时间】: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.Currentnull
  • 在 Form.Load 事件中,SynchronizationContext.CurrentWindowsFormsSynchronizationContext 的一个实例
  • 表单关闭后,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


    【解决方案1】:

    在您的上下文中包装 WindowsFormsSynchronizationContext。 喜欢:

    class MySynchronizationContext : SynchronizationContext
    {
        private SynchronizationContext context = new WindowsFormsSynchronizationContext();
    
        public override SynchronizationContext CreateCopy()
        {
            return context.CreateCopy();
        }
    
        public override bool Equals(object obj)
        {
            return context.Equals(obj);
        }
    
        public override int GetHashCode()
        {
            return context.GetHashCode();
        }
    
        public override void OperationCompleted()
        {
            context.OperationCompleted();
        }
    
        public override void OperationStarted()
        {
            context.OperationStarted();
        }
    
        public override void Post(SendOrPostCallback d, object state)
        {
            context.Post(d, state);
        }
    
        public override void Send(SendOrPostCallback d, object state)
        {
            context.Send(d, state);
        }
    
        public override string ToString()
        {
            return "Wrapped";
        }
    
        public override int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)
        {
            return context.Wait(waitHandles, waitAll, millisecondsTimeout);
        }
    }
    

    并在任何 Windows 窗体调用之前设置它。在这种情况下它不会被替换:

            /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
    
            MySynchronizationContext context = new MySynchronizationContext();
            SynchronizationContext.SetSynchronizationContext(context);
            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 );
        }
    

    希望它会有所帮助。

    【讨论】:

      猜你喜欢
      • 2011-09-03
      • 2023-04-04
      • 2014-11-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-24
      相关资源
      最近更新 更多