Use the upgraded FolderBrowserDialog ("Vista style") in Powershell Use the upgraded FolderBrowserDialog ("Vista style") in Powershell powershell powershell

Use the upgraded FolderBrowserDialog ("Vista style") in Powershell


Wow, you can use C# in PowerShell!

Looking around, I got envious of everybody playing around in C# and leveraging cool features I don't know how to access in PowerShell.
I liked this approach, for example, which doesn't rely on legacy APIs and has a fallback for unsupported systems.
And then I saw that you can use actual C# in PowerShell!I put the two together, modified the code a bit to make it easier to call from PS, and out comes a fairly lightweight, hopefully robust way to summon the best Folder Browser Dialog available to the user:

[Revised code below]

I'm interested to hear opinions on how reliable the whole approach likely is.
How likely is it that some reference can't be accessed or that something else goes wrong?

Anyway, I'm pretty happy with this approach for now :)

Edit: PowerShell "Core" (pwsh.exe; Newer assemblies)

So, as @mklement0 pointed out in the comments, the actively developed PowerShell (formerly (and hereafter, for the sake of readability) known as "PowerShell Core"; as oppowed to the Windows PowerShell that comes with Windows) doesn't seem to play as nicely with this.After looking into what PS Core unhelpfully only reported as "The type initializer for 'VistaDialog' threw an exception.", (and adding a reference to System.ComponentModel.Primitives), it turns out PS Core tends to use a newer version of System.Windows.Forms, in my casee 5.0.4.0, which doesn't contain a type FileDialogNative, let alone its nested type IFileDialog.
I tried to force it to use the version that Windows PS referenced (4.0.0.0), but it wouldn't comply.

Well, I finally had the penny drop and had PS Core simply use the default dialog, which is already the upgraded one I was after in the first place 😂

So, along with the conceptual change of introducing a constructor method with optional parameters, I added a fallback for a failing "Vista Dialog".
Instead of checking for versions of PS or individual assemblies and/or classes/types, I simply wrapped the call in a try/catch block.

$path = $args[0]$title = $args[1]$message = $args[2]$source = @'using System;using System.Diagnostics;using System.Reflection;using System.Windows.Forms;/// <summary>/// Present the Windows Vista-style open file dialog to select a folder. Fall back for older Windows Versions/// </summary>#pragma warning disable 0219, 0414, 0162public class FolderSelectDialog {    private string _initialDirectory;    private string _title;    private string _message;    private string _fileName = "";        public string InitialDirectory {        get { return string.IsNullOrEmpty(_initialDirectory) ? Environment.CurrentDirectory : _initialDirectory; }        set { _initialDirectory = value; }    }    public string Title {        get { return _title ?? "Select a folder"; }        set { _title = value; }    }    public string Message {        get { return _message ?? _title ?? "Select a folder"; }        set { _message = value; }    }    public string FileName { get { return _fileName; } }    public FolderSelectDialog(string defaultPath="MyComputer", string title="Select a folder", string message=""){        InitialDirectory = defaultPath;        Title = title;        Message = message;    }        public bool Show() { return Show(IntPtr.Zero); }    /// <param name="hWndOwner">Handle of the control or window to be the parent of the file dialog</param>    /// <returns>true if the user clicks OK</returns>    public bool Show(IntPtr? hWndOwnerNullable=null) {        IntPtr hWndOwner = IntPtr.Zero;        if(hWndOwnerNullable!=null)            hWndOwner = (IntPtr)hWndOwnerNullable;        if(Environment.OSVersion.Version.Major >= 6){            try{                var resulta = VistaDialog.Show(hWndOwner, InitialDirectory, Title, Message);                _fileName = resulta.FileName;                return resulta.Result;            }            catch(Exception){                var resultb = ShowXpDialog(hWndOwner, InitialDirectory, Title, Message);                _fileName = resultb.FileName;                return resultb.Result;            }        }        var result = ShowXpDialog(hWndOwner, InitialDirectory, Title, Message);        _fileName = result.FileName;        return result.Result;    }    private struct ShowDialogResult {        public bool Result { get; set; }        public string FileName { get; set; }    }    private static ShowDialogResult ShowXpDialog(IntPtr ownerHandle, string initialDirectory, string title, string message) {        var folderBrowserDialog = new FolderBrowserDialog {            Description = message,            SelectedPath = initialDirectory,            ShowNewFolderButton = true        };        var dialogResult = new ShowDialogResult();        if (folderBrowserDialog.ShowDialog(new WindowWrapper(ownerHandle)) == DialogResult.OK) {            dialogResult.Result = true;            dialogResult.FileName = folderBrowserDialog.SelectedPath;        }        return dialogResult;    }    private static class VistaDialog {        private const string c_foldersFilter = "Folders|\n";                private const BindingFlags c_flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;        private readonly static Assembly s_windowsFormsAssembly = typeof(FileDialog).Assembly;        private readonly static Type s_iFileDialogType = s_windowsFormsAssembly.GetType("System.Windows.Forms.FileDialogNative+IFileDialog");        private readonly static MethodInfo s_createVistaDialogMethodInfo = typeof(OpenFileDialog).GetMethod("CreateVistaDialog", c_flags);        private readonly static MethodInfo s_onBeforeVistaDialogMethodInfo = typeof(OpenFileDialog).GetMethod("OnBeforeVistaDialog", c_flags);        private readonly static MethodInfo s_getOptionsMethodInfo = typeof(FileDialog).GetMethod("GetOptions", c_flags);        private readonly static MethodInfo s_setOptionsMethodInfo = s_iFileDialogType.GetMethod("SetOptions", c_flags);        private readonly static uint s_fosPickFoldersBitFlag = (uint) s_windowsFormsAssembly            .GetType("System.Windows.Forms.FileDialogNative+FOS")            .GetField("FOS_PICKFOLDERS")            .GetValue(null);        private readonly static ConstructorInfo s_vistaDialogEventsConstructorInfo = s_windowsFormsAssembly            .GetType("System.Windows.Forms.FileDialog+VistaDialogEvents")            .GetConstructor(c_flags, null, new[] { typeof(FileDialog) }, null);        private readonly static MethodInfo s_adviseMethodInfo = s_iFileDialogType.GetMethod("Advise");        private readonly static MethodInfo s_unAdviseMethodInfo = s_iFileDialogType.GetMethod("Unadvise");        private readonly static MethodInfo s_showMethodInfo = s_iFileDialogType.GetMethod("Show");        public static ShowDialogResult Show(IntPtr ownerHandle, string initialDirectory, string title, string description) {            var openFileDialog = new OpenFileDialog {                AddExtension = false,                CheckFileExists = false,                DereferenceLinks = true,                Filter = c_foldersFilter,                InitialDirectory = initialDirectory,                Multiselect = false,                Title = title            };            var iFileDialog = s_createVistaDialogMethodInfo.Invoke(openFileDialog, new object[] { });            s_onBeforeVistaDialogMethodInfo.Invoke(openFileDialog, new[] { iFileDialog });            s_setOptionsMethodInfo.Invoke(iFileDialog, new object[] { (uint) s_getOptionsMethodInfo.Invoke(openFileDialog, new object[] { }) | s_fosPickFoldersBitFlag });            var adviseParametersWithOutputConnectionToken = new[] { s_vistaDialogEventsConstructorInfo.Invoke(new object[] { openFileDialog }), 0U };            s_adviseMethodInfo.Invoke(iFileDialog, adviseParametersWithOutputConnectionToken);            try {                int retVal = (int) s_showMethodInfo.Invoke(iFileDialog, new object[] { ownerHandle });                return new ShowDialogResult {                    Result = retVal == 0,                    FileName = openFileDialog.FileName                };            }            finally {                s_unAdviseMethodInfo.Invoke(iFileDialog, new[] { adviseParametersWithOutputConnectionToken[1] });            }        }    }    // Wrap an IWin32Window around an IntPtr    private class WindowWrapper : IWin32Window {        private readonly IntPtr _handle;        public WindowWrapper(IntPtr handle) { _handle = handle; }        public IntPtr Handle { get { return _handle; } }    }        public string getPath(){        if (Show()){            return FileName;        }        return "";    }}'@Add-Type -Language CSharp -TypeDefinition $source -ReferencedAssemblies ("System.Windows.Forms", "System.ComponentModel.Primitives")([FolderSelectDialog]::new($path, $title, $message)).getPath()

This should work for Windows PowerShell (final version ~5.1) and current PS "Core" (pwsh.exe ~7.1.3)