C++获取系统图标方法

来源:互联网 发布:外汇实时分析软件 编辑:程序博客网 时间:2024/06/03 19:29
系统图像列表(有时亦被称作 Shell 图标缓存)是一个由 Windows Shell 维护的图标资源,资源管理器和其它应用程序使用这个列表来显示系统对象、程序和文件类型的图标。其实就是一个简单的 HIMAGELIST(可以用图像列表 API 存取的标准图像列表),一些应用程序可能会发现显示系统提供的图标更好一些,而不是自己内部存储这些图标的副本。所以,本教程的目的就是解说如何存取系统的图像列表,并在你的应用程序里使用它。
简单的方法
如果你先前从未花时间浏览过 MSDN 的话,你可能就会调用 SHGetFileInfo API(由 shell32.dll 导出,Win9x/WinNT4/2000/XP 都可使用)。这个函数看起来是这样的:
DWORD_PTR SHGetFileInfo(
    LPCTSTR     pszPath,              
    DWORD       dwFileAttributes,
    SHFILEINFO *psfi,
    UINT        cbFileInfo,
    UINT        uFlags
);
复制代码
此函数可被用来获得指定文件的信息,uFlags 参数则指定了要获取的具体哪些信息。对于这个参数来说,有两个非常有趣的取值: SHGFI_ICON 和 SHGFI_SYSICONINDEX 。当指定了这两个值之后,SHGetFileInfo 会返回系统图像列表的句柄,并将相应的图标类型索引保存到 SHFILEINFO 结构中。
下面的调用示范了可能的使用方法:
SHFILEINFO shfi;
HIMAGELIST hSysImgList;
...
hSysImgList = (HIMAGELIST)SHGetFileInfo(
       "C:", 
       0, 
       &shfi, 
       sizeof(SHFILEINFO), 
       SHGFI_SYSICONINDEX | SHGFI_SMALLICON | SHGFI_ICON);
平台差异
在 Windows 9x 和 Windows NT 系列的操作系统之间,存在一个很微小但又十分重要的区别。在 Windows 9x 下,SHGetFileInfo 返回系统图像列表的句柄,这个句柄是被所有进程共享的。这个图像列表与资源管理器所使用的以及 Shell 用来显示系统图标的图像列表是同一个图像列表,这就意味着如果一个进程对这个图像列表进行了任何的误操作,那么所有的进程(包括资源管理器)都会受到影响。
在 Windows NT (以及 2000/XP )之下,就会略有不同。由于系统架构的不同,亦为保持一个稳定的操作环境,SHGetFileInfo 会为每个请求系统图像列表的进程返回一份单独的拷贝。详而述之,SHGetFileInfo 并不会像 Windows 9x 那样返回一个完整的系统图像列表——只有在 SHGetFileInfo 调用中所请求的图标会出现在返回的图像列表中。对于 99% 的情况来说并没有什么关系,但是由于我们要制作一个浏览整个图像列表的应用程序,这就向我们提出了一个问题。
在所有的 Windows 版本中定位系统图像列表
闲话休叙,在 shell32.dll 中有一个名为 Shell_GetImageLists 的未公开 API ,——你可能猜到了,它会返回系统图像列表的句柄(注意函数名的复数形式,有大图标和小图标的版本)。这个 shell32.dll 中未公开的 API 适用于所有的 Windows 版本。
BOOL WINAPI Shell_GetImageLists(HIMAGELIST *lphimlLarge, HIMAGELIST *lphimlSmall);
我们需要的另一个 API 是 FileIconInit, 它只适用于 Windows NT/2000/XP 。这不是问题,因为我们只需要在 Windows NT 下调用它。
BOOL WINAPI FileIconInit(BOOL bFullInit);
这些 API 并未用函数名导出,而只有序号:
// shell32 未公开的函数
Shell_GetImageLists @71
FileIconInit        @660
复制代码
获得了这一信息后,我们现在可以编写一个函数来返回系统图像列表的句柄了。第一步是定义两个 typedef ,以使我们的工作更容易些。
typedef BOOL (WINAPI * SH_GIL_PROC)(HIMAGELIST *phLarge, HIMAGELIST *phSmall);
typedef BOOL (WINAPI * FII_PROC)   (BOOL fFullInit);
复制代码
SH_GIL_PROC 是 Shell_GetIconLists_Procecedure 简写,同理 FII_PROC 是 FileIconInit_Procedure 的简写。现在我们可以定位这些 shell32.dll 未公开的的函数了,以便调用。
HMODULE      hShell32;
SH_GIL_PROC  Shell_GetImageLists;
FII_PROC     FileIconInit;    
// 装载 shell32.dll ,如果它尚未装载的话
hShell32 = LoadLibrary("shell32.dll");
if(hShell32 == 0)
    return FALSE;//
// 从 shell32.dll 中获取未公开的 API
//
Shell_GetImageLists  = (SH_GIL_PROC) GetProcAddress(hShell32, (LPCSTR)71);
FileIconInit         = (FII_PROC)    GetProcAddress(hShell32, (LPCSTR)660);
复制代码
请注意我们是如何把序号参数传给 GetProcAddress 的。OK ,现在我们已经获得了所需的函数指针,可以进行处理了。
// 初始化本进程的图像列表——在 Win95/98 下函数是不可用的
if(FileIconInit != 0)
    FileIconInit(TRUE);
// 获得大图标和小图标的系统图像列表句柄

Shell_GetImageLists(phLarge, phSmall);

// 初始化本进程的图像列表, 为加载系统图标列表做准备
typedef BOOL (WINAPI * pfn_FileIconInit) (BOOL fFullInit);
pfn_FileIconInit FileIconInit = (pfn_FileIconInit) GetProcAddress(LoadLibrary("shell32.dll"), (LPCSTR)660);
FileIconInit(TRUE);

// 不要卸载 shell32.dll !
上面代码段最后的注释指出“不要卸载 shell32.dll ”,这是非常重要的。因为只要 shell32.dll 被卸载,系统图像列表就会被销毁,这样一来我们是前的所有工作都会付之东流。
永远不要修改或删除系统图像列表!
在我们往下进行之前,你应该了解另外一条重要的信息。你永远不要去尝试在系统图像列表中添加、删除图标,也不要删除系统图像列表。这一点在 SHGetFileInfo 的文档中已经解释得很清楚了,而且这也是个很好的意见。但是,仍然有一个很简单的方法会将系统图像列表误删。
现在来说这个问题。在默认情况下,当一个列表视图(ListView)控件被销毁后,它会自动调用 ImageList_Destroy API 来删除与之关联的图像列表。也就是说,在默认的情况下你甚至要求你的程序删除系统图像列表!——是的,当时就是这样。所幸,列表视图控件有一个 LVS_SHAREIMAGELISTS 样式可以防止它删除它的图像列表。(在这种情况下,请确认你设置了这个样式!)
十分诡异,树型视图(TreeView)控件却不会引起这一问题—— MSDN 指出树型视图不会销毁与之关联的图像列表,所以只有列表视图控件需要特别注意。
原创粉丝点击