Rozpoczynam cykl z C# z którym styczność mam na co dzień , otóż przychodzi mi rozwiązać łatwiejsze oraz trudniejsze zadania(subiektywne podejście ;)) ale dzisiaj natrafiłem na problem z wyświetleniem FolderDialogu – wyboru katalogu podczas instalacji MSI.
Wszystko działało w aplikacji testowej – jednak z instalacją był problem, po resarchu natrafiłem na taki artykuł:
http://blogs.msdn.com/b/smondal/archive/2012/12/31/10059279.aspx
wyjaśniający takie zachowanie , ogólnie chodzi o różnice działania całej aplikacji a w szczególności między wątkami STA i MTA , ogólnie chodzi o relikt przeszłości kiedy to systemy nie miały możliwości zrównoleglenia…, odsyłam do krótkiego artykułu:
http://www.pzielinski.com/?p=846
Przedstawię tutaj sposób na rozwiązanie tego problemu wzorując się na powyższym artykule z msdn :
Tworzę klasę pomocniczą:
public class DialogState { public DialogResult result; public FolderBrowserDialog dialog; public IWin32Window parent; public void ThreadProcShowDialog() { result = dialog.ShowDialog(parent); } }
w zasadzie jedyną zastanawiającą rzeczą może być argument metody ShowDialog, otóż moje okienko jest TopMost – widoczne najwyżej , przekazując rodzica do wywołania ShowDialog moje okienko wyboru folderu będzie jeszcze wyżej 🙂
Następnie metoda wykonania wątku STA
private DialogResult STAShowDialog(FolderBrowserDialog dialog) { DialogState state = new DialogState(); state.dialog = dialog; state.parent = this; System.Threading.Thread t = new System.Threading.Thread(state.ThreadProcShowDialog); t.SetApartmentState(System.Threading.ApartmentState.STA); t.Start(); t.Join(); return state.result; }
Chyba najważniejsza linijka dotycząca wątku STA:
t.SetApartmentState(System.Threading.ApartmentState.STA);
określa jaki to będzie wątek – tu tkwi cały problem instalator MSI działa jako MTA i nie wyświetli nam okienka z wyborem folderu(w moim przypadku) , następnie zapewniona synchronizacja poprzez join ,jako parenta przekazuje this – obiekt(form) na rzecz którego działa dialog.
Zastosowanie:
using (FolderBrowserDialog dialog = new FolderBrowserDialog()) { dialog.Description = "opis"; dialog.ShowNewFolderButton = false; dialog.RootFolder = Environment.SpecialFolder.MyComputer; DialogResult ret = STAShowDialog(dialog); if (ret == DialogResult.OK) { //do stuff } }
jak widać zamiast wywoływać bezpośrednio ShowDialog na obiekcie dialog stosujemy metodę STAShowDialog.
W moim przypadku musiałem zmodyfikować jeszcze tworzenie samego forma bazowego chcąc użyć go jako rodzica w argumencie metody ShowDialog – musi być stworzony jako STA.