2010-07-09 1 views
0

У меня есть приложение C# WinForms. Это исключение выбрасывается внутри метода static void Main(), когда DevExpress XtraMessageBox отображается перед запуском основной формы пользовательского интерфейса. Ниже приведен код (упрощенно):Почему возникает исключение «Объект в настоящее время используется в другом месте»?

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) 

Update: Я решил его, убедившись, что Application.Run() выполняется перед выполнением любой работы пользовательского интерфейса. Таким образом запускается конвейер/насос сообщения. Теперь у меня есть Application.Run(), начиная с формы всплеска, которая является легкой и быстрой. Из формы всплеска я затем создаю экземпляр основной формы, активирую ее и скрываю форму всплеска.

+0

WAG: Вытяните вызов Stop() из области использования формы. – Will

ответ

1

Как я понимаю, необходимо указать специальную форму Application.Run() перед тем, как вы покажете какую-либо форму, так как она запускает цикл сообщений/насос окна и в основном создает отдельный поток для пользовательского интерфейса.

Если вы этого не сделаете, форма не сможет обрабатывать сообщения или рисовать.

Мое предложение состоит в том, чтобы загрузить основную форму и вызвать основную форму заставки, прежде чем она начнет выполнять какие-либо из ее обычных вещей FormLoad. Если лицензия не удалась, вы можете позвонить Application.Exit() и return из FormLoad, тем самым отключив приложение до того, как пользователь сможет его использовать.

Edit: Обратите внимание, что основная форма не будет отображаться, пока после 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); 
    } 
} 

Тогда вы можете вызвать его в вашем главном:

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

Я не проверял, но в теории она должна работать.

Редактировать 3: Я понял, что здесь могут быть некоторые проблемы с безопасностью потока, которые, возможно, придется обойти. Проверьте CodeProject article. Он делает это лучше, чем то, что я здесь сделал.

+0

У меня сложная форма и просто создание экземпляра может занять 1-2 секунды. Мне нужен экран заставки, который отображается сразу. Я видел различные онлайн-статьи о показе экранов Splash до вызова Application.Run(). Таким образом, мне очень интересно узнать, действительно ли нужно сначала вызвать Application.Run(). – Elan

+0

Я также искал, и хотя я нашел [эту статью] (http://msdn.microsoft.com/en-us/library/Aa446493), он не совсем делает так, как вы ожидаете: он загружает вверх по форме всплеска в 'Application.Run()' вместо. Вместо этого, если ваша форма выполняет большую обработку, вызывается форму всплеска в конструкторе основной формы перед любым другим вызовом, таким образом это первое, что выполняется. Или вы можете использовать 'Application.Run()' на своем заставке и вызывать основную форму оттуда в отдельном потоке. – rakuo15

+0

Я добавил некоторые вещи, которые я нашел в [этой статье] (http://www.codeproject.com/KB/cs/applicationcontextsplash.aspx) в CodeProject. Надеюсь, это вас доставит. – rakuo15