Windows shell最重要的组成部件是explorer.exe,在使用Windows操作系统时,开始菜单、任务栏、资源管理器等都是explorer.exe提供的。
Windows shell编程接口的函数名一般是以SH开头的。一般Shell API都在shlobj.h头文件中声明,由Shell32.dll导出,链接时需要使用到Shell32.lib库。
Shell最重要的功能之一是进行文件浏览、查找、管理以及将文件和应用程序关联。Windows Shell中有很多特殊目录和文件,比如“我的文档”、“桌面”、“回收站”、“程序文件”(Program files)等。这些目录都是shell的特殊目录。
Shell对目录和文件的管理形式:
Shell有一种特殊的文件和目录管理方式,每个目录都有一个PIDL(Pointer of Item identifier list,项标识符表指针)值,这个值唯一标识一个文件夹。
有系统定义的特殊文件夹的CSIDL(constant special item ID list)是常数,如:
CSIDL_DESKTOP //"桌面"文件夹
CSIDL_FAVORITES //"收藏夹"
CSIDL_FONTS //字体文件夹
CSIDL_MYDOCUMENTS //"我的文档"
CSIDL_MYMUSIC //"我的音乐"
CSIDL_PROFILE //"用户文件夹",一般是"C:/Document and Settings/username"
CSIDL_PROGRAMS //"程序"文件夹,一般是"C:/Program Files"
CSIDL_RECENT //"最近的文档"
CSIDL_STARTMENU //"开始菜单"目录
CSIDL_SYSTEM //"系统"目录
CSIDL_WINDOWS //"Windows"目录
特殊目录的操作:
SHGetSpecialFolderPath函数用于通过文件夹的CSIDL来获得文件夹的路径:
BOOL SHGetSpecialFolderPath(
HWND hwndOwner, //保留值
__out LPTSTR lpszPath, //指向用于存放返回的文件夹路径的字符串缓冲区,
//大小至少为MAX_PATH个字符
__in int csidl, //指定的CSIDL值
__in BOOL fCreate //指定如果目标文件夹不存在是否创建一个新的
);
SHGetFolderLocation函数用于获取文件夹的路径,并保存在ITEMIDLIST结构中:
HRESULT SHGetFolderLocation(
__in HWND hwndOwner, //保留
__in int nFolder, //指定的CSIDL值
__in HANDLE hToken, //代表一个特定用户的访问令牌,一般设为NULL
__reserved DWORD dwReserved, //保留
__out PIDLIST_ABSOLUTE *ppidl //IDL指针的地址(即PIDL的地址)该IDL指定相对于命名空间
//根目录(桌面)的一个文件或目录的位置
//函数失败时该值设为NULL,
//调用的程序应该使用ILFree函数来释放该资源
);
返回值:
成功时,返回S_OK;
失败时,返回ERROR_FILE_NOT_FOUND(CSIDL值有效,但找不到目录);E_INVALIDARG(CSIDL值无效)。
SHGetPathFromIDList函数用于将PIDL转换成路径:
BOOL SHGetPathFromIDList(
__in PCIDLIST_ABSOLUTE pidl, //IDL的地址,该IDL指定相对于命名空间根目录(桌面)的
//一个文件或目录的位置
__out LPTSTR pszPath //用于接收文件系统路径的缓冲区地址,大小至少为MAX_PATH个字符
);
实例代码如下:
#include <windows.h>
#include <shlobj.h>
#include <stdio.h> //printf函数(包含在_tprintf中)
#include <tchar.h> //_tprintf函数
#pragma comment(lib, "Shell32.lib") //可有可无
int main()
{
//获得"我的文档"的路径
TCHAR szMyDocument[MAX_PATH];
//获取特殊目录
SHGetSpecialFolderPath(NULL, szMyDocument, CSIDL_PERSONAL, FALSE);
//获取桌面的路径
TCHAR szDesktop[MAX_PATH];
LPITEMIDLIST pidl = NULL;
LPMALLOC pMalloc = NULL;
//分配
SHGetMalloc(&pMalloc);
//获取任意目录的路径
SHGetFolderLocation(NULL, CSIDL_DESKTOPDIRECTORY, NULL, 0, &pidl);
SHGetPathFromIDList(pidl, szDesktop);
//释放
pMalloc->Free(pidl);
pMalloc->Release();
//显示结果
_tprintf("My Document:/t%s/n", szMyDocument);
_tprintf("Desktop:/t%s/n", szDesktop);
system("pause");
return 0;
}
上面代码使用两种方法来获得特殊文件夹路径,一种是直接使用SHGetSpecialFolderPath;另一种更为通用,使用SHGetFolderLocation从PIDL或CSIDL获得文件夹位置后,再使用SHGetPathFromIDList获得文件路径字符串。
绑定、遍历、属性获取:
通过Shell API可以编写更为一般的文件操作,如:
1)将IShellFolder接口绑定到目录对象上,以获取目录、子目录等相关信息;
2)获取IEnumIDList接口,用于遍历目录中包含的对象;
3)获取和显示文件和目录对象的相关属性。
SHGetDesktopFolder函数用于获取以IShellFolder接口形式返回的桌面文件夹:
HRESULT SHGetDesktopFolder(
__out IShellFolder **ppshf //ppshf指向返回的IShellFolder指针
);
返回值:
成功时,返回S_OK;
失败时,返回HRESULT错误码。
IShellFolder是Windows shell程序对目标进行管理的一个重要的接口。每一个目录对应一个实例化的IShellFolder接口。IShellFolder接口的成员包括EnumObjects、GetAttributesOf、GetDisplayNameOf等。
IEnumIDList接口提供了一组标准方法,用于遍历PIDL,其成员包括Clone、Next、Reset、Skip等。
STRRET结构是很多IShellFolder接口成员返回的字符串形式:
typedef struct _STRRET {
UINT uType; //指定返回的字符串的格式:
//STRRET_CSTR---cStr数组形式;
//STRRET_OFFSET---uOffset形式;
//STRRET_WSTR---pOleStr形式
union {
LPWSTR pOleStr; //存放返回的字符串的地址
UINT uOffset; //从IDL开始的uOffset个字节处就是返回的字符串的位置
CHAR cStr[MAX_PATH]; //存放返回字符串的缓冲区
} ;
} STRRET, *LPSTRRET;
代码实例,列举“回收站”中的文件和目录:
#include <windows.h>
#include <shlobj.h>
#include <stdio.h>
#include <tchar.h>
//#pragma comment(lib, "Shell32.lib")
int main()
{
TCHAR pszPath[MAX_PATH];
//IShellFolder接口
IShellFolder *pisf = NULL;
IShellFolder *pisfRecBin = NULL;
//获得Shell命名空间根目录,即桌面
SHGetDesktopFolder(&pisfRecBin);
IEnumIDList *peidl = NULL; //对象遍历接口
LPITEMIDLIST pidlBin = NULL;
LPITEMIDLIST idlCurrent = NULL;
LPMALLOC pMalloc = NULL;
//分配
SHGetMalloc(&pMalloc);
//回收站位置
SHGetFolderLocation(NULL, CSIDL_BITBUCKET, NULL, 0, &pidlBin);
//绑定回收站对象
pisfRecBin->BindToObject(pidlBin, NULL, IID_IShellFolder, (void**)&pisf);
//列举回收站对象,得到IEnumIDList接口,包括SHCONTF_FOLDERS、
//SHCONTF_NONFOLDERS、¡SHCONTF_INCLUDEHIDDEN类型对象
pisf->EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS |
SHCONTF_INCLUDEHIDDEN, &peidl);
STRRET strret;
ULONG uFetched;
HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
_tprintf("/nFiles In Recycle Bin:/n");
while(1)
{
//遍历IEnumIDList对象,idlCurrent为当前对象
if (peidl->Next(1, &idlCurrent, &uFetched) == S_FALSE)
{
break;
}
//获取回收站当前对象当前的路径
SHGetPathFromIDList(idlCurrent, pszPath);
//DisplayName,删除前的路径
pisf->GetDisplayNameOf(idlCurrent, SHGDN_NORMAL, &strret);
//显示,_tprintf可能会造成字符串编码不正确
//wprintf(L"/t%s/n", strret.pOleStr);---->wprint显示不了中文
WriteConsoleW(hOutput, L"/t", 1, NULL, NULL);
WriteConsoleW(hOutput, strret.pOleStr, lstrlenW(strret.pOleStr), NULL, NULL);
WriteConsoleW(hOutput, L"/n", 1, NULL, NULL);
}
//释放资源
pMalloc->Free(pidlBin);
pMalloc->Free(strret.pOleStr);
pMalloc->Release();
peidl->Release();
pisf->Release();
system("pause");
return 0;
}
在显示结果时,由于STRRET strret保存的是UNICODE编码的字符串,在使用输出函数进行结果输出时,需要注意字符编码。
浏览文件夹对话框:
代码如下:
#include <windows.h>
#include <shlobj.h>
/************************************************************************/
/* 功能:弹出"浏览文件夹"对话框,并获取用户选择的文件夹目录 */
/* 参数:hwnd--父窗口句柄 */
/************************************************************************/
DWORD Browse(HWND hwnd)
{
//用于保存路径
TCHAR szRoot[MAX_PATH];
TCHAR szChoose[MAX_PATH];
TCHAR szDisplayName[MAX_PATH];
//相关变量
LPITEMIDLIST pidlRoot = NULL;
LPITEMIDLIST pidlSelected = NULL;
BROWSEINFO bi = {0};
LPMALLOC pMalloc = NULL;
//"浏览文件夹"的根路径,开发人员可根据情况选择,比如只浏览"我的文档"
SHGetFolderLocation(NULL, CSIDL_DESKTOP, NULL, 0, &pidlRoot);
SHGetPathFromIDList(pidlRoot, szRoot);
//填充BROWSEINFO结构
bi.hwndOwner = hwnd;
bi.pidlRoot = pidlRoot;
bi.pszDisplayName = szDisplayName;
bi.lpszTitle = "Choose a target";
bi.ulFlags = 0;
bi.lpfn = NULL;
bi.lParam = 0;
//弹出对话框
pidlSelected = SHBrowseForFolder(&bi);
//DisplayName
MessageBox(NULL, szDisplayName, "Display Name:", MB_OK);
//选择的文件夹
SHGetPathFromIDList(pidlSelected, szChoose);
MessageBox(NULL, szChoose, "Choose:", MB_OK);
//释放
ILFree(pidlRoot);
return 0;
}
/************************************************************************/
/* 程序入口函数 */
/************************************************************************/
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nShowCmd )
{
return Browse(NULL);
}