ApplicationContext ještě jednou a lépe

Udělat chybu je celkem lidské, že to však dopadne takto jsem tedy vůbec nepředpokládal. Když jsem nedávno psal o splash screenu a jak jej zobrazit za pomoci třídy ApplicationContext vůbec jsem netušil, jaký to bude mít dopad a že něco není v pořádku.

Chyba není na vašem přijímači

To jediné mě malinko uklidňuje, že chyba není ani na vašem a kupodivu ani na mém přijímači. Tedy samozřejmě v přeneseném slova smyslu (je třeba si za tím představit napsaný kód). A o jakou vlastně chybu se jedná? Při použití SaveFileDialogu a pokusu o přepsání již existujícího souboru by se měl objevit dialog zda chceme soubor přepsat. Místo toho však aplikace vyvolá vyjímku Cannot access a disposed object named "FormSplash". S výpisem tohoto Stack trace:

-----[Core exception]--------------------at System.Windows.Forms.Control.CreateHandle()at System.Windows.Forms.Form.CreateHandle()at System.Windows.Forms.Control.get_Handle()at System.Windows.Forms.ThreadWindows.Callback(IntPtr hWnd, IntPtr lparam)at System.Windows.Forms.UnsafeNativeMethods.EnumThreadWindows(Int32 dwThreadId, EnumThreadWindowsCallback lpfn, HandleRef lParam)at System.Windows.Forms.ThreadWindows..ctor(Control parent, Boolean onlyWinForms)at System.Windows.Forms.ThreadContext.DisableWindowsForModalLoop(Boolean onlyWinForms)at System.Windows.Forms.ThreadContext.BeginModalMessageLoop()at System.Windows.Forms.Application.BeginModalMessageLoop()at System.Windows.Forms.MessageBox.ShowCore(IWin32Window owner, String text, String caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton, MessageBoxOptions options)at System.Windows.Forms.MessageBox.Show(String text, String caption, MessageBoxButtons buttons, MessageBoxIcon icon)at System.Windows.Forms.FileDialog.MessageBoxWithFocusRestore(String message, String caption, MessageBoxButtons buttons, MessageBoxIcon icon)at System.Windows.Forms.SaveFileDialog.PromptFileOverwrite(String fileName)at System.Windows.Forms.SaveFileDialog.PromptUserIfAppropriate(String fileName)

To je, zjednodušeně řečeno, způsobené tím, že smyčka zpráv se vytvořila nad formulářem FormSplash a přestože došlo následně k přiřazení hlavního formuláře do property MainForm ve třídě ApplicationContext v přepsané metodě OnMainFormClosed, smyčka zpráv se nepředala a zůstala nad již zavřeným formulářem.

Finální řešení

Mohli bychom samozřejmě provádět ještě šílenější obezličky než za chvíli zmíněné řešení, ale proč. Určitě je možné zobrazovat Splash screen v obsluze události Load hlavního formuláře, nebo vymyslet ještě obskurnější řešení, většinou však tato řešení budou poměrně hodně svázána s konkrétním hlavním formulářem a nebude tak možné mít jednu obecnou třídu pro zobrazení Splash screenu.

Navržené řešení je triviální, leč ne úplně čisté a děkovat za něj mohu dobrému pomocníkovi Reflectoru. Je totiž nutné přenastavit interní proměnnou currentForm ve třídě ThreadContext, která je interní třídou v ApplicationContext.

Část kódu, který je tedy přítomen v metodě OnMainFormClosed vypadá následovně a prosím všechny ty, kteří použili mnou navržené řešení, o opravu v jejich kódu:

_main = new FormMain();Type application = typeof(Application);Type threadContext = application.GetNestedType("ThreadContext", BindingFlags.NonPublic); object current = threadContext.InvokeMember("FromCurrent", BindingFlags.Static | BindingFlags. NonPublic | BindingFlags.InvokeMethod, null, null, new object[0]);FieldInfo currentForm = threadContext.GetField("currentForm", BindingFlags.NonPublic | BindingFlags.Instance);currentForm.SetValue(current, _main); this.MainForm = _main;this.MainForm.Show();

Proč považuji toto řešení za ne úplně čisté je celkem jasné, je využívána reflection, což by samo o sobě vadit nemělo, avšak je nutné uvést textově odkazy na získání interních proměnných, jejichž název se může v příští verzi změnit.

Po provedení této úpravy by se aplikace měla chovat tak jak očekáváme a tak jak jsme ji napsali.

2 Comments

  • beran said


    <p>dobry den,</p>
    <p>proc pisu zrovna sem, vysvetlim zahy.</p>
    <p>Dotaz: Nevite, prosim, proc v Property Pan objektu Form ve VS2005 neni vlastnost Visible? V runtimu mohu tuto vlastnost bezne menit, kdyz pisi kod, tak mi ji pri pouziti 'this' VS2005 nabizi, ja bych ale potreboval spustit aplikaci s hlavnim formularem skrytym (videt bude toliko NotifyIcon) a to se mi nedari.</p>
    <p>Kdosi mi doporucil, at se poohlednu po ApplicationContext a tak jsem se ocitl tady.</p>
    <p>Pokud, prosim, muzete pomoci, byl bych vdecen.</p>
    <p>Dekuji</p>

  • said


    <p>Nakonec uveřejňuji následující text do samostatného příspěvku, trošku více jsem se rozepsal na otázku</p>

Add a Comment