我在第二章中给出了文件夹的概览和它在Windows Shell中的地位,在这一章中咱们打算更详细地讨论它们。咱们主要集中精力阐述涉及文件夹全部层面的Shell函数,以及保证全部操做顺利进行的潜在机理。所以,咱们须要深刻研究两个起着很是重要做用的概念:快捷方式和PIDLs。前者是下一章的题目,在这一章中咱们将研究PIDLs,其中包括:程序员
SHBrowseForFolder()函数的用途算法
关于PIDLs进一步的讨论,以及怎样使用PIDLsshell
虚拟文件夹和位置编程
怎样得到文件夹的设置小程序
咱们将要论述的例子包含了一个加强版本的API函数SHBrowseForFolder(),一些使它更容易同PIDLs一道工做的辅助函数,以及一些怎样枚举某些指定位置(如,‘发送到’,‘Favorites’,以及‘个人资料’)内容的样例程序。api
让咱们先从各类文件夹选择方法开始咱们的讨论。对于让用户可以从特定驱动器上选择特殊目录的应用程序,这是一个普通的需求。Windows3.x API没有为这提供任何内建的工具,因此必须创建本身的辅助函数,然而,有一项通用技术可使用,它由修改通用对话框模版组成,例如删除象列表框那样的包含文件名的控件。数组
然而,推出这个方案到Win32有一个障碍:你必须抛弃新探测器风格的用户界面,而仍然忠实地使用老界面:安全
在Win32平台上,探测器风格的‘打开’对话框是一个单一实体,其中的任何控件都是不能摆脱掉的(好比:文件列表框)。服务器
要选择采用老的Windows3.x界面,另外一个选择是安排一个VC++显示方式的对话框到新项目中来请求一个特殊的文件夹。网络
跳出Win32关于GetOpenFileName()函数的资料,可以发现更多的东西。
从Wondwos95 开始,Win32 SDK 就包含了浏览文件夹的系统解决方案:这个函数称为SHBrowseForFolder()。其主要的特征是使用相似于咱们已知的和探测器钟情的树观察:
与前两章中咱们测试过的函数同样,SHBrowseForFolder()函数有一个简单的原型,可是,它实际上包含了一个带有大量设置和标志的结构,能够把这个函数看做是文件夹的中心函数,其目的之一就是使咱们可以在桌面命名空间中选择可用的文件夹。
如今看一下SHBrowseForFolder()的原型,它声明在shlobj.h中:
参数只有一个BROWSEINFO结构的指针,它的声明也在同一个文件中:
成员的说明以下表:
名称 |
描述 |
hwndOwner |
拥有这个对话框的窗口Handle |
pidlRoot |
被表述层次对象的根节点标识。是一个PIDL。 |
pszDisplayName |
必须是一个已分配缓冲区的指针,它将包含选择对象的显示名。 |
lpszTitle |
必须是一个缓冲区指针,包含一个做为树观察标题的串。 |
ulFlags |
指定外观和窗口行为(后面将介绍有效的值)。 |
Lpfn |
用于钩住对话框的回调函数。 |
lParam |
32位传递给回调函数的客户数据。一般是一个指针或Handle。 |
Iimage |
包含选中文件夹或文件的图标索引。是相对系统图像列表的索引。 |
调用SHBrowseForFolder()最简单的方法是:
这段代码显示一个前面看到过的对话框,而且恢复选中文件夹的PIDL。若是文件夹有一个对应的路径,你能够经过下述代码得到:
有几个有趣的结果与使用SHBrowseForFolder()函数有关。下面给出概述,在整个下一节咱们都将详细地讨论这些问题:
这个函数透明地处理PIDLs和路径名
这个函数容许浏览特殊的系统文件夹
这个函数返回大量的信息,是SHGetFileInfo()所不能的。
对话框稍微能够客户化,这老是一个好消息。
SHBrowseForFolder()函数所能作的事情由BROWSEINFO结构的ulFlags成员所限制,其合法的值由下述标志的组合构成:
标志 |
描述 |
BIF_RETURNONLYFSDIRS |
若是设置,仅在用户选择了文件系统的目录后,OK按钮才被容许。例如,你选择‘网上邻居’节点,若是这个标志设置,OK按钮是灰的。 |
BIF_DONTGOBELOWDOMAIN |
不显示网络文件夹,仅有域名节点。 |
BIF_STATUSTEXT |
对话框模版含有能够显示任何文字的标签,特别是在子类化这个对话框窗口后(后面将详细讲解)。 |
BIF_EDITBOX |
这是Shell 4.71版之后的新特征,它容许一个编辑框,在这里能够手动输入文件夹。 |
BIF_VALIDATE |
这是Shell 4.71版的另外一个新特征,它是对BIF_EDITBOX标志功能的补充。若是你设置了这个标志,而且子类化了这个对话框,则用户每次在编辑框键入和确认一个不正确的文件或文件夹名时,你都能收到通知(后面将详细讲解)。 |
BIF_BROWSEFORCOMPUTER |
容许用户仅选择计算机名。浏览正常发生,可是OK按钮老是灰的,除非选择了计算机名。 |
BIF_BROWSEFORPRINTER |
与上相同,可是是打印机。 |
BIF_BROWSEINCLUDEFILES |
若是这个标志设置,无论其它标志如何,在树观察中都显示文件名,而不只仅是文件夹名。这就提供了设置对话框显示系统中全部打印机或可用字体的机会。 |
在调用SHBrowseForFolder()函数时,有两种方法来客户化最终对话框的外观,经由回调函数子类化这个窗口更有力一些,咱们将在这一章的之后部分讨论这个内容。得到有限程度客户化的较简单方法是修改树观察上面的文字。BROWSEINFO结构的lpszTitle成员负责这一点。它声明为一个指针,因此,你必须传递一个有效的内存缓冲区:
TCHAR szBuf[MAX_PATH] = {0};
lstrcpy(szBuf, __TEXT("Choose a folder:"));
bi.lpszTitle = static_cast<LPCSTR>(szBuf);
对pszDisplayName成员,也同样,它是一个返回缓冲区。若是你对选中文件夹的显示名感兴趣,就须要传递一个有效的缓冲区,首先声明或分配它,而后 把指针赋值到pszDisplayName。
TCHAR szDisp[MAX_PATH] = {0};
bi.pszDisplayName = static_cast<LPSTR>(szDisp);
函数认为pszDisplayName至少有MAX_PATH字节尺寸。
正如前几章说明的,文件夹的显示名是探测器用来显示文件夹的名字。例如,(C:)的显示名是C:/。
技术上讲,函数返回的是PIDL,它标识一个选中的文件或文件夹。若是‘取消’了对话框,函数返回NULL,很是简单。然而,这个函数还可以经过传递的BROWSEINFO结构返回其它有用的信息。这一点的特殊例子是包含选中对象的显示名(上面已经提到了),和表明它的图标。
即便SHBrowseForFolder()看起来彷佛正在重复咱们已经从SHGetFileInfo()函数得到的功能。然而,就得到和显示图标仍然有至关的工做须要作。
在函数返回时,BROWSEINFO结构的iImage成员含有一个数字,它是图标在系统图像列表中的位置索引。于是,若是想要绘制图标—或更简单,想要它的HICON Handle—你就必须首先得到这个图像列表的Handle。
在前一章中已经讲到了怎样取得图标,可是,采用这里的方法要容易一些。若是使用SHGFI_ICON和标志调用SHGetFileInfo(),而且设置了SHGFI_SYSICONINDEX,函数则返回系统图像列表的Handle。
HICON SHGetSystemIcon(int iIconIndex)
{
SHFILEINFO sfi;
ZeroMemory(&sfi, sizeof(SHFILEINFO));
// 不指定文件名,由于咱们只想要一个Handle...
HIMAGELIST himl = reinterpret_cast<HIMAGELIST>(SHGetFileInfo(
"*.*", 0, &sfi, sizeof(SHFILEINFO), SHGFI_ICON | SHGFI_SYSICONINDEX));
HICON hIcon = ImageList_ExtractIcon(0, himl, iIconIndex);
return hIcon;
}
上面的代码是一个辅助例程,给定一个索引,返回系统图像列表中对应的图标。要运行这段代码须要包含shellapi.h,和经过调用InitCommonControls()或InitCommonControlsEx()初始化公共控件库。就象附录A中讨论的那样,第一个方法适用于老版本的Shell,第二才被推荐到Shell 4.71及其之后的版本。
有趣的是SHBrowseForFolder()函数要求一个回调函数。要子类化由这个函数创建的对话框,你须要指派一个有效的函数指针到BROWSEINFO的lpfn字段。这个指针必须指向有以下原型的函数:
int CALLBACK BrowseCallbackProc(HWND hwnd,
UINT uMsg,
LPARAM lParam,
LPARAM dwData);
其中hwnd是被钩住窗口的Handle,uMsg接收到的消息。lParam是一个值,根据uMsg它有不一样的意义,而最后这个dwData是用户定义的数据—与你经过BROWSEINFO的lParam成员指定的数据相同。若是你须要回调函数在调用程序创建的数据上工做,而不是使用全程变量,应该使用一个32位值来填写BROWSEINFO结构的lParam成员,而且保证它自动地经由dwData变量传递给回调函数。为了适合多个数据的传递,可使用指针,更好地,分配一个Handle内存块,锁定它,封包全部东西,解锁,而后把它存入lParam字段。下图显示了回调函数设置的情形:SHBrowseForFolder()调用了你所定义的函数,传送一些数据和通知某些事件。
由SHBrowseForFolder()创建的对话框能够通知回调函数下列事件:
对话框初始化完成
选择已经改变
用户在编辑框中键入了无效文件或文件夹。
经过发送下面消息完成这些功能:
BFFM_INITIALIZED
BFFM_SELCHANGED
BFFM_VALIDATEFAILED
这些消息由回调函数经过uMsg参数接收。每个消息都在lParam变量带有一个LPARAM类型的值。如今就让咱们逐个消息地观看一下lParam是怎样配置的:
消息 |
lParam 意义 |
BFFM_INITIALIZED |
无用消息—它老是NULL,这个消息在对话框窗口过程完成WM_INITDIALOG后发送。 |
BFFM_SELCHANGED |
指向新选中文件夹的标识符列表,注意,就象其余Windows控件同样,在选择已经改变时通知改变事件。 |
BFFM_VALIDATEFAILED |
指向编辑框的当前内容。就象它标志的那样,这个消息仅仅在Shell 4.71中支持。由返回0,回调函数能够强迫浏览对话框关闭,返回1,对话框保持活动。 |
有几个回调函数能够发送给对话框窗口的消息,使它能执行必定的活动。它们是:
消息 |
描述 |
BFFM_ENABLEOK |
根据lParam值,容许或禁止OK按钮。若是非零,按钮容许,此时能够确认当前选中的文件夹。wParam 没有用。 |
BFFM_SETSELECTION |
选择特殊文件或文件夹。lParam中存储一个指向PIDL或路径名的指针,wParam表示怎样解释这个指针。FALSE说明是一个PIDL,TRUE说明路径名。 |
BFFM_SETSTATUSTEXT |
设置你提供的文字到对话框的状态区域。实际文字由lParam指向,WParam无用。 |
这些消息正常地使用SendMessage()函数发送,经过组合它们,你能够实际地加强SHBrowseForFolder()的行为。
经过使用回调函数,你能够介入和改变对话框的用户界面,于是,使它能够更好地适应你的需求。例如,你不但愿?(帮助)按钮在标题条上出现,或使某些控件具备更显著的3D外观等。下面内容将说明这些功能是怎样实现的
从标题条中删除关联帮助按钮是一个关于窗口显示风格的简单问题:你只须要关闭处使Windows绘制和处理它的指示位便可。在扩展风格的任何窗口设置了WS_EX_CONTEXTHELP位时,这个按钮就显示。
扩展风格首先在Windows3.x中提出,而后在Windows95的SDK版本中获得增强。在正常风格和扩展风格之间的惟一不一样在于它们占用的存储区域,而不是概念上的差异。
你须要使用不一样于访问窗口风格的代码来访问‘扩展’风格,为了关闭使帮助按钮显示的位,在回调函数响应BFFM_INITIALIZED消息时中所必须这样作:
DWORD dwStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
SetWindowLong(hwnd, GWL_EXSTYLE, dwStyle & ~WS_EX_CONTEXTHELP);
首先,取得当前扩展风格(用GWL_EXSTYLE 代替 GWL_STYLE),而后关闭指定位,最后回存这个风格值。
作这个比前一个稍微复杂一点,而且要求多行代码。然而你应该清楚,如今所作的并不能保证你的代码在全部存在的和将来的Windows版本中都能正常工做。它仅仅能工做在你已经成功地测试了这个功能的地方。
如今,咱们想要绘制一个带有3D边框的状态标签,就象状态条那样。BIF_STATUSTEXT标志可能有点误导—它不是象所指望的那样在窗口的底部加一个状态条,而是在树观察上方和标题之间加一个静态标签。这个标签窗口有一个控件ID,咱们能够经过Spy++得知:
当你知道了这个控件的ID以后,一旦回调函数进入到对话框代码之中,得到任何子窗口的Handle就象调用下述代码那样容易:
HWND hwndChild = GetDlgItem(hDlg, controlID);
在上图中你能够看到,咱们感兴趣的标签有ID号0x3743,因此:
HWND hwndLabel = GetDlgItem(hwnd, 0x3743);
dwStyle = GetWindowLong(hwndLabel, GWL_EXSTYLE);
SetWindowLong(hwndLabel, GWL_EXSTYLE, dwStyle | WS_EX_STATICEDGE);
SetWindowPos(hwndLabel, NULL, 0, 0, 0, 0,
SWP_NOSIZE | SWP_NOMOVE | SWP_DRAWFRAME);
上述代码加了一个细边框到窗口中,外观以下图所示:
注意,为了看到这个变化,你须要强迫窗口重画它的非客户区域,这就是SetWindowPos()函数出现的缘由。再有就是,全部这些操做均在回调函数响应BFFM_INITIALIZED消息时完成。
前面我已经警告过,这段代码潜在的风险,如今,在全部Win32 平台上都能很好地工做,可是有一天微软决定改变这个ID时,将会怎样?一个好的方法多是:
咱们对GetDlgItem()函数返回的Handle执行了两个检查,头一个是使用IsWindow()来检查是否为有效的窗口,第二个是验证这个窗口是否是一个标签—一个‘static’类窗口。若是其中有一个检查失败,则退出,以免非法操做。
比加3D边框更有用的是改变对话框窗口的标题—使用新字符串调用SetWindowText(),即,在响应BFFM_INITIALIZED消息时,执行下面代码:
SetWindowText(hwnd, szNewCaption);
另外一个在响应BFFM_INITIALIZED消息的初始化中能够作的是定位窗口到指定位置。典型地,能够移动对话框到屏幕中心:
状态标签的典型用途是显示当前选中的文件或文件夹名,就象在前面图中显示的那样。其实现方法就是响应BFFM_SELCHANGED消息:
在接收到这个消息的时候,lParam变量指向一个新选中文件夹或文件的PIDL,说明文件或文件夹是存在的,所以能够调用SHGetPathFromIDList()函数得到可显示的路径名,注意,不是全部文件夹映射到物理目录—例如‘个人计算机’,是一个没有实际目录的文件夹。若是使用‘个人计算机’的PIDL调用SHGetPathFromIDList()函数,可能会得到一个NULL字符串。
从SHGetPathFromIDList()中恢复的字符串可使用BFFM_SETSTATUSTEXT消息发送到状态窗口。
自绑定到IE4.0的Shell 4.71版开始,这个对话框的用户界面就加入了一个编辑框,并且不须要借助于回调方式就能够操做。你只要在调用SHBrowseForFolder()函数时简单地设置BIF_EDITBOX标志便可。结果显示以下:
这个编辑框使你可以用输入文件夹的名字来选择它们。当你点击‘OK’时,函数将确认你的输入有效。若是编辑框中包含了一个文件夹的全路径名,或当前选择的文件夹名,则它的内容是正确的,如上图所示。若是BIF_VALIDATE标志设置,而且SHBrowseForFolder()函数发现编辑框的内容是不正确的,则它用BFFM_VALIDATEFAILED消息唤醒回调函数,在编辑框中的字符串经由lParam变量传递给回调函数。经由BROWSEINFO结构的lParam成员传递的任何用户数据,做为回调函时的第四个参数传递。所以,若是须要经过输入选择文件夹,绝对必须输入全路径名。
下面列出的代码例子说明咱们目前所看到的所有状况,后面章节中将给出整个应用的例程。
SHBrowseForFolder()函数设计时的一个缺陷是没有一个方便的方法来指定开始浏览的初始目录。你能够指定显示层的根,就是这样,若是想要使用目录而不是文件夹,也不简单。为了在代码中设置初始选中的文件夹,咱们必须借助于回调函数,特别是,必须探索BFFM_SETSELECTION消息,和请求函数移动焦点到一个特殊的文件夹。实现的最好方式是在响应BFFM_INITIALIZED通知消息的地方,下面就是实现的代码:
BFFM_SETSELECTION消息须要知道lParam变量是一个PIDL仍是一个路径名,在上面代码中由于设置(第三个参数)为TRUE,因此dwData指向一个路径名,若dwData指向PIDL,则第三个参数应该设置为FALSE。
指定根节点
前面已经暗示,SHBrowseForFolder()函数容许指定桌面层上的哪个节点做为根节点。换句话说,你能够选择想要浏览的探测器观察子树。能这样作的参数就是BROWSEINFO结构的pidlRoot成员。若是这个成员设置为NULL,树观察以桌面做为根。下面图中显示浏览对话框以‘打印机’做为根节点,而且设置了BIF_BROWSEINCLUDEFILES标志:
附带地,这个例子还说明,BIF_BROWSEINCLUDEFILES的做用。代码以下:
若是核对一下BROWSEINFO结构的声明,你将看到,pidlRoot成员应该有LPCITEMIDLIST类型—即,PIDL。在上面代码段中,经过在函数SHGetSpecialFolderLocation()第二个参数中传递CSIDL_PRINTERS,咱们已经取得了这个值,这一点将在后面继续讨论。如今,归纳地讲,你能够指定显示树的根节点,可是须要提供一个PIDL。
若是咱们的目标正好就是浏览某个系统文件夹,如‘打印机’或‘字体’或‘Favorites’,没有问题—使用上面的代码段,只需把CSIDL_PRINTERS换成想要的文件夹ID便可。若是想要把普通的目录做为树观察的根,事情就有点严重了。
对于特殊文件夹ID,翻看SHGetSpecialFolderLocation()函数的资料或研究shlobj.h源码就能够找到某些资料没有说明的IDs。
没有别的办法,你只有转换路径名到PIDL。如今,你可能但愿有一个Shell的API函数作这个转换,不幸的是没有这样的函数。然而,有一种方法可以转换路径名到PIDL,这须要两个步骤:
得到指向IShellFolder接口的指针
调用它的ParseDisplayName()的方法
ParseDisplayName()方法确实能实现你所要求的转换:它接受路径名,而后把它转换成PIDL,问题是咱们怎样才能得到指向IShellFolder接口的指针。在你写一个命名空间扩展的时候,IShellFolder接口是一个须要实现的接口,而且,探测器也使用这个接口一块儿工做,请求绘制和枚举其内容。一个指向IShellFolder接口的指针由SHGetDesktopFolder()函数返回—精确地讲,它返回一个桌面文件夹的IShellFolder接口。就咱们考虑的状况而言,咱们只是须要一个提供实际实现ParseDisplayName()功能的对象指针,于是SHGetDesktopFolder()函数返回的是可用的。下面是一个新Shell函数的代码,它接受路径名和返回对应的PIDL,以微软命名习惯,咱们称之为SHPathToPidl():
ParseDisplayName()函数的原形以下:
第一个变量是用做函数可能须要显示消息框的父窗口的Handle。第二个,pbcReserved没有使用,必须设置为NULL。头一个有意义的变量是lpszDisplayName,表示一个要求转换的Unicode格式的名字串,pchEaten是一个包含实际传递字符数的缓冲,而pdwAttributes(若是不空NULL),则包含由lpszDisplayName所指定文件夹的属性,这些属性都是SHGAO_前缀常量属性。若是不注意,可能会传递NULL属性。最后是ppidl变量,新生成的PIDL的返回缓冲。一旦对指定的路径成功地生成了PIDL,你就可以限制用户的浏览到指定的子树,而不能继续向上,例如:
这个图显示C:/Program Files做为根文件夹的情形。
到目前为止,咱们孤立地讨论了SHBrowseForFolder()函数,而且老是给出解决特殊问题的代码段,如今,咱们构造一个完整的应用来总结上面全部咱们看到的特性。
图中的对话框是测试SHBrowseForFolder()函数特征所使用过的,称之为SHBrowse。经过选择路径名(文件夹编辑框)或PIDL(PIDL列表框)指定根文件夹—使用PIDL复选框肯定是哪种路径。能够在标题编辑框中设置对话框标题,以及几个与SHBrowseForFolder()函数标志大体匹配的复选框,结果显示在下面的区域,显示名,路径名和文件夹图标。增长的第一段代码在OnInitDialog()函数中,以便使用特殊文件夹的名字设置PIDL下拉列表框。
此时你能够从这个列表中选择‘发送到’(SendTo)文件夹,这依赖于你的其它选择设置。如图:
固然,这个对话框在你本身的计算机上可能稍有不一样,这是由于‘SendTo’目录可能有不一样的快捷方式。在此选择‘OutLook Express’,结果以下:
整个项目是可用的,代码包含了BrowseCallbackProc(),SHGetSystemIcon()和SHPathToPidl()函数。编译以前必定要记住#include shlobj.h 和resource.h。这些函数在点击OK按钮时被执行。
上面函数的工做方式对你来说是很显然的—除了,最后面的一段。为了进一步解释它,咱们须要更深地了解关于PIDL的知识。
在第二章中咱们检测了PIDLs,在这里,咱们获得了它的特殊应用:使用PIDL浏览文件夹的内容,不管这个内容是什么。每个Windows Shell 的元素都有它本身的PIDL而且包含在某种文件夹中,于是,对于每个元素,都有一段代码来处理文件夹和依据文件夹自有的规则和需求提供PIDL。也就是说,咱们从不能设定PIDL结构或它所组合成的数据,而是必须使用通用接口来处理它。例如,若是但愿探索SHITEMID结构链,你就应该在每一步检查下一个块的长度。就像你已经看到过的那样,一个ITEMIDLIST—或PIDL—是由一个或多个连续分配的SHITEMID组成的,这个链在cb字段为0的元素上终止。下面是从MSDN上摘录的函数,它能够说明怎样遍历这个列表的项。这与普通的列表操做没有太大的差异。
你不能够设定PIDL的格式,对于一个文件夹,一种方法可能工做的很好,对于另外一个可能会失败。例如,为了保证两个项是相同的,你必须经过IShellFolder::CompareIDs()方法请求文件夹本身来比较它们。
在进一步讨论以前,有必要解释一下上面例子中的最后一段代码。在文件夹创建PIDLs时,一般必须由其它模块销毁,这就是咱们在OnOK()函数最后所作的。标识符列表的内存从Shell应用的分配器上取得,向第二章中所见,咱们能够调用SHGetMalloc()函数得到指向分配器的指针。通常,调用顺序以下:
回到咱们的题目当中,使PIDLs有某些实际的用途,这里有两个主要的目标,头一个,咱们想要可以枚举任何文件夹的内容,第二个是但愿重复探测器在Shell在4.71和更高版本中所支持的特征。为了展现这个想法,下面是探测器在地址栏输入‘打印机’后,键入‘回车’截图:
探测器容许使用‘打印机’做为一般意义的文件夹名。换言之,它模糊了物理文件夹和虚拟文件夹的差别,精确地讲,‘打印机’是一个内容为有效打印机设备的虚拟文件夹的显示名。在这个例子中咱们创建一个示范程序称之为Pidl,其用户界面以下:
‘搜索路径’按钮接受编辑框的内容并努力查找具备这个名字的文件夹。编辑框中的串就是一个文件夹的显示名(记住,路径名也是显示名)。若是成功,应用将在列表观察中显示全部在这个文件夹中找到的文件对象。另外一方面,‘显示PIDL内容’按钮将在列表观察中枚举特殊文件夹中找到的全部文件对象,特殊文件夹在下拉列表中选择。
让咱们从点击‘搜索路径’按钮所执行的代码开始,固然这两个新按钮都须要APP_DlgProc()过程来处理,因此在这里添加代码以下:
这里所涉及到的第一个函数是DoSearchPath(),它从‘文件夹名’编辑框中读出你键入的名字,把它做为搜索路径名,若是它确实是一个路径名,全部操做都正常进行,可是若是它是一个文件夹的显示名,将会有什么事情发生呢?咱们但愿函数可以处理如C:/和(C:)等的串,这个实现将可以正确处理全部路径名和与桌面关联的子文件夹的显示名,或‘个人计算机’等虚拟文件夹。
注意,正常地,驱动器的显示名由括弧中驱动器字符加标号给出,例如:Ms-Dos_6(C:)。若是没有标号,则认为有一个前导空格在(C:)中。
SHEnumFolderContent()是用户定义的函数,他接受一个文件夹的PIDL和一个回调函数做为输入,而后枚举这个文件夹的全部项,并传递这些项到这个函数做进一步的处理。后面咱们将进一步讨论它。为了理解它在这里的用途,你只须要知道,若是没有指定回调函数,它返回所找到的项目数:
int iNumOfItems = SHEnumFolderContent(pFolder, NULL, 0, NULL);
不然,它返回实际处理的项目数。这两个值必然是不一样的,由于回调函数能够在自己选中的点上中止枚举。例如SearchText()函数,当它找到了它正在寻找的名字时,SHEnumFolderContent()中止了。
SHEnumFolderContent()函数开始搜索,检查咱们在编辑框中键入的名字是否对应桌面下一个文件夹的显示名。这就是上面代码终止后,rc 和 iNumOfItems不等的状况。若是它们相等,咱们就在‘个人计算机’节点上开始一个新的搜索:
在重复调用SHEnumFolderContent()函数工做于‘个人计算机’文件夹上以前,咱们须要为之得到IShellFolder接口指针,在这一点上咱们有的只是桌面的IShellFolder接口,然而,咱们能够经过接口的BindToObject()方法得到想要的接口。这个操做使你能绑定到子文件夹的IShellFolder接口,所以,你能够一样使用这个方法做用于PIDL。
若是既没在桌面上也没在‘个人计算机’上找到指定的显示名,则咱们所取得的是一个快捷方式,并且咱们确实不能肯定它的位置。尽管如此,也不要认为这是一个系统限制—在文件夹上使用递归搜索来肯定名字的位置是颇有可能的。这个方法略述以下:
枚举桌面文件夹的内容,就象上面所作的。
对每个找到的文件夹(不只是‘个人计算机’)重复这个搜索过程
然而,彻底递归的搜索可能致使试图经过名字查找一个不惟一的文件夹—这是颇有可能的。固然可能有两个文件夹具备相同的显示名MyDir,一个在c:/,另外一个在d:/,上面的算法将老是中止在头一个文件夹出现的地方。
一个较好的方法是接受和分析全质量文件夹名,好比:
My Computer/ (c:)/Windows
Control Panel/Add New Hardware
这样作仅仅须要不多的额外代码来分析文件夹名,而且从搜索桌面上的第一个项开始,前一步完成到达文件夹的下一个项,等等。上面所看到的代码能够稍微加推广,和封装在一个循环中。
回想一下,这确实与在文件系统中搜索没有什么不一样,正好就是使用FindFirstFile()和 FindNextFile()来枚举目录的内容,只是使用了由文件夹对象的COM接口暴露的方法而已。
在完成代码以前,注意,键入的显示名是一个完整的路径名的状况,如c:/。在输出一个消息框以前处理这种状况是有价值的—正象咱们须要转换这个名字为PIDL格式同样,看一下什么状况发生了,若是没有错误,则一个路径名被接受了。
最后,若是函数在这一点以前没有返回,咱们知道,已经有了一个可用于输出文件夹图标的PIDL,也就是说,在编辑框中有一个输入串做为上面源码中的szName被引用。咱们就用那个名字标识文件夹对象并得到它的PIDL。如今,要枚举这个文件夹的内容,咱们须要得到它的IShellFolder接口和把它传递给SHEnumFolderContent()函数。
于是,‘搜索路径’按钮处理程序的结尾代码有以下形式:
查看上面的代码,你可能已经注意到,我使用了SHPathToPidlEx()函数来转换路径名到PIDL。在这一章的开始,咱们开发了有一样目的的SHPathToPidl()辅助函数—它使用IShellFolder接口的ParseDisplayName()方法。SHPathToPidl()函数的代码浓缩到这一点上,它取得相对于桌面的PIDL—即,层次的根是
SHGetDesktopFolder(&pFolder);
pFolder->ParseDisplayName(NULL, NULL, wszPath, &n, ppidl, NULL);
不幸的是这个PIDL是相对于提供IShellFolder接口文件夹的,是桌面。新状况是咱们须要相对于操做文件夹之父文件夹的PIDL。理由是,当咱们使用BindToObject()方法来得到一个子文件夹的IShellFolder时,要求传递一个PIDL,它是与咱们调用BindToObject()位于相同位置文件夹的PIDL。
此后,咱们还须要在得到IShellFolder接口指针和ParseDisplayName()函数之间添加几步,这几步是要保证用于ParseDisplayName()调用的IShellFolder接口确实是咱们想要的文件夹的接口。
代码以下:
这个函数比SHPathToPidl()更通常,并且它也要求传递PIDL相对的文件夹。若是传递一个NULL,而不是一个IShellFolder指针,则桌面的IShellFolder接口被使用,而后被释放。在这个例子中调用转换函数的代码是:
HRESULT hr = SHPathToPidlEx(szName, &pidl, pFolder);
试着传递一个NULL而不是肯定的路径名给pFolder,进行搜索,将会看到:不管如何,你将老是枚举‘桌面’文件夹的内容。
做为SHEnumFolderContent()细节的一部分,在这个代码中咱们所看到的最简单的辅助函数是ClearUI():
这正是重置应用的对话框,从观察列表中删除全部项,以及清空由SHEnumFolderContent()函数创建的图像列表,最后一项任务由HIMAGELIST类型的全称变量g_himl完成,它在WinMain()中被初始化为0。
关于枚举给定文件夹内容这方面仍然有大量的问题须要考虑,好比,它是一个物理目录仍是一个如‘打印机’的虚拟文件夹等。咱们所看到的源码给出一个重要的函数SHEnumFolderContent(),这是一个负责查询文件夹和逐个枚举其内容的函数。
有些文件夹其内容是文件的集合,还有些文件夹其可见内容能够是单个文件的记录或某种硬件设备。通常而言,仅仅是文件夹才确切地知道它的内容是什么。对探测器或程序而言,没有任何保险的方法不须要查询文件夹就能枚举它所包含的项。这并不奇怪,由于这种通信是基于COM接口的。
在程序中,SHEnumFolderContent()询问一个文件夹的内容,并传输它找到的每个项的名字到另外的函数作进一步的处理。你已经看到了这两个函数:SearchText()和ShowFolderContent()。然而要理解它们的适看成用,首先要研究项目枚举是怎样发生的。
关联于‘搜索路径’(和显示PIDLs内容)按钮的代码,其目的在于读出文件夹的内容。为了容许枚举其中的项,文件夹实现了IEnumIDList接口,这个接口暴露了四个函数:Next(), Skip(), Reset()和 Clone(),使之能够在给定的集合中先后移动。它们的原型为:
头一个变量是请求的项目数,第二个是一个PIDLs数组指针,第三个则是返回的实际拷贝的项目数。IEnumIDList接口自己负责分配保存PIDL数据的内存。
要想了解指定文件夹的内容,须要设计一段代码,要记住是经过获取指向IEnumIDList接口的指针和IShellFolder接口所暴露的方法EnumObjects()实际完成这个操做的。这个方法的原型以下:
这个方法的第二个参数容许你规定枚举项目的类型。它取以下定义的枚举类型组合值:
就如助记名所述的同样:你能够决定枚举文件夹、非文件夹对象,甚至是隐藏对象。
之后在写一个命名空间扩展得时候,详细讨论这些接口是很是必要的。如今,咱们建议你花点时间看一下VC++的帮助文件,以澄清这些方法名和原型。
上面这段代码描述了触发枚举文件夹项函数的过程。每次循环条件成立,pItem指向一个PIDL的单项。得到这个数据后有两件事情须要处理:它的显示名和可能的图标。
即便有了PIDL,取得项目的显示名也不是一件容易的事。尽管有IShellFolder::GetDisplayNameOf()函数,仍有一些工做要作,问题在于这个方法不提供正常的ANSI或Unicode格式。相反,它返回的是一个STRRET结构的指针。STRRET结构定义以下:
资料中对这些标志的描述是足够清晰的。所以,你能够构建你所指望的具备最终行为的函数。然而,不管我怎样设置标志,全部样例程序都是以一样的方式工做。坦白地说,我也不知道哪儿除了问题。因此我建议老是使用0做为这个参数的值。
这个代码段以可阅读格式显示项目名。再次提醒注意,对于文件型文件夹以及Fonts, Favorites, Printers, Control Panel等特殊文件夹这是正确的。也就是说,咱们可以列出全部‘控制面板’中的小程序。
自己,咱们没有用到STRRET结构,这要感谢StrretToString()函数提供的帮助,它固然也包含在Shell库函数中。
已开始接触Shell编程,觉得很是艰巨,然而,当你从初始的头三四个月中挺过来以后,你就开始有机会接触高级项目问题的答案了。要说明这一点,你认为怎样才能取得一个项的图标?回答是,你必须询问提供它的文件夹。IShellFolder::GetUIObjectOf()方法返回全部你可能须要处理用户界面和文件对象的接口。
对这个声明咱们感兴趣的是能够请求必定数量的不一样接口指针,它们都能影响UI显示。例如,请求IContextMenu,以得到元素的关联菜单HMENU的Handle。在咱们的任务中,请求的是IExtractIcon接口,以便查找图标(在第十六章中将看到更多关于GetUIObjectOf()的解释)。
pFolder->GetUIObjectOf(NULL, 1, const_cast<LPCITEMIDLIST*>(&pItem),
IID_IExtractIcon, NULL, reinterpret_cast<LPVOID*>(&pExtractIcon));
IExtractIcon接口有两个新方法:GetIconLocation()和Extract()。头一个使你能知道图标的索引和位置,第二个则返回一个HICON 型的Handle。在客户端调用GetIconLocation()函数时,它返回包含图标的文件名,以及在这个文件的资源中的一个从零开始的图标索引。
Extract()函数依次从指定文件中抽取给定的图标,和返回它的HICON。这个方法几乎与API函数ExtractIconEx()是同样的。
对这些函数而言资料显得有点冗长,例如,即便你不关心pwFlags的内容,你也须要知道它不能为NULL。相似地,即便你仅仅须要大图标,你仍然必须为小图标传递一个有效的非零HICON。下面测试一个例子,说明怎样调用它:
在开发这个实例代码时,我得到了另外一个有趣的结果,坦白地讲,这超出了个人想象。在有些状况下Extract()函数返回的Handle是NULL,即便在图标的位置和索引都是正确的状况下,也是如此。奇怪,使用相同的参数调用ExtractIconEx()函数工做无缺。固然,工做环境是直接的:
if(hIcon == NULL)
ExtractIconEx(szIconFile, iIconIndex, &hIcon, NULL, 1);
在这一点上,最后要作的工做就是须要创建一个新的Shell函数,它接收IShellFolder指针,在它的项上循环,为每一个项唤醒回调函数。就像不少其余称为‘枚举’的函数同样,咱们的SHEnumFolderContent()函数将提供一个用户定义的缓冲(dwData)发送程序级的变量到回调函数。进一步,若是回调函数返回FALSE,则函数将中止工做。这里是它的原型:
其中FOLDERCONTENTPROC是一个用户定义的函数指针,其声明是:
typedef BOOL (CALLBACK *FOLDERCONTENTPROC)(LPCSTR, HICON, DWORD);
第一个变量是元素显示名,然后是一个图标Handle。而后是用户定义的缓冲。像已经提到的那样,函数返回FALSE来中断枚举,TRUE则继续。
SHEnumFolderContent()的最后一个参数是PIDL。这并不严格地必要,只是有时须要(如在咱们的例子中),理解这个最后传递的参数PIDL是有帮助的。若是这个变量是NULL,则它被忽略。下面是SHEnumFolderContent()函数的源代码:
典型地,回调函数用于实现某些项的采集任务。如此,SHEnumFolderContent()调用这样的函数采集各个文件夹项。SearchText()函数仅仅报告是否传递过来的两个串是相等的。
ShowFolderContent()函数用于构建一个传递过来图标的图像列表,插入图标到提供的列表观察中:
必定要保证,主程序代码中包含#includes shlobj.h 和resource.h,下面图中显示使用这一阶段开发的实例程序能够作的操做。键入‘打印机’,你能充填这个列表观察,就像标准的文件夹窗口同样:
再有,指定一个路径名,能够看到文件和文件夹,正象在探测器中所看到的那样:
回想一下,若是想要获得任何驱动器根目录的内容时,你必须包括一个最后的反斜杠‘/’,例如C:/工做正常,而C: 则产生这个结果:
适当地使用咱们全部的辅助函数,写一个‘显示PIDLs内容’按钮的处理器不是太困难的事。与这个按钮相关的combo框由名字和一些特殊文件夹的ID初始化,这个过程执行与SHBrowse例子中相同的代码。此处由函数在点击‘显示PIDLs内容’按钮时执行,并充填列表观察:
这个函数从得到特殊文件夹的ID开始,这个ID来自combo框的选择,然后调用SHGetSpecialFolderLocation()函数得到文件夹的PIDL,再从这个PIDL得到IShellFolder接口,而后传递给SHEnumFolderContent()函数。图中显示了这个应用怎样枚举‘控制面板’的小程序:
咱们首先在第二章中看到了特殊文件夹和它们的基本概念,其中有三种基本类型。几乎全部的特殊文件夹都有对应的目录,可是这些与普通的文件型文件夹和客户文件夹是彻底不一样的。第三种类型是由没有目录的文件夹(虚拟文件夹)组成。
虚拟文件夹感受上象一个文件夹,可是它们的位置和内容没有文件和目录概念的映射。‘控制面板’,‘打印机’,‘网上邻居’,‘个人计算机’都是虚拟文件夹的例子。例如‘控制面板’能够包含全部安装了的小程序。
除了外观,没有称为‘控制面板’的物理目录能够包含与之相关的任何东西,好比‘添加新硬件’或‘Modems’。这个文件夹所列出的全部图标都来自系统目录下的.cpl文件。他们由命名空间扩展汇集和表示为虚拟文件夹。
系统对特殊文件夹的支持
Windows API定义了必定数量的特殊文件夹,而且有一堆函数做用其上。这些例程经过如ID同样的数字标识每个特殊文件夹,可是对PIDLs或CLSIDs则没有什么可作的。
ID定义在shlobj.h中,而且都有至关奇怪的符号名:都以CSIDL_开始。下表列出了一些可用的特殊文件夹:
文件夹ID |
虚拟的 |
描述 |
CSIDL_DESKTOP |
Yes |
桌面 |
CSIDL_DRIVES |
Yes |
个人计算机 |
CSIDL_BITBUCKET |
Yes |
回收站 |
CSIDL_CONTROLS |
Yes |
控制面板 |
CSIDL_NETWORK |
Yes |
网上邻居 |
CSIDL_INTERNET |
Yes |
Shell上的IE探测器节点(版本4.71以上) |
CSIDL_PRINTERS |
Yes |
打印机 |
CSIDL_DESKTOPDIRECTORY |
|
含有所有桌面快捷方式的目录 |
CSIDL_FAVORITES |
|
Favorite文件夹的快捷方式 |
CSIDL_FONTS |
|
安装的字体 |
CSIDL_NETHOOD |
|
网络域的引用 |
CSIDL_PRINTHOOD |
|
打印机的引用 |
CSIDL_PERSONAL |
|
私有文件的快捷方式 |
CSIDL_PROGRAMS |
|
‘程序’菜单的快捷方式 |
CSIDL_RECENT |
|
最近使用文档的快捷方式 |
CSIDL_SENDTO |
|
‘发送到’菜单项的快捷方式 |
CSIDL_STARTMENU |
|
‘开始’菜单中用户定义的项 |
CSIDL_STARTUP |
|
启动时运行的程序的快捷方式 |
CSIDL_COOKIES |
|
Cookies |
CSIDL_TEMPLATES |
|
文档模版的快捷方式 |
CSIDL_HISTORY |
|
访问过的Web页面的快捷方式 |
CSIDL_INTERNET_CACHE |
|
IE的临时Internet文件 |
CSIDL_APPDATA |
|
一个应用专有数据的文件夹 |
CSIDL_ALTSTARTUP |
|
非本地‘启动’组 |
资料中还提到了另一些标号为CSIDL_COMMON_XXX的文件夹,它们是:
CSIDL_COMMON_STARTUP CSIDL_COMMON_STARTMENU
CSIDL_COMMON_PROGRAMS CSIDL_COMMON_FAVORITES
CSIDL_COMMON_DESKTOPDIRECTORY CSIDL_COMMON_ALTSTARTUP
除了它们指向一个任何用户均可看到的物理文件夹之外,这些文件夹与不包含COMMON的文件夹相同。虽然这一点在资料中没有显式提到,这些文件夹彷佛感受仅在WindowsNT下出现。
非虚拟文件夹在机器的某个地方有一个路径,你能够经过调用SHGetSpecialFolderPath() API函数得到一个特殊文件夹的路径。特殊文件夹与它的路径之间的链接存储在注册表中,注册键入下:
HKEY_CURRENT_USER
/Software
/Microsoft
/Windows
/CurrentVersion
/Explorer
/Shell Folders
在HKEY_LOCAL_MACHINE下相同的键存储了全部可用的COMMON文件夹,可是,在Windows95和Windoes98下并非全部COMMON文件夹都有路径。事实上,仅仅CSIDL_COMMON_DESKTOPDIRECTORY和CSIDL_COMMON_STARTUP有路径存储。假设C:/Windows是系统目录,所列出的路径在C:/Windows/All Users folder下。然而,在Windows95/Windows98下SHGetSpecialFolderPath()函数不能为其返回任何值。反之,在WindowsNT下使用一样的函数,则返回正确的路径。
咱们能够经过观察曾经使用过的函数SHGetSpecialFolderLocation()来走进函数SHGetSpecialFolderPath()函数。这个函数恢复指定的特殊文件夹的PIDL,它有下面的原型:
hwndOwner是任何弹出显示窗口的父窗口,nFolder是特殊文件夹的标识符,能够是上表列出的常量之一,而ppidl则是指向包含这个文件夹的PIDL缓冲的指针。SHGetSpecialFolderPath()函数,试图恢复给定文件夹的路径,与上函数很是类似:
lpszPath将包含路径名,而fCreate是一个逻辑值,表示若是文件夹不存在,是否须要创建。固然,在这种状况下,你不能指定一个虚拟文件夹的ID。注意,与SHGetSpecialFolderLocation()函数不一样,SHGetSpecialFolderPath()仅仅支持Shell 4.71以上版本。
IE4.0和活动桌面极大地增长了系统文件夹的设置量。‘文件夹选项’对话框有完善的复选框来肯定文件夹的外观,使其具备咱们但愿的形式:
这个对话框是Windows系统上人人都使用的对话框,它使你能设置查看系统文件和隐藏文件。有些设置(不是所有)能够经过编程读出,天然这些也仅能在Shell 4.71以上版本上有效。
在VC++的资料中能够找到每个设置的详细说明,在这里所能找到的仅仅是一个例子程序。
SHGetSettings()函数
实际上,使用SHGetSettings()函数是至关简单的,它仅须要两个变量:
void SHGetSettings(LPSHELLFLAGSTATE lpsfs, DWORD dwMask);
SHELLFLAGSTATE是一个很是简洁的结构:
dwMask参数是二进制屏蔽位—对于结构中每个感兴趣的字段和须要函数恢复的字段,都必须设置适当的屏蔽位。可能的值是:
字段 |
屏蔽位 |
在文件夹选择对话框中的设置 |
fShowAllObjects |
SSF_SHOWALLOBJECTS |
显示全部文件 |
fShowExtensions |
SSF_SHOWEXTENSIONS |
隐藏已知文件类型的文件扩展 |
fNoConfirmRecycle |
SSF_NOCONFIRMRECYCLE |
None |
fShowSysFiles |
SSF_SHOWSYSFILES |
不显示隐藏文件 |
fShowCompColor |
SSF_SHOWCOMPCOLOR |
None |
fDoubleClickInWebView |
SSF_DOUBLECLICKINWEBVIEW |
在‘通常|客户设置’对话框上‘双击打开项’选择 |
fWin95Classic |
SSF_WIN95CLASSIC |
在‘通常’页上‘典型风格’选择 |
fDontPrettyPath |
SSF_DONTPRETTYPATH |
所有容许大写名字 |
fMapNetDrvBtn |
SSF_MAPNETDRVBUTTON |
在工具条中显示‘网络驱动器映射’按钮 |
fShowAttribCol |
SSF_SHOWATTRIBCOL |
在‘细节观察’中显示文件属性 |
fShowInfoTip |
SSF_SHOWINFOTIP |
对文件夹和桌面项显示弹出的描述 |
fDesktopHTML |
SSF_DESKTOPHTML |
在活动桌面关联菜单上设置‘Web页面观察’ |
fHideIcons |
SSF_HIDEICONS |
当桌面做为Web页面时,隐藏图标 |
不少资料都说明fHideIcons是没用的,事实上它仅仅说明当桌面设置成Web模式时,是否桌面上的图标应该被显示。如今让咱们查看一些应用,以便探讨能够从这些标志中得到的信息。
头一个用途是程序员是否想要在用户的应用界面中显示文件扩展。若是应用程序须要在任何状况下显示文件名,你就应该依据这个标志的状态使用户可以选择肯定是否显示扩展名。
fHideIcons标志使你在观察模式设置为‘Web页面’时知道桌面上的图标是否能够看到。而fDesktopHTML标志从另外一方面告诉你是否桌面使用了一个HTML页做为它的背景。若是桌面是在Web模式中,而且图标不可见,则你就不能在桌面上创建新的快捷方式。
若是咱们仅仅能设置这些位而不能得到它们的状态的话,组合使用fDesktopHTML和fHideIcons是很是有用的。考虑下面的状况:有不少方法清除桌面以禁止公共计算机用户而不是你浏览和运行应用程序。而使用fDesktopHTML和fHideIcons的组合给出了一种新方法。首先,它容许你设置标志显示HTML页面做为桌面的背景,其次隐藏全部桌面上的图标。使用这种方法,你能够把Windows桌面(和机器)变成一个专门运行单一HTML应用的服务器。诚然,任务条还在,可是,你能够经过取得其HWND,而后使用SW_HIDE标志调用ShowWindow()函数来隐藏它:
在Shell 4.71以上版的众多文件夹的设置中,有可能设置文件夹的行为使其在被选中时变成下划线形式,和只要一次点击即可打开它们。你能够经过‘文件夹选项’对话框的‘通常’页设置这些选择。有趣的是,这些风格对于通用控件库版本4.70的列表观察也有效。因此你能够根据fDoubleClickInWebView标志来修改列表观察的活动形式和鼠标跟踪能力。如此,须要考虑下列关系:
这个列表中的版本号指的是通用控件库的版本号,不是Shell的版本号。版本4.70的comctl32.dll与IE4.0一同发布(不管活动桌面是否安装)。4.71与IE4.01。
设置扩展风格的列表观察,须要使用ListView_SetExtendedListViewStyle()函数,它是一个围绕LVM_SETEXTENDEDLISTVIEWSTYLE消息创建的宏。上面列表中的头两种风格的意义是直接的,其它的涉及到热点项(一个术语,用于描述鼠标正在通过的项)。LVS_EX_UNDERLINECOLD引发非热点项下划线,而LVS_EX_UNDERLINEHOT则仅仅热点项下划线。
fNoConfirmRecycle标志指示在删除文件以前是否显示确认对话框。能够想象,这仅适用于删除到‘回收站’和Shell操做的场合。然而,即便你没有使用Shell函数,如SHFileOperation(),来删除文件,若是用户但愿有这样的询问时,是否不能很好地给出确认提示呢?阅读一下fNoConfirmRecycle能够向这个方向前进一大步,进而使之成为可能。
示例程序
程序界面,做为这一章最后一个例子,以下图所示,可能你已经猜到了什么是咱们要用于创建的程序框架。
这个例子的代码是十分容易的:只须要为‘取得设置’按钮加一个处理器便可,它将引发当前Shell的选项设置被读出。下面的源码产生了在图中所看到的结果。总之,必定要记住在源文件顶部包含#include shlobj.h 和 resource.h。
阅读了这些参数的设置,对处理各类状况确实有很多帮助,可是更有兴趣的应该是编程地设置这些特征。不幸地是,到目前为止只有SHSetSettings()例程出现,如今,咱们要说明的是有大量的能够达到这个目标的方法可用而不须要微软的帮助。
参数存储在哪里
正如你已经大体猜到的那样,全部可使用SHGetSettings()读到的参数都存储在注册表的某个地方。也即,能够由一个相对安全的方法来编程地设置这些参数。
在向下进行以前,咱们要强调一个重点。在官方资料缺少的状况下,微软在将来的操做系统版本中是能够自由改变注册表键的用途的,这就间接地影响到你的代码。在编写代码时,咱们使用的技术在版本4.71下是好用的。
打开注册表,查看下面的键:
HKEY_CURRENT_USER
/Software
/Microsoft
/Windows
/CurrentVersion
/Explorer
/Advanced
彷佛咱们已经找到了要找的东西。修改这些注册表项是足够简单的,对吗?很不幸,不是这样的—你很快就能注意到,这个值列表缺乏了条目数。尤为是‘Web观察’设置。
回想一下测试反向工程注册表设置的黄金定律:老是比较HKEY_CURRENT_USER和 HKEY_LOCAL_MACHINE下相同键的内容。这里就是咱们要找的:
正如所见,有一个完整的层次结构复制了与‘文件夹选项’对话框相同的树结构。主节点是一个“group”类型的节点,有本身的Bitmap,和显示名。结构的叶含有一个属性集,其中突出的有两个值:RegPath和 HKeyRoot:
这就是说,在子树中每个条目都指向注册表中的另外一个键,实际值就存储在那儿,其路径是HKeyRoot/RegPath/ValueName。叶特征肯定了显示文字,选项类型(复选框或收音按钮),选择值(选中或没选中),默认值,甚至文件名和有帮助的主题ID等。
给出了这些以后,处理客户的SHSetSettings()函数就只是简单地读写注册表数据操做了。
由于在‘文件夹选项’对话框的层次结构和注册表子树布局之间有良好的对应,咱们能当即猜想到添加一个新键到注册表应该在标准对话框中产生一个新的客户选项。为了证实这一点,仅须要作一件事情:加一个新键到注册表子树。
我在Folder下定义了一个新键,称为MySetting。而后定义全部在其它叶上看到的值:
保存这个改变到注册表以后,期待地打开‘文件夹选项’对话框,没有任何新东西出现。事实上,对此有一个显而易见的缘由:对话框仅在可以读出所存储的值时才添加新项。正如早先提到过的那样,这个值存储在注册表的另外一个位置—是由HKeyRoot,RegPath 和ValueName指向的。剩下的就是在下面的键上创建一个称为MySetting的新值:
HKEY_CURRENT_USER
/Software
/Microsoft
/Windows
/CurrentVersion
/Explorer
/Advanced
还应该设置所指望选项的默认值。在保存改变和响应‘文件夹选项’对话框后,新的设置就出现了,以下图所示:
在‘文件夹选项’对话框中添加新的客户选项不是为了向人炫耀—也不是一项容许用户客户化你的程序的便利方法。咱们并不建议你使用这个对话框来设置一个应用的全部设置。可是,考虑使用选项来解决用户界面和文件夹的问题是有价值的。能更好地探索这些特征的模块应该在命名空间扩展应用中。
使用注册表路径的选择彻底在于你本身,可是,应该认识到这一点,把你本身的设置存储到远离标准设置的地方,好的选择是使用应用特殊的注册键。
文件夹是一个普遍的题目,这一章一直在努力给出详细解释。你已经看到怎样浏览特殊文件安夹和怎样与它们一道工做。枚举它们的内容和设置它们的参数。特别,在这一章中还揭示了:
怎样更好地使用SHBrowseForFolder()
怎样枚举任何文件夹的内容
处理特殊系统文件夹的函数
那些文件夹设置是可读的,怎样编程设置它们
为此,咱们构建了潜在有用的函数来扩展API所提供的工具。例如SHEnumFolderContent()和SHPathToPidlEx()辅助例程。此外,咱们还揭示了Shell怎样存储文件夹的设置,和给出了添加新选项到标准的‘文件夹选项’对话框的方法。