.net环境中使用FolderBrowser

好久没有动手写东西了(其实以前什么也没写过;-)),今天要写的可能已经有哪位前辈完成了。拙作实在不敢拿出来现丑,高手看标题和上面的贴图就可以决定要不要看下去。不浪费大家时间。

客户的要求 :有一个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,反汇编以后差一点吐血。大家看看下面的就明白了。

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_0000: ldsfld IntPtr . Zero
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_0028: ldsfld IntPtr . Zero
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_0050: ldsfld IntPtr . Zero
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_0099: ldsfld IntPtr . Zero
L_009e: stfld BROWSEINFO . lpfn
L_00a3: ldloc.s V_4
L_00a5: ldsfld IntPtr . Zero
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_00c0: ldsfld IntPtr . Zero
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_00fa: callvirt IMalloc . Free
L_00ff: ldloc.3
L_0100: ldsfld IntPtr . Zero
L_0105: call IntPtr . op_Inequality
L_010a: brfalse.s L_0114
L_010c: ldloc.s V_6
L_010e: ldloc.3
L_010f: callvirt IMalloc . Free
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
{
///

1<summary>   
2/// Summary description for FolderBrowser.   
3/// </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,

/// 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;

Published At
Categories with Web编程
Tagged with
comments powered by Disqus