显示和显示对话框不起作用

3

这不像我以前见过的任何东西。
当我调用(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
}

在新线程中创建并显示主窗体使一切重新开始工作。但是在每个窗体上出现随机错误,这使得这种解决方案不够好。 编辑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#

2
尝试在对话框中设置一个断点,我猜它会抛出一个异常。 - undefined
请确保在主线程上执行时创建并显示对话框。当尝试在后台线程中创建或显示用户界面表单时,可能会发生问题。 - undefined
你试过创建一个form对象然后调用它吗? - undefined
1
为FormClosing事件添加事件处理程序并在其上设置断点。查看调用堆栈。 - undefined
@Behrooz:如果你将ShowDialog替换为show,它能正常工作吗?如果你将Mainform替换为只是Form f = new Form();,它能正常工作吗? - undefined
显示剩余15条评论
6个回答

6
这个问题导致我的程序中每个表单都出现了一个独特的错误(例如“无法注册拖放事件处理程序”)。 这是你代码核心问题明显的提示。只要有任何AllowDrop属性设置为true的控件,就会调用RegisterDragDrop()本地函数。当创建窗体的本地窗口时,该函数将被调用。不幸的是,如果你在64位版本的Windows上强制运行程序以32位模式运行,任何异常在此时被抛出都是非常糟糕的。参考此答案
RegisterDragDrop()失败的原因很少。但是有一种情况。从你附带的代码段可以看出,你已经在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返回。 - undefined
如果您在登录表单中使用任何ActiveX组件,请考虑它可能存在错误并调用CoUninitialize()的可能性,这也将是致命的。 - undefined
我只是在考虑这种情况可能会发生在System.Net.Sockets吗?我有很多关于登录的网络相关的东西,我经常进行更改。登录是在一个只有对System.dll的一个引用的库中进行的。 - undefined
5
帮助那些只逐渐提供少量信息的SO用户是非常困难的。您需要正确记录您的问题并给我们一个合理的机会来诊断它。为此,请创建一个最小复现项目来展示这个问题,并将其发布到文件共享服务中。请注意,确保文档内容通俗易懂,不改变原意。 - undefined
我会做的,但是需要时间。 - undefined
显示剩余4条评论

1

看起来 IMsoComponentManager.FPushMessageLoopP() 在事件队列中出现 WM_QUIT 消息时会调用 Application.ThreadContext.DisposeThreadWindows()

你是否在 LoginForm 的按钮事件处理程序中发布了退出消息?


不,整个应用程序中没有任何API调用或类似的东西,甚至没有不安全的代码,纯粹的.Net而且没有任何dll,系统类中也没有反射,没有一行不正规的代码,这是令我痛心的地方。 - undefined
好的,你有没有编写任何自定义代码来关闭LoginForm?你应该依赖于OK/Cancel或其他按钮的DialogResult属性。 - undefined
在对话框中不需要使用this.Close() - 你应该在按钮上设置DialogResult属性 - 对话框会自动关闭。我对此有一种不好的感觉 - 虽然我不能说这会解决你的问题,但你可以尝试移除this.Close()调用。 - undefined
实际上,Close() 会引起 WM_CLOSE 而不是 WM_QUIT - 嗯...我猜你可能想透露更多关于你在 "LoginForm" 中正在做的事情 - 我几乎确定你的事件队列中出现了 WM_QUIT。 - undefined

1

尝试检查是否抛出了线程异常错误。检查应用程序的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,之前不知道这个。希望它能够工作,但是什么都没有发生。 - undefined
你可以将你的调用更改为类似这样的形式 DialogResult dialogResult= (new System.Windows.Forms.Form()).ShowDialog(); ,然后检查 dialogResult 的值。 - undefined
另一个选项是将所有者窗体设置为主窗体,例如 newForm.ShowDialog(MainForm) - undefined
我会尝试两种方法。每次都要花几分钟,因为登录过程太长了。 - undefined
对话框结果是Cancel=2,而另一个我无法做,因为我们所谈论的是主窗体。 - undefined

0
尝试将该形式定义为类成员,而不是在函数内部。

0
你的 Main 方法看起来有点奇怪。我认为你忘了调用 Application.Run()。由于你不希望应用程序在登录窗体关闭后立即退出,可能需要一个 ApplicationContext。MSDN上有一个示例:http://msdn.microsoft.com/en-us/library/ms157901.aspx 另一种可能性是将 Application.Run() 作为参数传递给一个不可见的窗体,然后在该窗体中显示其他窗体(直到应用程序退出),但在我看来这是一种相当丑陋的破解方法。

0

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

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

var form1 = Form1();
Application.Run(form1);

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

这将成功启动几种形式。
虽然这不是一种非常优雅的做事方式,但它确实有效...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接