using System.Runtime.InteropServices; using System.Text; using System; using System.Drawing; using System.Windows.Forms; using System.ComponentModel; using System.Security.Permissions; namespace BizHawk { /// /// Component wrapping access to the Browse For Folder common dialog box. /// Call the ShowDialog() method to bring the dialog box up. /// public sealed class FolderBrowserEx : Component { private static readonly int MAX_PATH = 260; // Root node of the tree view. private FolderID startLocation = FolderID.Desktop; // Browse info options. private int publicOptions = (int) Win32API.Shell32.BffStyles.RestrictToFilesystem | (int) Win32API.Shell32.BffStyles.RestrictToDomain; private int privateOptions = (int)(Win32API.Shell32.BffStyles.NewDialogStyle | Win32API.Shell32.BffStyles.ShowTextBox); // Description text to show. public string Description = "Please select a folder below:"; // Folder chosen by the user. private string directoryPath = String.Empty; /// /// Enum of CSIDLs identifying standard shell folders. /// public enum FolderID { Desktop = 0x0000, Printers = 0x0004, MyDocuments = 0x0005, Favorites = 0x0006, Recent = 0x0008, SendTo = 0x0009, StartMenu = 0x000b, MyComputer = 0x0011, NetworkNeighborhood = 0x0012, Templates = 0x0015, MyPictures = 0x0027, NetAndDialUpConnections = 0x0031, } /// /// Helper function that returns the IMalloc interface used by the shell. /// private static Win32API.IMalloc GetSHMalloc() { Win32API.IMalloc malloc; Win32API.Shell32.SHGetMalloc(out malloc); return malloc; } /// /// Shows the folder browser dialog box. /// public DialogResult ShowDialog() { return ShowDialog(null); } private int callback(IntPtr hwnd, uint uMsg, IntPtr lParam, IntPtr lpData) { switch (uMsg) { case 1: { IntPtr str = Marshal.StringToHGlobalUni(SelectedPath); Win32.SendMessage(hwnd, (0x400 + 103), 1, str.ToInt32()); Marshal.FreeHGlobal(str); break; } } return 0; } /// /// Shows the folder browser dialog box with the specified owner window. /// public DialogResult ShowDialog(IWin32Window owner) { IntPtr pidlRoot = IntPtr.Zero; // Get/find an owner HWND for this dialog. IntPtr hWndOwner; if (owner != null) { hWndOwner = owner.Handle; } else { hWndOwner = Win32API.GetActiveWindow(); } // Get the IDL for the specific startLocation. Win32API.Shell32.SHGetSpecialFolderLocation(hWndOwner, (int) startLocation, out pidlRoot); if (pidlRoot == IntPtr.Zero) { return DialogResult.Cancel; } int mergedOptions = (int) publicOptions | (int) privateOptions; if ((mergedOptions & (int) Win32API.Shell32.BffStyles.NewDialogStyle) != 0) { if (System.Threading.ApartmentState.MTA == Application.OleRequired()) mergedOptions = mergedOptions & (~(int) Win32API.Shell32.BffStyles.NewDialogStyle); } IntPtr pidlRet = IntPtr.Zero; try { // Construct a BROWSEINFO. Win32API.Shell32.BROWSEINFO bi = new Win32API.Shell32.BROWSEINFO(); IntPtr buffer = Marshal.AllocHGlobal(MAX_PATH); bi.pidlRoot = pidlRoot; bi.hwndOwner = hWndOwner; bi.pszDisplayName = buffer; bi.lpszTitle = Description; bi.ulFlags = mergedOptions; bi.lpfn = new Win32API.Shell32.BFFCALLBACK(callback); // The rest of the fields are initialized to zero by the constructor. // bi.lParam = IntPtr.Zero; bi.iImage = 0; // Show the dialog. pidlRet = Win32API.Shell32.SHBrowseForFolder(ref bi); // Free the buffer you've allocated on the global heap. Marshal.FreeHGlobal(buffer); if (pidlRet == IntPtr.Zero) { // User clicked Cancel. return DialogResult.Cancel; } // Then retrieve the path from the IDList. StringBuilder sb = new StringBuilder(MAX_PATH); if (0 == Win32API.Shell32.SHGetPathFromIDList(pidlRet, sb)) { return DialogResult.Cancel; } // Convert to a string. directoryPath = sb.ToString(); } finally { Win32API.IMalloc malloc = GetSHMalloc(); malloc.Free(pidlRoot); if (pidlRet != IntPtr.Zero) { malloc.Free(pidlRet); } } return DialogResult.OK; } /// /// Helper function used to set and reset bits in the publicOptions bitfield. /// private void SetOptionField(int mask, bool turnOn) { if (turnOn) publicOptions |= mask; else publicOptions &= ~mask; } /// /// Only return file system directories. If the user selects folders /// that are not part of the file system, the OK button is unavailable. /// [Category("Navigation")] [Description("Only return file system directories. If the user selects folders " + "that are not part of the file system, the OK button is unavailable.")] [DefaultValue(true)] public bool OnlyFilesystem { get { return (publicOptions & (int) Win32API.Shell32.BffStyles.RestrictToFilesystem) != 0; } set { SetOptionField((int) Win32API.Shell32.BffStyles.RestrictToFilesystem, value); } } /// /// Location of the root folder from which to start browsing. Only the specified /// folder and any folders beneath it in the namespace hierarchy appear /// in the dialog box. /// [Category("Navigation")] [Description("Location of the root folder from which to start browsing. Only the specified " + "folder and any folders beneath it in the namespace hierarchy appear " + "in the dialog box.")] [DefaultValue(typeof (FolderID), "0")] public FolderID StartLocation { get { return startLocation; } set { new UIPermission(UIPermissionWindow.AllWindows).Demand(); startLocation = value; } } public string SelectedPath; } internal class Win32API { // C# representation of the IMalloc interface. [InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("00000002-0000-0000-C000-000000000046")] public interface IMalloc { [PreserveSig] IntPtr Alloc([In] int cb); [PreserveSig] IntPtr Realloc([In] IntPtr pv, [In] int cb); [PreserveSig] void Free([In] IntPtr pv); [PreserveSig] int GetSize([In] IntPtr pv); [PreserveSig] int DidAlloc(IntPtr pv); [PreserveSig] void HeapMinimize(); } [DllImport("User32.DLL")] public static extern IntPtr GetActiveWindow(); public class Shell32 { // Styles used in the BROWSEINFO.ulFlags field. [Flags] public enum BffStyles { RestrictToFilesystem = 0x0001, // BIF_RETURNONLYFSDIRS RestrictToDomain = 0x0002, // BIF_DONTGOBELOWDOMAIN RestrictToSubfolders = 0x0008, // BIF_RETURNFSANCESTORS ShowTextBox = 0x0010, // BIF_EDITBOX ValidateSelection = 0x0020, // BIF_VALIDATE NewDialogStyle = 0x0040, // BIF_NEWDIALOGSTYLE BrowseForComputer = 0x1000, // BIF_BROWSEFORCOMPUTER BrowseForPrinter = 0x2000, // BIF_BROWSEFORPRINTER BrowseForEverything = 0x4000, // BIF_BROWSEINCLUDEFILES } // Delegate type used in BROWSEINFO.lpfn field. public delegate int BFFCALLBACK(IntPtr hwnd, uint uMsg, IntPtr lParam, IntPtr lpData); [StructLayout(LayoutKind.Sequential, Pack = 8)] public struct BROWSEINFO { public IntPtr hwndOwner; public IntPtr pidlRoot; public IntPtr pszDisplayName; [MarshalAs(UnmanagedType.LPTStr)] public string lpszTitle; public int ulFlags; [MarshalAs(UnmanagedType.FunctionPtr)] public BFFCALLBACK lpfn; public IntPtr lParam; public int iImage; } [DllImport("Shell32.DLL")] public static extern int SHGetMalloc(out IMalloc ppMalloc); [DllImport("Shell32.DLL")] public static extern int SHGetSpecialFolderLocation( IntPtr hwndOwner, int nFolder, out IntPtr ppidl); [DllImport("Shell32.DLL")] public static extern int SHGetPathFromIDList( IntPtr pidl, StringBuilder Path); [DllImport("Shell32.DLL", CharSet = CharSet.Auto)] public static extern IntPtr SHBrowseForFolder(ref BROWSEINFO bi); } } }