为什么我得到“对象目前在其他地方使用”的例外情况?

时间:2010-07-09 20:45:21

标签: c# user-interface message-pump

我有一个C#WinForms应用程序。在启动主UI表单之前显示DevExpress XtraMessageBox时,会在static 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()启动了splash表单,它是轻量级和快速的。在启动表单中,然后我实例化主表单,激活它并隐藏启动表单。

1 个答案:

答案 0 :(得分:1)

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

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

我的建议是,加载主窗体并让主窗体调用启动屏幕,然后再执行任何正常的FormLoad内容。如果许可失败,您可以从Application.Exit()拨打returnFormLoad,从而在用户可以使用之前关闭您的应用。

修改:请注意,在FormLoad退出之后,主窗体才会显示,因此您无需担心在初始屏幕显示时隐藏主窗体。< / p>

编辑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);
    }
}

然后你可以在你的主要电话中打电话:

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

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

编辑3:我意识到这里可能存在一些线程安全问题,您可能还需要解决。查看CodeProject article。它比我在这里做得更好。