Zawieszenie okienka (dialog-u) np. FolderDialog podczas instalacji msi

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.