【问题标题】:Show and ShowDialog won't workShow 和 ShowDialog 不起作用
【发布时间】:2013-04-21 05:46:14
【问题描述】:

这和我以前见过的任何东西都不一样。
当我调用 (new System.Windows.Forms.Form()).ShowDialog() 时,表单会显示一毫秒左右,然后消失。
我追踪了电话并得到了这个:

System.Windows.Forms.Form.Dispose
System.ComponentModel.Component.Dispose
System.Windows.Forms.Application.ThreadWindows.Dispose
System.Windows.Forms.Application.ThreadContext.DisposeThreadWindows
System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop
System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner
System.Windows.Forms.Application.ThreadContext.RunMessageLoop
System.Windows.Forms.Form.ShowDialog Esfand.Program.Main C#

我已经尝试了任何想到的方法来解决这个问题。

虽然我在尝试显示此表单之前已经显示了登录表单。
我不认为登录表单有什么特别之处(通常无聊的东西,连接到服务器,发送凭据,接收数据)。
我正在为表单使用主线程。我有消息传递和消息循环的经验。我在登录表单中使用了线程。

编辑: 澄清 Cody Gray 的建议:

这就是我在void Main(string[]) 中的内容:

LoginForm login = new LoginForm ();
login.ShowDialog ();//works
if (login.DialogResult == DialogResult.OK)
{
    MainForm f = new MainForm ();
    f.ShowDialog ();//won't work
}

在新线程中创建和显示 MainForm 使一切重新开始工作。但是每个表单上都会出现随机错误,这使得该解决方案不够好。

编辑 2:
FormClosing 事件甚至不会触发。

System.Windows.Forms.Form A;
A = new Form();
A.FormClosing += new FormClosingEventHandler((sender, e) => { System.Diagnostics.Debugger.Break();/*won't work. tried Breakpoints and other stuff too*/ });
A.ShowDialog();

编辑 3: HandleDestroyed 事件堆栈跟踪:

>   Esfand.exe!Esfand.Program.Main.AnonymousMethod__1(object sender = {System.Windows.Forms.Form}, System.EventArgs e = {System.EventArgs}) Line 50 + 0x6 bytes C#
    System.Windows.Forms.dll!System.Windows.Forms.Control.OnHandleDestroyed(System.EventArgs e) + 0x9e bytes    
    System.Windows.Forms.dll!System.Windows.Forms.Form.OnHandleDestroyed(System.EventArgs e) + 0x13 bytes   
    System.Windows.Forms.dll!System.Windows.Forms.Control.WmDestroy(ref System.Windows.Forms.Message m) + 0x54 bytes    
    System.Windows.Forms.dll!System.Windows.Forms.Control.WndProc(ref System.Windows.Forms.Message m) + 0x547 bytes 
    System.Windows.Forms.dll!System.Windows.Forms.Form.WndProc(ref System.Windows.Forms.Message m) + 0x6d bytes 
    System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.DebuggableCallback(System.IntPtr hWnd, int msg = 2, System.IntPtr wparam, System.IntPtr lparam) + 0x15e bytes    
    [Native to Managed Transition]  
    [Managed to Native Transition]  
    System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.DestroyHandle() + 0xf7 bytes 
    System.Windows.Forms.dll!System.Windows.Forms.Control.DestroyHandle() + 0x3e3 bytes 
    System.Windows.Forms.dll!System.Windows.Forms.Control.Dispose(bool disposing) + 0x347 bytes 
    System.Windows.Forms.dll!System.Windows.Forms.ContainerControl.Dispose(bool disposing) + 0x19 bytes 
    System.Windows.Forms.dll!System.Windows.Forms.Form.Dispose(bool disposing) + 0x26a bytes    
    System.dll!System.ComponentModel.Component.Dispose() + 0x1b bytes   
    System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadWindows.Dispose() + 0xb3 bytes  
    System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.DisposeThreadWindows() + 0x12d bytes    
    System.Windows.Forms.dll!System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(System.IntPtr dwComponentID, int reason, int pvLoopData) + 0x58e bytes    
    System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(int reason = 4, System.Windows.Forms.ApplicationContext context = {System.Windows.Forms.Application.ModalApplicationContext}) + 0x593 bytes 
    System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoop(int reason, System.Windows.Forms.ApplicationContext context) + 0x81 bytes    
    System.Windows.Forms.dll!System.Windows.Forms.Form.ShowDialog(System.Windows.Forms.IWin32Window owner) + 0x765 bytes    
    Esfand.exe!Esfand.Program.Main(string[] a = {string[0]}) Line 51 + 0x10 bytes   C#

【问题讨论】:

  • 尝试在对话框中设置断点,我猜它会抛出异常
  • 还要确保在主线程上执行时创建并显示对话框。当您尝试从后台线程创建或显示 UI 表单时,就会发生不好的事情。
  • 您是否尝试创建form 的对象然后调用它??
  • @dthorpe 我正在为此使用主线程,我很确定。
  • 为 FormClosing 事件添加事件处理程序并在其上设置断点。查看调用堆栈。

标签: c# .net multithreading winforms


【解决方案1】:

这件事让我的程序中的每一个表单都引发了一个独特的错误(例如,“无法注册拖放事件处理程序”)

这是对代码中核心问题的强烈暗示。当您有任何控件的 AllowDrop 属性设置为 true 时,将调用 RegisterDragDrop() 本机函数。创建表单的本机窗口时调用它。不幸的是,如果您拥有 64 位版本的 Windows 并且您强制您的程序在 32 位模式下运行,那么对于引发任何异常都是一个非常糟糕的时机。 this answer的主题。

RegisterDragDrop() 失败的充分理由很少。但是一个。我们已经可以从您的 sn-p 中看出您一直在修改 Program.cs。它的样板版本如下所示:

static class Program {
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]                // <=== Here!!!
    static void Main() {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }
}

我在重要的代码行放了一个大箭头。 [STAThread] 属性在任何 GUI 程序中都是必不可少的。它告诉 CLR 它应该初始化 COM 并将程序的主线程配置为单线程单元。公寓是一个非常重要的 COM 实现细节,其范围有点超出这个答案。如果缺少该属性,则程序的主线程将加入 MTA,即多线程单元。非线程安全的代码(例如拖放、剪贴板和 shell 对话框)的危险场所。

忘记使用该属性会导致令人困惑的异常。当您的开发机器启动 64 位版本的 Vista 或 Win7 时尤其糟糕,Windows 版本在关键时刻引发异常时遇到问题,如链接答案中所述。

您的 Program.Main() 方法的正确版本,不存在此问题并使用推荐的做法:

    [STAThread]                // <=== Don't forget this
    static void Main() {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        using (var login = new LoginForm()) {
            if (login.ShowDialog() != DialogResult.OK) return;
        }
        Application.Run(new MainForm());
    }

【讨论】:

  • +1,我会尽快检查一下。我几天前无法访问上游代码。
  • 好吧,我无法提供另一种解释,为什么 RegisterDragDrop() 会失败,对您的执行环境不够了解,而且我看不到您的 Main() 方法或登录表单。否则它会解释您所看到的一切,因此验证您的假设。添加一行代码调用 System.Threading.Thread.CurrentThread.GetApartmentState() 并确保您获得 STA。
  • 如果您在 LoginForm 上使用任何 ActiveX 组件,请考虑它可能存在错误并调用 CoUninitialize(),这也是致命的。
  • 我没有使用任何外部库或 ActiveX 组件。
  • 很难帮助那些一次只提供一点点信息的 SO 用户。您需要正确记录您的问题,并给我们一个很好的机会来诊断它。为此,请创建一个显示此问题的最小重现项目并将其发布到文件共享服务。
【解决方案2】:

尝试检查是否抛出了线程异常错误。检查您是否在 Application_ThreadException 事件中看到任何内容。

static void Main()
{
    Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
    try
    {
        //Your existing code
    }
    catch (Exception ex)
    {
    }
}

private static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
{
}

编辑: 另一种选择是将 MainForm 显式设置为正在创建的 newForm 的所有者。

newForm.ShowDialog(MainForm);

我感觉所有者默认设置为您的登录表单,该表单已关闭,这将自动关闭您的新表单

【讨论】:

  • +1,不知道那个。希望它能起作用,但什么也没发生。
  • 你能不能把你的电话改成DialogResult dialogResult= (new System.Windows.Forms.Form()).ShowDialog();这样的东西,然后检查dialogResult中的值。
  • 另一种选择是将所有者表单设置为 MainForm,例如 newForm.ShowDialog(MainForm)
  • 两个都试一下,每次都需要几分钟,因为登录时间太长了。
  • DialogResult 是 Cancel=2 另一个我做不到,因为我们说的窗体是主窗体。
【解决方案3】:

当事件队列上有 WM_QUIT 消息时,IMsoComponentManager.FPushMessageLoopP() 似乎会调用 Application.ThreadContext.DisposeThreadWindows()

您是否有机会在LoginForm 的按钮事件处理程序中发布退出消息?

【讨论】:

  • 不,整个应用程序中没有API调用之类的东西,甚至没有不安全的代码,没有任何dll的纯纯.Net,系统类中没有反射,没有一行hacky代码,这就是伤害我的原因。
  • 好的,您是否编写了任何自定义代码来关闭 LoginForm?您应该依赖 OK/Cancel 的 DialogResult 属性或您拥有的任何按钮。
  • this.Close() 在对话框上不是必需的 - 您应该在按钮上设置 DialogResult 属性 - 对话框将关闭。我对此感觉不好 - 虽然我不能说它会解决您的问题,但您可以尝试删除 this.Close() 调用
  • 实际上 Close() 会导致 WM_CLOSE 而不是 WM_QUIT - 嗯.. 我想你可能想在LoginForm 中透露更多关于你在做什么的信息 - 我几乎可以肯定你已经最终在您的事件队列中出现了 WM_QUIT。
【解决方案4】:

尝试将表单定义为类成员。不在函数内部。

【讨论】:

    【解决方案5】:

    您的Main 方法看起来很奇怪。我想你错过了给Application.Run() 的电话。由于您不希望应用程序在登录表单关闭后立即退出,您可能需要ApplicationContext。 MSDN有一个例子:http://msdn.microsoft.com/en-us/library/ms157901.aspx

    另一种可能性是调用Application.Run() 并使用一个不可见的表单作为参数,然后显示其他表单(并且在应用程序退出之前永远不会关闭),但在我看来这是一个非常丑陋的黑客攻击。

    【讨论】:

      【解决方案6】:

      我的理解是表单需要在应用程序上下文中运行。

      我继承了一些从 Main 上下文中启动多个表单的代码,方式如下:

      var form1 = Form1();
      Application.Run(form1);
      
      // form returns, check public method form.ButtonPushed etc
      if (form.Button1Pushed)
      {
          var form2 = Form2();
          Application.Run(form2);
      }
      

      这将成功启动多个表单。

      这感觉不是一种非常优雅的做事方式,但它确实有效......

      【讨论】:

        猜你喜欢
        • 2019-06-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-01-09
        • 1970-01-01
        • 2013-10-10
        • 2013-03-04
        • 2015-06-29
        相关资源
        最近更新 更多