提取快捷方式的图标资源问题

一、背景

  获取桌面上的快捷方式并提取出图标资源。

二、问题点分析

  1. 如何获取桌面上的快捷方式?
  2. 如果通过快捷方式路径提取到图标资源?

三、获取桌面上的快捷方式

  Windows桌面上的文件保存在两个文件夹下,分别是:公共桌面文件夹和用户桌面文件夹。一般对于的系统路径是为系统盘下,例如:C:UserssmartDesktop(用户)和C:UsersPublicDesktop(公共)。然后对这两个文件夹下的文件遍历筛选出扩展名为lnk的快捷方式.

  对于实际编程中,桌面的文件路径是一种系统特殊文件夹路径,不能规定死了。而且如果使用过桌面文件迁移软件对桌面迁移过,桌面文件夹的目录也会发生变化。不过,微软为我们提供了一些API获取特殊文件路径

1 BOOL SHGetSpecialFolderPath( HWND hwnd,  //保留,设置为NULL
2 
3                    LPSTR pszPath, //特殊文件夹路径
4 
5                       int csidl,              //需要获取的特殊文件夹类型,如果是虚拟文件夹,获取失败
6 
7                    BOOL fCreate    //如果文件夹不存在,是否创建
8 
9                    );

  该函数在以后可能不在受支持,可以采用新的API。

 1 SHFOLDERAPI SHGetFolderPath( HWND hwnd, //保留,设置为NULL
 2 
 3                        int csidl,          //需要获取的特殊文件夹类型,如果是虚拟文件夹,获取失败
 4 
 5                      HANDLE hToken, //访问令牌,一般设置为NULL,也可以传入当前登录用户的令牌.
 6 
 7                                                                        DWORD dwFlags, //指定要返回的路径的标志
 8 
 9                                                                         LPSTR pszPath //特殊文件夹路径
10 
11                      );

  从Windows Vista开始,该函数只是SHGetKnownFolderPath的包装器。

1 HRESULT SHGetKnownFolderPath( REFKNOWNFOLDERID rfid,  //获取特殊文件夹路径类型
2 
3                     DWORD dwFlags, //指定特殊检索选项的标志,可以为0
4 
5                     HANDLE hToken, //访问令牌, 一般为空,如果为空,则该函数请求当前用户的已知文件夹,否则请求特定用户的已知文件夹
6 
7                     PWSTR *ppszPath //特殊文件夹路径 调用进程负责通过CoTaskMemFree调用释放不再需要的资源
8 
9                     );

  对该函数封装一个获取系统特殊文件夹路径的类,方便使用。

  获取到桌面文件目录后,我们就可以对文件夹下的文件进行枚举,通过文件扩展名筛选出快捷方式的文件。可以使用c++14标准库中的filesystem::path进行枚举,这里主要使用微软提供的API函数:

 1 HANDLE FindFirstFile( LPCSTR lpFileName, //查询的文件目录或路径
 2 
 3               LPWIN32_FIND_DATAA lpFindFileData //接收有关找到的文件或目录的信息
 4 
 5                );
 6 
 7 BOOL FindNextFile( HANDLE hFindFile,  //通过FindFirstFile返回的文件句柄
 8 
 9              LPWIN32_FIND_DATAA lpFindFileData //接收有关找到的文件或目录的信息
10 
11               );

  通过这两个函数就可以对指定文件目录下的文件进行查询,获取文件相关信息。FindFirstFileEx函数是FindFirstFile扩展函数,如果需要获取其他文件属性,使用该函数。

四、通过快捷方式路径提取图标资源

  当我们获取到快捷方式的路径后,还需要进一步处理才能提取到图标资源。通过快捷方式提取信息需要用到windows提供的COM接口:IShellLink。是由COM接口之前,都需要初始化COM。然后创建IShellLink的接口实例,其中通过GetIconLocation接口函数获取到制定快捷方式的图标路径,但是如果该快捷方式图标资源不是.ico,则获取的图标路径为空。我们可以通过右键快捷方式属性查看到图标资源是否使用的是.ico文件.

        

   但很多快捷方式并不是直接使用.ico资源文件,而是.exe中编译时编译成二进制的icon资源。

        

  对于这种快捷方式指向的资源路径为可执行路径时,可以通过其他方式从执行文件或dll文件中提取图标句柄,将图标句柄转换为图标资源。IShellLink::GetPath可以获取到快捷方式的目标文件路径,即可执行路径(并不一定是是.exe文件)。微软为我们提供了一些API获取图标资源句柄:

1 HICON ExtractIconA( HINSTANCE hInst,  //调用该函数的实例句柄 一般为NULL
2 
3                 LPCSTR pszExeFileName, //可执行文件,DLL或图标文件的名称
4 
5                                                      UINT nIconIndex               //要检索的图标的从零开始的索引 0:表示从第一个开始,-1:获取图标总数
6 
7               );

  该函数和ExtractIconEx(提取大图标和小图标)一般提取16*16 和32*32像素的图标.使用完图标资源后,调用DestoryIcon是否资源。

  如果需要提取一些像素更大的图标资源,上面的API就无法实现了,需要使用其他API获取。

 1 DWORD_PTR SHGetFileInfoW( LPCWSTR pszPath,          //提取图标资源的文件路径
 2 
 3                   DWORD dwFileAttributes, //文件属性标志位
 4 
 5                   SHFILEINFOW *psfi,         //接收文件信息
 6 
 7                   UINT cbFileInfo,               //指向SHFILEINFO结构体大小
 8 
 9                   UINT uFlags                    //要检索的文件信息的标志
10 
11                   );

  在调用SHGetFileInfo之前, 必须初始化组件对象模型(COM)

1 SHSTDAPI SHGetImageList( int iImageList,
2 
3                  REFIID riid,
4 
5                     void **ppvObj
6 
7                 );

  如果提取48*48或256*256像素大小的图标,需要使用该函数获取。Windows系统标准图标大小分别为:小(16 *16) 大(32 * 32)超大(48 *48)和巨型(256 *256).所以通过API获取的一般是系统标准大小的图标,但是有些应用存在其他大小的不同,例如:64 * 64 128 *128等像素大小的图标,可以同过PE格式提取等方式,具体方式,目前还为找到。

  关于如何将HICON图标句柄资源保存到本地,可以使用GDI接口将HICON保存为bmp格式的图片文件,但是bmp占用空间大,压缩比较差,使用GDI比较麻烦,如果感兴趣的同学,可以使用微软封装的gdiplus库,转换方便,且可以将图标资源转换为其他格式。

参考:

1、https://docs.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetspecialfolderpatha

2、https://www.cnblogs.com/aspring/archive/2005/03/12/117337.html

3、https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findfirstfilea

4、https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-ishelllinka

5、https://docs.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-extracticona

6、https://stackoverflow.com/questions/16330403/get-hbitmaps-for-all-sizes-and-depths-of-a-file-type-icon-c

7、https://docs.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shgetfileinfow

8、https://docs.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shgetimagelist 。   

   

原文地址:https://www.cnblogs.com/smartNeo/p/11386109.html