0

我有一个 C# WinForms 应用程序。当在启动主 UI 表单之前显示 DevExpress XtraMessageBox 时,静态 void Main() 方法中会引发此异常。以下是代码(简化):

static void Main(string[] args)
{
    // Display Splash Screen.
    SplashForm.Start();

    if (!CheckLicense())
        XtraMessageBox.Show(null, "Not Licensed!", "License Check",
            MessageBoxButtons.OK, MessageBoxIcon.Information);

    using (MainForm form = new MainForm())
    {
        SplashForm.Stop();

        if (form != null)
            Application.Run(form);
    }
}

虽然它是一个 DevExpress 控件,但实际上在调用以下命令时会引发异常:

System.Drawing.Graphics.get_PageUnit()

异常不会始终如一地抛出。它可以在特定机器上重现,但是一旦我在异常之前添加一个 MicroSoft MessageBox.Show() 以显示调试信息,那么我就不再得到异常。这是堆栈跟踪:

Object is currently in use elsewhere.
   at System.Drawing.Graphics.get_PageUnit()
   at DevExpress.Utils.Text.FontsCache.GetFontCacheByFont(Graphics graphics, Font font)
   at DevExpress.Utils.Text.FontsCache.GetStringSize(Graphics graphics, String text, Font font, StringFormat stringFormat, Int32 maxWidth)
   at DevExpress.Utils.Text.TextUtils.GetStringSize(Graphics g, String text, Font font, StringFormat stringFormat, Int32 maxWidth)
   at DevExpress.Utils.Paint.XPaintMixed.CalcTextSize(Graphics g, String s, Font font, StringFormat strFormat, Int32 maxWidth)
   at DevExpress.Utils.AppearanceObject.CalcTextSize(Graphics g, StringFormat sf, String s, Int32 width)
   at DevExpress.Utils.AppearanceObject.CalcTextSize(Graphics g, String s, Int32 width)
   at DevExpress.XtraEditors.Drawing.EditorButtonPainter.CalcCaptionSize(EditorButtonObjectInfoArgs e)
   at DevExpress.XtraEditors.Drawing.EditorButtonPainter.CalcObjectMinBounds(ObjectInfoArgs e)
   at DevExpress.XtraEditors.Drawing.SkinEditorButtonPainter.CalcObjectMinBounds(ObjectInfoArgs e)
   at DevExpress.XtraEditors.ViewInfo.BaseButtonViewInfo.CalcBestFit(Graphics g)
   at DevExpress.XtraEditors.BaseControl.CalcBestSize()
   at DevExpress.XtraEditors.XtraMessageBoxForm.CreateButtons()
   at DevExpress.XtraEditors.XtraMessageBoxForm.ShowMessageBoxDialog()
   at DevExpress.XtraEditors.XtraMessageBoxForm.ShowMessageBoxDialog(XtraMessageBoxArgs message)
   at DevExpress.XtraEditors.XtraMessageBox.Show(UserLookAndFeel lookAndFeel, IWin32Window owner, String text, String caption, DialogResult[] buttons, Icon icon, Int32 defaultButton, MessageBoxIcon messageBeepSound)
   at DevExpress.XtraEditors.XtraMessageBox.Show(IWin32Window owner, String text, String caption, DialogResult[] buttons, Icon icon, Int32 defaultButton, MessageBoxIcon messageBeepSound)
   at DevExpress.XtraEditors.XtraMessageBox.Show(IWin32Window owner, String text, String caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton)
   at DevExpress.XtraEditors.XtraMessageBox.Show(IWin32Window owner, String text, String caption, MessageBoxButtons buttons, MessageBoxIcon icon)
   at Test.Program.Main(String[] args)

更新: 我通过确保在执行任何 UI 工作之前执行 Application.Run() 来解决它。以这种方式启动消息循环/泵。我现在让 Application.Run() 启动启动画面,它轻量级且快速。然后我从启动表单中实例化主表单,激活它并隐藏启动表单。

4

1 回答 1

1

据我了解,在Application.Run()显示任何表单之前需要专门调用,因为它启动窗口消息循环/泵并且基本上为 UI 生成一个单独的线程。

如果您不这样做,表单将无法处理消息或绘制。

我的建议是,加载主窗体并让主窗体在执行任何正常操作之前调用启动屏幕FormLoad。如果许可失败,您可以调用Application.Exit()return退出FormLoad,从而在用户使用之前关闭您的应用程序。

编辑:请注意,主窗体直到退出后才会显示FormLoad,因此您不必担心在显示初始屏幕时隐藏主窗体。

编辑 2:我发现了一些有价值的东西,使用ApplicationContext. 您可以切换主上下文中的表单,因此您可以在初始应用程序上下文中加载初始屏幕,然后在加载后将其换出。试试这个:

public class MyApplicationContext : ApplicationContext {
    SplashForm splashForm;
    MainForm mainForm;

    public MyApplicationContext() {
        splashForm = new SplashForm();
        base.MainForm = splashForm;

    }

    public void RunApplication() {
        // This will show the splash screen
        ThreadPool.QueueUserWorkItem(new WaitCallback(MessageLoopThread));

        // This will perform any miscellaneous loading functions
        splashForm.PerformLoadingFunctions();

        if (!CheckLicensing()) {
            ShowErrorMessage();
            Application.Exit();
            return;
        }

        // Now load the main form
        mainForm = new MainForm();

        // We're done loading!  Swap out our objects
        base.MainForm = mainForm;

        // Close our splash screen
        splashForm.Close();
        splashForm.Dispose();
        splashForm = null;
    }

    private void MessageLoopThread(object o) {
        Application.Run(this);
    }
}

然后你可以在你的 main 中调用它:

static void Main() {
    MyApplicationContext applicationContext = new MyApplicationContext();
    applicationContext.RunApplication();
}

我没有对此进行测试,但理论上它应该可以工作。

编辑 3:我意识到这里可能存在一些线程安全问题,您可能也必须解决这些问题。查看CodeProject 文章。它比我在这里做的更好。

于 2010-07-09T21:00:09.380 回答