好久没有动手写东西了(其实以前什么也没写过;-)),今天要写的可能已经有哪位前辈完成了。拙作实在不敢拿出来现丑,高手看标题和上面的贴图就可以决定要不要看下去。不浪费大家时间。
客户的要求:有一个winform程序,用户要求可以自己选择数据文件存放的位置,简单点说就是在硬盘上选择一个目录。
最初的想法:Win32 SDK中有一个SHBrowseForFolder API,这也是平时使用最频繁的方法,既然用.net开发程序当然想尽量用.net framework提供的managed代码。
问题:查阅MSDN,发现FolderBrowser类与SHBrowseForFolder的一样的功能,但是帮助文件提示:FolderNameEditor.FolderBrowser 类型支持 .NET 框架结构,因此不适用于直接从代码中使用。ms似乎并不希望开发人员直接利用这个类,文档中没有关于这个类成员的任何说明。FolderBrowser类的申明如下:[C#]
protected sealed class FolderNameEditor.FolderBrowser : Component属性是protected和sealed.
解决方法:FolderBrowser嵌套地定义在类FolderNameEditor中,还好ms没有做绝把它也做成protected和sealed,这样通过继承FolderNameEditor,再在继承类中实例化一个FolderBrowser类就可以调用这个对话框了。至于FolderBrowser的成员可以通过其它查看.net汇编的小工具获得,推荐 使用lutz Roader's .net reflector,在下面的截图可以看到FolderBrowser的所有成员。
该类实际上只有一个方法ShowDialog,反汇编以后差一点吐血。大家看看下面的就明白了。
.maxstack 3
.locals (IntPtr V_0, IntPtr V_1, int V_2, IntPtr V_3, BROWSEINFO V_4, IntPtr V_5, IMalloc V_6, DialogResult V_7)
.try L_0056 to L_00f0 finally L_00f0 to L_0115
L_0005: stloc.0
L_0006: ldarg.1
L_0007: brfalse.s L_0012
L_0009: ldarg.1
L_000a: callvirt IWin32Window.get_Handle
L_000f: stloc.1
L_0010: br.s L_0018
L_0012: call UnsafeNativeMethods.GetActiveWindow
L_0017: stloc.1
L_0018: ldloc.1
L_0019: ldarg.0
L_001a: ldfld FolderBrowser.startLocation
L_001f: ldloca.s V_0
L_0021: call Shell32.SHGetSpecialFolderLocation
L_0026: pop
L_0027: ldloc.0
L_002d: call IntPtr.op_Equality
L_0032: brfalse.s L_0036
L_0034: ldc.i4.2
L_0035: ret
L_0036: ldarg.0
L_0037: ldfld FolderBrowser.publicOptions
L_003c: ldarg.0
L_003d: ldfld FolderBrowser.privateOptions
L_0042: or
L_0043: stloc.2
L_0044: ldloc.2
L_0045: ldc.i4.s 64
L_0047: and
L_0048: brfalse.s L_0050
L_004a: call Application.OleRequired
L_004f: pop
L_0055: stloc.3
L_0056: newobj BROWSEINFO..ctor
L_005b: stloc.s V_4
L_005d: ldsfld FolderBrowser.MAX_PATH
L_0062: call Marshal.AllocHGlobal
L_0067: stloc.s V_5
L_0069: ldloc.s V_4
L_006b: ldloc.0
L_006c: stfld BROWSEINFO.pidlRoot
L_0071: ldloc.s V_4
L_0073: ldloc.1
L_0074: stfld BROWSEINFO.hwndOwner
L_0079: ldloc.s V_4
L_007b: ldloc.s V_5
L_007d: stfld BROWSEINFO.pszDisplayName
L_0082: ldloc.s V_4
L_0084: ldarg.0
L_0085: ldfld FolderBrowser.descriptionText
L_008a: stfld BROWSEINFO.lpszTitle
L_008f: ldloc.s V_4
L_0091: ldloc.2
L_0092: stfld BROWSEINFO.ulFlags
L_0097: ldloc.s V_4
L_009e: stfld BROWSEINFO.lpfn
L_00a3: ldloc.s V_4
L_00aa: stfld BROWSEINFO.lParam
L_00af: ldloc.s V_4
L_00b1: ldc.i4.0
L_00b2: stfld BROWSEINFO.iImage
L_00b7: ldloc.s V_4
L_00b9: call Shell32.SHBrowseForFolder
L_00be: stloc.3
L_00bf: ldloc.3
L_00c5: call IntPtr.op_Equality
L_00ca: brfalse.s L_00d1
L_00cc: ldc.i4.2
L_00cd: stloc.s V_7
L_00cf: leave.s L_0117
L_00d1: ldloc.3
L_00d2: ldloc.s V_5
L_00d4: call Shell32.SHGetPathFromIDList
L_00d9: pop
L_00da: ldarg.0
L_00db: ldloc.s V_5
L_00dd: call Marshal.PtrToStringAuto
L_00e2: stfld FolderBrowser.directoryPath
L_00e7: ldloc.s V_5
L_00e9: call Marshal.FreeHGlobal
L_00ee: leave.s L_0115
L_00f0: call FolderBrowser.GetSHMalloc
L_00f5: stloc.s V_6
L_00f7: ldloc.s V_6
L_00f9: ldloc.0
L_00ff: ldloc.3
L_0105: call IntPtr.op_Inequality
L_010a: brfalse.s L_0114
L_010c: ldloc.s V_6
L_010e: ldloc.3
L_0114: endfinally
L_0115: ldc.i4.1
L_0116: ret
L_0117: ldloc.s V_7
L_0119: ret
费了那么多劲原来它自己也是通过interop调用SHBrowseForFolder,那还不如直接调用这个API。
完整的FolderBrowser类
using System;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Collections;
namespace FolderBrowser
{
/// <summary>
/// Summary description for FolderBrowser.
/// </summary>
///
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
[ComVisible(true)]
public class BROWSEINFO {
public IntPtr hwndOwner;
public IntPtr pidlRoot;
public IntPtr pszDisplayName;
public string lpszTitle;
public int ulFlags;
public IntPtr lpfn;
public IntPtr lParam;
public int iImage;
}
[Flags, Serializable]
public enum BrowseFlags {
BIF_DEFAULT = 0x0000,
BIF_BROWSEFORCOMPUTER = 0x1000,
BIF_BROWSEFORPRINTER = 0x2000,
BIF_BROWSEINCLUDEFILES = 0x4000,
BIF_BROWSEINCLUDEURLS = 0x0080,
BIF_DONTGOBELOWDOMAIN = 0x0002,
BIF_EDITBOX = 0x0010,
BIF_NEWDIALOGSTYLE = 0x0040,
BIF_NONEWFOLDERBUTTON = 0x0200,
/// </summary>
BIF_RETURNFSANCESTORS = 0x0008,
BIF_RETURNONLYFSDIRS = 0x0001,
BIF_SHAREABLE = 0x8000,
BIF_STATUSTEXT = 0x0004,
BIF_UAHINT = 0x0100,
BIF_VALIDATE = 0x0020,
BIF_NOTRANSLATETARGETS = 0x0400,
}
public class API {
[DllImport("shell32.dll", PreserveSig=true, CharSet=CharSet.Auto)]
public static extern IntPtr SHBrowseForFolder(BROWSEINFO bi);
[DllImport("shell32.dll", PreserveSig=true, CharSet=CharSet.Auto)]
public static extern bool SHGetPathFromIDList(IntPtr pidl, IntPtr pszPath);
[DllImport("shell32.dll", PreserveSig=true, CharSet=CharSet.Auto)]
public static extern int SHGetSpecialFolderLocation(IntPtr hwnd, int csidl, ref IntPtr ppidl);
}
public class FolderBrowser
{
private string m_strDirectoryPath;
private string m_strTitle;
private string m_strDisplayName;
private BrowseFlags m_Flags;
public FolderBrowser()
{
//
// TODO: Add constructor logic here
//
m_Flags = BrowseFlags.BIF_DEFAULT;
m_strTitle = "";
}
public string DirectoryPath {
get{return this.m_strDirectoryPath;}
}
public string DisplayName {
get{return this.m_strDisplayName;}
}
public string Title {
set{this.m_strTitle = value;}
}
public BrowseFlags Flags {
set{this.m_Flags = value;}
}
public DialogResult ShowFolderBrowser() {
BROWSEINFO bi = new BROWSEINFO();
bi.pszDisplayName = IntPtr.Zero;
bi.lpfn = IntPtr.Zero;
bi.lParam = IntPtr.Zero;
bi.lpszTitle = "Select Folder";
IntPtr idListPtr = IntPtr.Zero;
IntPtr pszPath = IntPtr.Zero;
try {
if (this.m_strTitle.Length != 0) {
bi.lpszTitle = this.m_strTitle;
}
bi.ulFlags = (int)this.m_Flags;
bi.pszDisplayName = Marshal.AllocHGlobal(256);
idListPtr = API.SHBrowseForFolder(bi);
if (idListPtr == IntPtr.Zero) {
return DialogResult.Cancel;
}
pszPath = Marshal.AllocHGlobal(256);
bool bRet = API.SHGetPathFromIDList(idListPtr, pszPath);
m_strDirectoryPath = Marshal.PtrToStringAuto(pszPath);
this.m_strDisplayName = Marshal.PtrToStringAuto(bi.pszDisplayName);
}
catch (Exception ex) {
Trace.WriteLine(ex.Message);
return DialogResult.Abort;
}
finally {
if (idListPtr != IntPtr.Zero) {
Marshal.FreeHGlobal(idListPtr);
}
if (pszPath != IntPtr.Zero) {
Marshal.FreeHGlobal(pszPath);
}
if (bi != null) {
Marshal.FreeHGlobal(bi.pszDisplayName);
}
}
return DialogResult.OK;
}
}
}
如何调用这个类:
FolderBrowser aFolderBrowser = new FolderBrowser();
aFolderBrowser.Title = "Select a Folder";
aFolderBrowser.Flags = BrowseFlags.BIF_NEWDIALOGSTYLE|BrowseFlags.BIF_EDITBOX|BrowseFlags.BIF_STATUSTEXT;
DialogResult result = aFolderBrowser.ShowFolderBrowser();
if (result == DialogResult.OK ) {
textBox1.Text = aFolderBrowser.DirectoryPath;