Launching Dialog Windows in Managed MMC Snapins
Launching a child window inside MMC 3.0 from managed code isn’t easy. Different APIs are required for different circumstances.
I don’t know exactly why this is, but I assume it has something to do with all the tricks MMC does to give each managed snapin its own UI thread, and needing to marshal a request back to the main UI thread owned by the console.
There are 3 APIs you need to know about when launching a child window (modal or modeless) inside a managed snapin.
1) If you want a child window of the console then use Console.Show/ShowDialog
2) If you want a child window of a property sheet then use PropertySheet.Show/ShowDialog
3) If you want a child window of a winforms window (previously launched w/ Console/PropertySheet APIs) then use Form.Show/ShowDialog
The same rules apply for simple MessageBoxes. The APIs to use are Console.ShowDialog(MessageBoxPrameters), PropertySheet.ShowDialog(MessageBoxParameters) and MessageBox.Show(). Follow the same rules as above.
In one project I worked on we had some common code which needed to launch dialogs & show messages – but that code could be called from anywhere and so it didn’t know what API to call. What we did was make an interface called INotifyUser which had Show/ShowMessageBox/ShowDialog methods. Then we had 3 concrete implementations, one for each case – ConsoleNotifyUser, PropertyPageNotifyUser and WinformsNotifyUser. The caller of the common code would pass in the correct implementation, allowing these API differences to be abstracted away from the common code.
As time went on this interface changed into an abstract class, NotifyUser. This class had abstract methods for Show/ShowMessageBox/ShowDialog, for the concrete sub-classes. It also added higher level methods like ShowYesNoDialog, ShowConfirmDialog, etc… which internally called ShowMessageBox with the right parameters.
This approach turned out very well. Everything dealing with the details of child windows was encapsulated in one place, which provided a nice consistent API.
Late in the project we had a spec change to use the Task Dialog in Windows for things like the YesNo and Confirm dialogs. After implementing a TaskDialog class which P/Invoked the APIs, we were able to wire it into the NotifyUser.ShowYesNoDialog/ShowConfirmDialog/etc implementations – and update all our dialogs without having to hunt down all the code which dealt with these dialogs.
After that it was straight forward to update the methods, and callers, to take advantage of the additional functionality of the Task Dialog (extra text sections, icons, etc…).
I thought it actually worked out rather nice, and is a great example of the sort of power you get when you encapsulate logical functionality behind an interface. And those unit testers out there will appreciate how this helped make the logic more testable by allowing test code to use its own test double implementation of NotifyUser.
Enjoy