现在的位置: 首页 > 综合 > 正文

Windows Shell 编程 第四章

2013年02月25日 ⁄ 综合 ⁄ 共 5956字 ⁄ 字号 评论关闭

第四章 文件的本质

         以前,所有文件和目录都有一个确定的属性集:时间,日期,尺寸,以及表示‘只读的’,‘隐藏的,‘存档的’,或‘系统的’状态标志。然而,Windos95(及后来的WindowsNT4.0)出现使这些概念产生了改变,其中最重要的‘文件’变得更加广泛。现在,文件可以是任何Shell部件对象—不一定必须是文件系统的部件。

         文件的精确定义是,任何作为Shell命名空间部件的对象称之为文件对象。注意,在定义中所说的‘命名空间’,它不是C++的关键字。‘Shell 命名空间’所指的是实际组成Shell的所有命名项的集合。它们都被显示在探测器的树观察中。

         并不是所有文件都是文件系统中的一个实体,比如‘打印机’和‘我的计算机’。一个包含子文件对象的文件对象成为文件夹对象,文件和目录是最普通的文件对象。

         所有这些变化作为微软完全面向对象的操作系统实现的第一步,已经融入到Windows9xWindowsNT中。

         一个文件对象可以有多少属性呢?回答是,它是一个集合,它完全包含MS-DOS下一个文件的所有属性以及几个由Windows95 Windows NT外壳的图形本质要求的属性。Shell API提供了一个复合函数和相当丰富的功能来探索给定文件对象的特征,它可以是一个普通文件,一个目录,甚或一个系统文件夹,一个象打印机或拨号连接那样的系统对象。这个函数就是SHGetFileInfo()

         在这一章中,主要目标是研究这个函数的原形。对于特定的文件对象,重点是:

                   怎样获取类型名

                   怎样获取探测器图标的Handle

                   怎样获取可执行文件的目标平台

                   怎样读出属性,以确定在探测器下对文件对象有哪些事情是可以做的,哪些事情是不能做的。

SHGetFileInfo()函数所获得信息的多样性,你会感到奇怪。曾记得,有一个读者询问我,怎样确定一个给定的.EXE文件是16位的还是32位的(不用映射EXE的头结构)。我的答案是SHGetFileInfo()。几天以后,他回来再次问我,怎样获取驱动器的图标,我再次告诉他,使用SHGetFileInfo()。这最终的结果告诉我们仔细研究SHGetFileInfo()函数是理解Shell文件对象的最好方法。

 

SHGetFileInfo()函数的功能

         同以往一样,我们从函数的原形开始,它在shellapi.h中定义。这个函数有五个变量,定义如下:

DWORD SHGetFileInfo(    LPCTSTR pszPath,

DWORD dwFileAttributes,

SHFILEINFO FAR* psfi,

UINT cbFileInfo,

UINT uFlags);

基本上讲,SHGetFileInfo()函数提供关于文件系统对象的信息。如前面解释的,这个对象可以是文件,文件夹,目录或驱动器根。DWORD的返回是指可能有相当多的返回状态,这与uFlags变量的设置有关。简单地说,使用这个函数,你可以期望:

    确定可执行文件的目标平台(Win32Win16MS-DOS)

    获取各种有特色的文件图标(小的,大的,有关联重叠的,选中的,打开的)

    读出其它显示属性,如文件类型(显示在探测器类型列上的简短描述)和显示名(出现在名字列上)

读出任何其它属性,可以是文件特有的,如,是否可以拷贝,移动,删除或重命名,是否它可以形成一个快捷方式,它是否有子文件夹,是否是共享的,是拖拽目标,或有附加的属性页,等等。

 

SHGetFileInfo()函数的工作原理

       为了正确地理解函数具有的功能,使用所有可能的方法强制调用这个函数是十分必要的。首先,让我们查看一下他所要求的变量:

变量名

描述

pszPath

一个包含要取得信息的文件相对或绝对路径的缓冲。它可以处理长或短文件名。

dwFileAttributes

资料上说,这个参数仅用于uFlags中包含SHGFI_USEFILEATTRIBUTES标志的情况。如此,它应该是文件属性的组合:存档,只读,目录,系统等。

Psfi

指向一个接收数据的SHFILEINFO结构的指针。

cbFileInfo

简单地给出上项结构的尺寸。

uFlags

函数的核心变量,通过所有可能的标志,你就能驾驭函数的行为和实际地得到信息。

 

SHFILEINFO结构定义如下:

typedef struct _SHFILEINFO

{

HICON hIcon;

int iIcon;

DWORD dwAttributes;

char szDisplayName[MAX_PATH];

char szTypeName[80];

} SHFILEINFO;

此外,这个结构总是用于返回数据到调用程序,并且从不需要初始化。唯一可以包含信息来影响函数行为的是dwAttributes成员,在后面将进一步给出解释。显然,驾驭SHGetFileInfo()函数各种行为的所有兴趣都集中在对uFlags变量值的设置上。绝大多数情况下,信息经由psfi缓冲返回,但也有些情况,回应可以有效地包含在函数的DWORD返回之中。

 

指定输入文件

         恢复文件信息的函数首先要求操作文件的名字,pszPath参数就是用于这个目的。然而,有一些观念是需要澄清的。其中之一是,它可以是路径名(正象所期望的),或是一个PIDL,这在第二章中讨论过了。

         如果想要传递一个PIDL,而不是普通的路径名,你就应该设置SHGFI_PIDL标志到uFlags变量中。反之也是如此:如果设置了SHGFI_PIDL,则pszPath就必须指向一个ITEMIDLIST结构(即一个PIDL)。当然,pszPath也可以是文件夹名或驱动器名,此时,你需要把一个 /’留在路径名的最后。即,你应该指定‘c:/’而不是‘c:’以避免错误地恢复了驱动器信息。

 

SHGetFileInfo()中使用通配符

         资料中并没有给出关于在SHGetFileInfo()函数中使用通配符的的任何解释,因此,你可能认为通配符不能识别。然而,我发现,如果传递一个带有通配符的串,然后提供至少一个文件匹配这个模式,函数照样正确工作。下图显示了一个简单程序的输出结果,这个程序将在后面详细讨论:

                

这个程序让我们选择一个文件名或路径名,然后恢复它的信息。它返回一个图标,显示名,类型名和所有其它属性的列表。另,你也可以询问程序以确定可执行文件的类型。Exe文件类型复选框抑制所有其它的选择。‘返回码’标签显示函数的返回码(或它的文字描述),通过选中‘接受任何文件名’复选框,可以强迫函数接受任何东西作为输入文件。

         上面显示,程序使用e:/mssdk/doc/misc/*.txt路径名,你可以看到程序的响应:图标和类型名是正确的(在我的PC上,文本文件的描述是‘Text Document)。奇怪,尽管指定了通配符,我们还是获得了非空的文件属性和显示名。图标和类型名可以从文件的扩展名中获得,但是,同样的情况对显示名和属性是不行的—显然那些信息是相对于一个特殊文件的。

         为了检测所发生的事情,我们再使用不同的路径调用函数:e:/mssdk/doc/misc/g*.*。象所看到的,在扩展名中和文件名中都有通配符。对话框的结果如下所示:

                  

正如显示所见,我们获得了与前面相同的信息文件,这明显说明,如果传递通配符,SHGetFileInfo()函数取第一个匹配这个串的文件,并对它进行操作。如果没有匹配这个模式的文件,这个函数什么也不做,直接返回0。另一个我们需要检测的情况是传递一个由‘**’结尾的路径名。如图所示,函数返回相关文件夹的信息:

                 

还要继续检测吗?先暂停一会,让我们来仔细地查看一下函数的输出。在‘显示(Display)’字段,你可以看到一个点(),就象在老DOS下目录列表一样。这个结果印证了我前面所说的:SHGetFileInfo()函数操作在名字匹配于这个模式的第一个文件对象上。事实上,如果你试着使用*.*来枚举一个文件夹的内容,作为匹配的串,所获得的第一个项是点(.)。如果还不信,选择测试下面的代码:

WIN32_FIND_DATA wff;

FindFirstFile("*.*", &wff);

Msg(wff.cFileName);

总的来说,即使这个特征没有写进资料,你也可以在函数中使用通配符如果:

         指定一个至少匹配一个文件的模式串

         知道函数停止在第一个找到的文件上

这可能是SHGetFileInfo()函数的内在代码的某个地方保持了一个WIN32_FIND_DATA结构所致。它由底层文件信息所填写,因此,用在这里就一点也不奇怪了。附带地,这个结构还涉及到另一个Shell函数SHGetDataFromIDList(),他也返回文件对象的信息,这将在后面章节中进行表述。

 

文件的显示名

         查看上面的截图,并在你自己的机器上运行这个程序,你就会注意到它返回的显示名稍微有些不同。在我的机器上,显示名是由文件名加扩展名组成,但是在你的机器上可能只看到文件名。这依赖于探测器的‘观察|文件夹选项’对话框的设置,在此,你可以选择‘隐藏已知类型的文件扩展名’。

         这里,‘已知文件类型’是指一个注册的文件类型。我们在第十四章中讨论怎样注册文件类型。现在,知道它就是一个Shell知道怎样处理的文档类型就足够了。如果你双击一个已知类型的文件,偶然地这个资料将由知道怎样处理它的程序打开。要编程取得这个设置,你需要使用SHGetSettings()函数。我们将在下一章讲述。

 

示例程序

         前面,我们已经看到了这个用于测试的示例程序。它是一个基于对话框的应用程序。这次我给它取的工程(project)名为FileInfo。示例的操作部分围绕SHGetFileInfo()函数展开,作为一个通用查询执行器,它查询给定文件或文件夹的状态和属性。下面是程序界面:

 

   

正如所见,用户界面由一个编辑框和相关的按钮组成,使你能选择一个文件。不幸地是,这种风格不能选择目录,如果你想传递文件夹名,就必须手动键入。复选检查框允许选择想要加到调用中的标志,如果选中了EXE类型框,所有其它复选框都被禁止。这是因为SHGetFileInfo()函数要求单独指定可执行类型标志。文件图标绘制在一个静态控件上,属性被解析并转换为描述串。

大多数代码都在OnOK()方法中,当用户单击‘Go’按钮后执行这段代码。要成功地编译这段代码,记住包含 #include "resource.h"语句,并保存对话框控件的IDs<shlobj.h>中声明了SHGetFileInfo()的原形:

void OnOK(HWND hDlg)

{

TCHAR szFile[MAX_PATH] = {0};

TCHAR szBuf[1024] = {0};

// 取得文件名

GetDlgItemText(hDlg, IDC_FILENAME, szFile, MAX_PATH);

/////////////////////////////////////////

// 收集标志

//

DWORD dwFileAttributes = 0;

UINT uFlags = 0;

if(IsDlgButtonChecked(hDlg, IDC_FILEICON))

uFlags |= SHGFI_ICON;

if(IsDlgButtonChecked(hDlg, IDC_DISPLAYNAME))

uFlags |= SHGFI_DISPLAYNAME;

if(IsDlgButtonChecked(hDlg, IDC_TYPENAME))

uFlags |= SHGFI_TYPENAME;

if(IsDlgButtonChecked(hDlg, IDC_OTHER))

uFlags |= SHGFI_ATTRIBUTES;

if(IsDlgButtonChecked(hDlg, IDC_WILDCARD))

uFlags |= SHGFI_USEFILEATTRIBUTES;

if(IsDlgButtonChecked(hDlg, IDC_EXETYPE))

uFlags = SHGFI_EXETYPE;

/////////////////////////////////////////

// 调用函数

//

SHFILEINFO sfi;

ZeroMemory(&sfi, sizeof(SHFILEINFO));

DWORD dwRC = SHGetFileInfo(

szFile, dwFileAttributes, &sfi, sizeof(SHFILEINFO), uFlags);

/////////////////////////////////////////

// 处理界面显示

//

wsprintf(szBuf, "%d", dwRC);

SetDlgItemText(hDlg, IDC_RETCODE, szBuf);

wsprintf(szBuf, "Icon Index: %d", sfi.iIcon);

SetDlgItemText(hDlg, IDC_ICONINDEX, szBuf);

SetDlgItemText(hDlg, IDC_DISPLAY, sfi.szDisplayName);

SetDlgItemText(hDlg, IDC_TYPE, sfi.szTypeName);

/////////////////////////////////////////

// Parse 解析和显示属性

//

DWORD dwAttrib = sfi.dwAttributes;

lstrcpy(szBuf, "");

if(dwAttrib != 0)

{

if(dwAttrib & SFGAO_CANCOPY)

lstrcat(szBuf, "Copy, ");

if(dwAttrib & SFGAO_CANMOVE)

lstrcat(szBuf, "Move, ");

if(dwAttrib & SFGAO_CANDELETE)

lstrcat(szBuf, "Delete, ");

if(dwAttrib & SFGAO_CANRENAME)

lstrcat(szBuf, "Rename, ");

if(dwAttrib & SFGAO_CANLINK)

抱歉!评论已关闭.