提取图标信息异常:ICONINFOEX字段值为0的问题排查
图标资源信息获取失败的原因及解决方案
问题重现
执行以下代码后,控制台无输出,ICONINFOEX的szResName和wResID字段均为0,但目标文件确实包含图标,HICON和ICONINFOEX对象也不为空:
#include <Windows.h> #include <vector> #include <iostream> using namespace::std; BOOL CALLBACK EnumerateIcons(HMODULE hModule, LPCTSTR type, LPTSTR szName, LONG_PTR ptr) { if (ptr == 0) return FALSE; vector<HICON>* pVec = (vector<HICON>*)ptr; HRSRC hRes = FindResource(hModule, szName, type); if (hRes == NULL) return TRUE; DWORD dwSize = SizeofResource(hModule, hRes); HGLOBAL hGlobal = LoadResource(hModule, hRes); if (hGlobal == NULL) return TRUE; LPBYTE bytes = (BYTE*)LockResource(hGlobal); HICON hIcon = CreateIconFromResourceEx(bytes, dwSize, TRUE, 0x00030000, 0, 0, LR_SHARED); if (hIcon) pVec->push_back(hIcon); return TRUE; } VOID IndexIcons(LPCWSTR szFileName) { HMODULE Binary = LoadLibraryEx(szFileName, NULL, LOAD_LIBRARY_AS_IMAGE_RESOURCE); if (!Binary) return; vector<HICON> Icons; EnumResourceNames(Binary, RT_ICON, (ENUMRESNAMEPROC)EnumerateIcons, (LONG_PTR)&Icons); for (HICON hIcon : Icons) { ICONINFOEX iconInfo = { sizeof(iconInfo) }; if (!GetIconInfoExW( hIcon, &iconInfo)) continue; wprintf(L"%s %d\n", iconInfo.szResName, iconInfo.wResID); } }
原因分析
GetIconInfoEx的设计限制:通过CreateIconFromResourceEx生成的HICON是基于内存资源数据创建的独立图标对象,并未与原模块中的资源条目建立关联。GetIconInfoEx的szResName和wResID字段仅对通过LoadIcon/LoadImage直接从模块加载的图标有效,对内存生成的图标不会填充这些字段,所以返回0。- 枚举范围不完整:Windows中图标通常以
RT_GROUP_ICON(图标组,对应资源管理器中看到的图标集合)形式存在,而你只枚举了RT_ICON(单个图标资源),可能漏掉了大部分实际需要的图标资源;此外,szName可能是整数ID(而非字符串),当前代码未区分这种情况,导致部分资源处理异常。
解决方案
方案一:枚举时直接记录资源标识
不需要依赖GetIconInfoEx,在EnumerateIcons回调中直接保存资源的原始名称/ID,因为此时的szName就是资源的原始标识:
#include <Windows.h> #include <vector> #include <iostream> using namespace::std; struct IconResourceInfo { HICON hIcon; // 存储资源名称或ID,通过isId区分 LONG_PTR resIdent; BOOL isId; }; BOOL CALLBACK EnumerateIcons(HMODULE hModule, LPCTSTR type, LPTSTR szName, LONG_PTR ptr) { if (ptr == 0) return FALSE; vector<IconResourceInfo>* pVec = (vector<IconResourceInfo>*)ptr; HRSRC hRes = FindResource(hModule, szName, type); if (hRes == NULL) return TRUE; DWORD dwSize = SizeofResource(hModule, hRes); HGLOBAL hGlobal = LoadResource(hModule, hRes); if (hGlobal == NULL) return TRUE; LPBYTE bytes = (BYTE*)LockResource(hGlobal); HICON hIcon = CreateIconFromResourceEx(bytes, dwSize, TRUE, 0x00030000, 0, 0, LR_SHARED); if (hIcon) { IconResourceInfo info; info.hIcon = hIcon; info.isId = IS_INTRESOURCE(szName); info.resIdent = (LONG_PTR)szName; pVec->push_back(info); } return TRUE; } VOID IndexIcons(LPCWSTR szFileName) { HMODULE Binary = LoadLibraryEx(szFileName, NULL, LOAD_LIBRARY_AS_IMAGE_RESOURCE); if (!Binary) return; vector<IconResourceInfo> Icons; // 同时枚举RT_ICON和RT_GROUP_ICON EnumResourceNames(Binary, RT_ICON, (ENUMRESNAMEPROC)EnumerateIcons, (LONG_PTR)&Icons); EnumResourceNames(Binary, RT_GROUP_ICON, (ENUMRESNAMEPROC)EnumerateIcons, (LONG_PTR)&Icons); for (const auto& info : Icons) { if (info.isId) { wprintf(L"资源ID: %d\n", LOWORD(info.resIdent)); } else { wprintf(L"资源名称: %s\n", (LPTSTR)info.resIdent); } DestroyIcon(info.hIcon); } FreeLibrary(Binary); }
方案二:直接遍历资源目录获取信息
如果不需要创建HICON,可以直接通过资源枚举函数获取所有图标资源的名称/ID,无需加载资源数据:
#include <Windows.h> #include <iostream> using namespace::std; BOOL CALLBACK EnumIconResProc(HMODULE hModule, LPCTSTR lpszType, LPTSTR lpszName, LONG_PTR lParam) { // 区分资源是ID还是字符串名称 if (IS_INTRESOURCE(lpszName)) { wprintf(L"图标资源ID: %d\n", LOWORD((LONG_PTR)lpszName)); } else { wprintf(L"图标资源名称: %s\n", lpszName); } return TRUE; } VOID IndexIcons(LPCWSTR szFileName) { HMODULE Binary = LoadLibraryEx(szFileName, NULL, LOAD_LIBRARY_AS_IMAGE_RESOURCE); if (!Binary) return; // 枚举图标组和单个图标资源 EnumResourceNames(Binary, RT_GROUP_ICON, EnumIconResProc, 0); EnumResourceNames(Binary, RT_ICON, EnumIconResProc, 0); FreeLibrary(Binary); }
内容的提问来源于stack exchange,提问作者Blaze
相关产品推荐
相关产品推荐

