将图标资源(不包括重复的)保存到磁盘
Saving icon resources (not including the duplicate ones) to disk
最近做了一个程序,它加载一个可执行文件或DLL文件,枚举其中的所有图标资源,然后将每个图标资源保存为一个文件夹中的.ico文件。但它使用直方图(如 this post 中讨论的那样)来避免保存重复图标(看起来相同的图标)。
该程序首先从用户那里获取两条路径(用户必须在控制台中键入它们 window)。其中一个是exe或dll文件的路径,另一个是.ico文件所在文件夹的路径。
然后程序通过调用 LoadLibraryEx
加载 exe 或 dll 文件,并通过调用 EnumResourceNames
.
枚举图标资源
传递给 EnumResourceNames
的回调函数将每个非重复图标资源保存为具有唯一名称的 .ico 文件。保存文件的名称是这样的:1.ico
、2.ico
、3.ico
、4.ico
、.etc
我的完整代码和我的问题
问题是我的程序创建了损坏的或未知的 .ico 文件,而不是真实的。
我的意思是,有些保存的图标有奇怪的图像,有些根本看不到!尽管我使用 WriteFile
函数为每个 .ico 文件编写了 BITMAPFILEHEADER
结构。
只需编译 运行 我的代码,然后在控制台 window 中键入以下内容:
C:\Windows\System32\shell32.dll
然后键入要保存 .ico 文件的现有文件夹的路径。
接下来,等待至少 4 到 7 秒,直到操作完成。
然后打开那个文件夹,然后看到图标!他们太可笑了!
以下是我的代码。抱歉,如果注释中或变量和函数名称中存在拼写错误。反正只有main()
和EnumIcons()
两个函数重要,一定要看代码下面的解释。
#include <iostream>
#include <conio.h>
#include <Windows.h>
#include <string>
#include <vector>
using namespace std;
string folderPath;
//Variables to store the number of
//successfully extracted icons.
UINT nSucceed = 0, nFail = 0;
typedef struct tagHISTOGRAM {
float bucket1; //Range 0-63
float bucket2; //Range 64-127
float bucket3; //Range 128-191
float bucket4; //Range 192-255
} HISTOGRAM, *PHISTOGRAM;
typedef struct tagIOV {
SIZE dimensions; //Width and height of the image
HISTOGRAM hgRed; //Histogram for red channel
HISTOGRAM hgGreen; //Histogram for green channel
HISTOGRAM hgBlue; //Histogram for blue channel
} IMAGE_OVERVIEW, *PIMAGE_OVERVIEW;
#define isInRange(n, s, e) (s <= n) && (n <= e)
//Constant used for the last parameter of CreateIconFromResource
#define ICO_VER 0x00030000
//Vector which stores IMAGE_OVERVIEW stuctures discribing all
//icons processed. When a new icon is being processed, the
//program checks all previous structures in the vector and
//then add the corresponding IMAGE_OVERVIEW structure to the
//vector.
vector<IMAGE_OVERVIEW> theIcons;
inline void incrementAppropriateBucket(PHISTOGRAM phg, BYTE pixel)
{
if (isInRange(pixel, 0, 63))
phg->bucket1++;
else if (isInRange(pixel, 64, 127))
phg->bucket2++;
else if (isInRange(pixel, 128, 191))
phg->bucket3++;
else if (isInRange(pixel, 192, 255))
phg->bucket4++;
}
//The following function divides each bucket total by the number
//of pixels in the entire image.
inline void finalizeHistogram(PHISTOGRAM phg, DWORD nPixels)
{
phg->bucket1 /= nPixels;
phg->bucket2 /= nPixels;
phg->bucket3 /= nPixels;
phg->bucket4 /= nPixels;
}
BOOL createImageOverview(HBITMAP hBmp, PIMAGE_OVERVIEW iov)
{
BITMAPINFO bmpInfo = {};
bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
HDC DC = CreateCompatibleDC(NULL);
SelectObject(DC, hBmp);
if (!GetDIBits(DC, hBmp, 0, 0, NULL, &bmpInfo, DIB_RGB_COLORS))
{
ReleaseDC(NULL, DC);
DeleteDC(DC);
return FALSE;
}
bmpInfo.bmiHeader.biBitCount = 32;
bmpInfo.bmiHeader.biCompression = BI_RGB;
bmpInfo.bmiHeader.biHeight = abs(bmpInfo.bmiHeader.biHeight);
iov->dimensions.cx = bmpInfo.bmiHeader.biWidth;
iov->dimensions.cy = bmpInfo.bmiHeader.biHeight;
BYTE* pixels = new BYTE[bmpInfo.bmiHeader.biSizeImage];
if (!GetDIBits(DC, hBmp, 0, bmpInfo.bmiHeader.biHeight, (PVOID) pixels, &bmpInfo, DIB_RGB_COLORS))
{
delete[] pixels;
ReleaseDC(NULL, DC);
DeleteDC(DC);
return FALSE;
}
for (LONG Y = bmpInfo.bmiHeader.biHeight - 1;Y >= 0;Y--)
for (LONG X = 0;X < bmpInfo.bmiHeader.biWidth;X++)
{
BYTE R = pixels[(bmpInfo.bmiHeader.biWidth * Y + X) * 4 + 2];
incrementAppropriateBucket(&(iov->hgRed), R);
BYTE G = pixels[(bmpInfo.bmiHeader.biWidth * Y + X) * 4 + 1];
incrementAppropriateBucket(&(iov->hgGreen), G);
BYTE B = pixels[(bmpInfo.bmiHeader.biWidth * Y + X) * 4];
incrementAppropriateBucket(&(iov->hgBlue), B);
}
DWORD nPixels = bmpInfo.bmiHeader.biWidth * bmpInfo.bmiHeader.biHeight;
finalizeHistogram(&(iov->hgRed), nPixels);
finalizeHistogram(&(iov->hgGreen), nPixels);
finalizeHistogram(&(iov->hgBlue), nPixels);
delete[] pixels;
ReleaseDC(NULL, DC);
DeleteDC(DC);
return TRUE;
}
float sumUpBucketDifferances(const HISTOGRAM hg1, const HISTOGRAM hg2)
{
float result = 0;
result += abs(hg1.bucket1 - hg2.bucket1);
result += abs(hg1.bucket2 - hg2.bucket2);
result += abs(hg1.bucket3 - hg2.bucket3);
result += abs(hg1.bucket4 - hg2.bucket4);
return result;
}
float compareImages(const IMAGE_OVERVIEW iov1, const IMAGE_OVERVIEW iov2)
{
float result = 0;
result += sumUpBucketDifferances(iov1.hgRed, iov2.hgRed);
result += sumUpBucketDifferances(iov1.hgGreen, iov2.hgGreen);
result += sumUpBucketDifferances(iov1.hgBlue, iov2.hgBlue);
return result;
}
BOOL isDuplicate(const IMAGE_OVERVIEW iov)
{
size_t s = theIcons.size();
for (size_t i = 0;i < s;i++)
{
if ((theIcons[i].dimensions.cx != iov.dimensions.cx) ||
(theIcons[i].dimensions.cy != iov.dimensions.cy))
continue;
if (compareImages(theIcons[i], iov) < 0.1)
return TRUE;
}
return FALSE;
}
BOOL CALLBACK EnumIcons(HMODULE hModule, LPCTSTR lpszType, LPTSTR lpszName, LONG_PTR lParam)
{
HRSRC hRes = FindResource(hModule, lpszName, lpszType);
if (!hRes)
{
nFail++;
return TRUE;
}
DWORD icoSize = SizeofResource(hModule, hRes);
if (!icoSize)
{
nFail++;
return TRUE;
}
HGLOBAL hIcoData = LoadResource(hModule, hRes);
if (!hIcoData)
{
nFail++;
return TRUE;
}
BYTE* icoData = (BYTE *) LockResource(hIcoData);
if (!icoData)
{
FreeResource(hIcoData);
nFail++;
return TRUE;
}
//Checking whether the icon is dupplicate
HICON hIcon = CreateIconFromResource(icoData, icoSize, TRUE, ICO_VER);
if (!hIcon)
{
FreeResource(hIcoData);
nFail++;
return TRUE;
}
ICONINFO ii;
if (!GetIconInfo(hIcon, &ii))
{
DestroyIcon(hIcon);
FreeResource(hIcoData);
nFail++;
return TRUE;
}
IMAGE_OVERVIEW iov;
ZeroMemory(&iov, sizeof(IMAGE_OVERVIEW));
BOOL r = createImageOverview(ii.hbmColor, &iov);
DeleteObject(ii.hbmColor);
DeleteObject(ii.hbmMask);
DestroyIcon(hIcon);
if (!r)
{
FreeResource(hIcoData);
nFail++;
return TRUE;
}
if (isDuplicate(iov))
{
FreeResource(hIcoData);
nFail++;
return TRUE;
}
theIcons.push_back(iov);
//End of checking whether the icon is dupplicate
//Note that the first icon is saved as 1.ico,
//the second is saved as 2.ico, and so on. So
//the number of the current icon is equal to
//the number of the previously saved icons + 1
int icoNumber = nSucceed + 1;
PSTR strIcoNumber = (PSTR) VirtualAlloc((LPVOID) NULL, 10, MEM_COMMIT, PAGE_READWRITE);
itoa(icoNumber, strIcoNumber, 10);
string icoPath(folderPath);
icoPath.append(strIcoNumber);
icoPath.append(".ico");
VirtualFree(strIcoNumber, 0, MEM_RELEASE);
PCSTR strIcoPath = icoPath.c_str();
HANDLE ico = CreateFile(strIcoPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (ico == INVALID_HANDLE_VALUE)
{
FreeResource(hIcoData);
nFail++;
return TRUE;
}
DWORD bytesWritten;
BITMAPFILEHEADER bfh;
bfh.bfType = 0x4d42;
bfh.bfSize = icoSize + sizeof(BITMAPFILEHEADER);
bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
bfh.bfReserved1 = bfh.bfReserved2 = 0;
r = WriteFile(ico, &bfh, sizeof(BITMAPFILEHEADER), &bytesWritten, NULL);
if (!r)
{
FreeResource(hIcoData);
CloseHandle(ico);
nFail++;
return TRUE;
}
r = WriteFile(ico, icoData, icoSize, &bytesWritten, NULL);
FreeResource(hIcoData);
CloseHandle(ico);
if (!r)
{
nFail++;
return TRUE;
}
nSucceed++;
return TRUE;
}
void main()
{
cout << "Enter the path where your EXE or DLL is located:" << endl;
string modulePath;
getline(cin, modulePath);
cout << "Now, enter the path of the folder where the .ico files are to be saved:" << endl;
getline(cin, folderPath);
//If the folder path doesn't have a trailing backslash, add one!
if (folderPath.rfind('\') != folderPath.length() - 1)
folderPath.push_back('\');
cout << "Extracting icons..." << endl;
PCSTR strModulePath = modulePath.c_str();
HMODULE hModule = LoadLibraryEx(strModulePath, NULL, LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE | LOAD_LIBRARY_AS_IMAGE_RESOURCE);
if (!hModule)
{
cout << "Unable to load the DLL or EXE." << endl;
getch();
return;
}
BOOL result = EnumResourceNames(hModule, MAKEINTRESOURCE(RT_ICON), (ENUMRESNAMEPROC) EnumIcons, NULL);
FreeLibrary(hModule);
if (!result)
{
cout << "Unable to extract icons." << endl;
getch();
return;
}
cout << nSucceed << " icons were successfully extracted." << endl;
cout << "Failed to extract " << nFail << " icons." << endl;
getch();
}
嗯,我们有四个全局变量。其中之一是 folderPath
。正如我之前提到的,我们从用户那里获取文件夹的路径。此操作在 main()
函数中执行,路径存储在 folderPath
变量中。我将此变量定义为全局变量,因为它可以被 EnumIcons()
函数和 main()
函数访问。
现在,看看 main()
函数。简单易懂。
所以在 main()
函数中,在获取用户的输入后,我们调用 LoadLibraryEx
然后 EnumResourceNames
,传递给它一个指向 EnumIcons
回调函数的指针。 EnumIcons
函数为每个成功保存的图标递增 nSucceed
全局变量,并为每个未能保存的图标或重复的图标递增 nFail
。
现在,看看 EnumIcons
函数,它保存在 EXE 或 DLL 文件中找到的每个非重复图标。这个函数里面的代码很清楚,除了其中一部分检查图标是否重复。
该部分代码以以下注释开头:
//Checking whether the icon is dupplicate
并以以下评论结束:
//End of checking whether the icon is dupplicate
我只是建议你跳过那部分功能,因为它与我的主要问题无关。在 EnumIcons
函数中,我调用了 FindResource
、LoadResource
、LockResource
、SizeofResource
、CreateFile
和 WriteFile
等函数。
只需检查我是否调用了这些函数并正确地进行了错误检查。不要注意检测重复图标的部分。
解决方案:
今天,我使用@Barmak Shemirani 的回答中的信息对我的代码进行了一些更改并解决了问题。我只是想知道 #pragma
指令是什么(在定义 ICONDIR
和 ICONDIRENTRY
结构之前和之后使用的指令)。这是我修改后的代码:
编辑: 代码已更新并做了一些小的改进。
#include <iostream>
#include <conio.h>
#include <Windows.h>
#include <string>
#include <vector>
using namespace std;
//Variables to store the number of
//successfully extracted icons and
//the icons that are dupplicate or
//failed to extract.
UINT nSucceed = 0, nFail = 0;
typedef struct tagHISTOGRAM {
float bucket1; //Range 0-63
float bucket2; //Range 64-127
float bucket3; //Range 128-191
float bucket4; //Range 192-255
} HISTOGRAM, *PHISTOGRAM;
typedef struct tagIOV {
SIZE dimensions; //Width and height of the image
HISTOGRAM hgRed; //Histogram for red channel
HISTOGRAM hgGreen; //Histogram for green channel
HISTOGRAM hgBlue; //Histogram for blue channel
} IMAGE_OVERVIEW, *PIMAGE_OVERVIEW;
#define isInRange(n, s, e) (s <= n) && (n <= e)
//Constant used for the last parameter of CreateIconFromResource
#define ICO_VER 0x00030000
//Vector which stores IMAGE_OVERVIEW stuctures discribing all
//icons processed. When a new icon is being processed, the
//program checks all previous structures in the vector and
//then add the corresponding IMAGE_OVERVIEW structure to the
//vector.
vector<IMAGE_OVERVIEW> theIcons;
#pragma pack(push, 2)
typedef struct
{
BYTE bWidth;
BYTE bHeight;
BYTE bColorCount;
BYTE bReserved;
WORD wPlanes;
WORD wBitCount;
DWORD dwBytesInRes;
DWORD dwImageOffset;
} ICONDIRENTRY, *LPICONDIRENTRY;
typedef struct
{
WORD idReserved;
WORD idType;
WORD idCount;
ICONDIRENTRY idEntries[1];
} ICONDIR, *LPICONDIR;
#pragma pack(pop)
inline void incrementAppropriateBucket(PHISTOGRAM phg, BYTE pixel)
{
if (isInRange(pixel, 0, 63))
phg->bucket1++;
else if (isInRange(pixel, 64, 127))
phg->bucket2++;
else if (isInRange(pixel, 128, 191))
phg->bucket3++;
else if (isInRange(pixel, 192, 255))
phg->bucket4++;
}
//The following function divides each bucket total by the number
//of pixels in the entire image.
inline void finalizeHistogram(PHISTOGRAM phg, DWORD nPixels)
{
phg->bucket1 /= nPixels;
phg->bucket2 /= nPixels;
phg->bucket3 /= nPixels;
phg->bucket4 /= nPixels;
}
BOOL createImageOverview(HBITMAP hBmp, PIMAGE_OVERVIEW iov)
{
BITMAPINFO bmpInfo = {};
bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
HDC DC = CreateCompatibleDC(NULL);
SelectObject(DC, hBmp);
if (!GetDIBits(DC, hBmp, 0, 0, NULL, &bmpInfo, DIB_RGB_COLORS))
{
DeleteDC(DC);
return FALSE;
}
bmpInfo.bmiHeader.biBitCount = 32;
bmpInfo.bmiHeader.biCompression = BI_RGB;
bmpInfo.bmiHeader.biHeight = abs(bmpInfo.bmiHeader.biHeight);
iov->dimensions.cx = bmpInfo.bmiHeader.biWidth;
iov->dimensions.cy = bmpInfo.bmiHeader.biHeight;
BYTE* pixels = new BYTE[bmpInfo.bmiHeader.biSizeImage];
if (!GetDIBits(DC, hBmp, 0, bmpInfo.bmiHeader.biHeight, (PVOID) pixels, &bmpInfo, DIB_RGB_COLORS))
{
delete[] pixels;
DeleteDC(DC);
return FALSE;
}
for (LONG Y = bmpInfo.bmiHeader.biHeight - 1;Y >= 0;Y--)
for (LONG X = 0;X < bmpInfo.bmiHeader.biWidth;X++)
{
BYTE R = pixels[(bmpInfo.bmiHeader.biWidth * Y + X) * 4 + 2];
incrementAppropriateBucket(&(iov->hgRed), R);
BYTE G = pixels[(bmpInfo.bmiHeader.biWidth * Y + X) * 4 + 1];
incrementAppropriateBucket(&(iov->hgGreen), G);
BYTE B = pixels[(bmpInfo.bmiHeader.biWidth * Y + X) * 4];
incrementAppropriateBucket(&(iov->hgBlue), B);
}
DWORD nPixels = bmpInfo.bmiHeader.biWidth * bmpInfo.bmiHeader.biHeight;
finalizeHistogram(&(iov->hgRed), nPixels);
finalizeHistogram(&(iov->hgGreen), nPixels);
finalizeHistogram(&(iov->hgBlue), nPixels);
delete[] pixels;
DeleteDC(DC);
return TRUE;
}
float sumUpBucketDifferances(const HISTOGRAM hg1, const HISTOGRAM hg2)
{
float result = 0;
result += abs(hg1.bucket1 - hg2.bucket1);
result += abs(hg1.bucket2 - hg2.bucket2);
result += abs(hg1.bucket3 - hg2.bucket3);
result += abs(hg1.bucket4 - hg2.bucket4);
return result;
}
float compareImages(const IMAGE_OVERVIEW iov1, const IMAGE_OVERVIEW iov2)
{
float result = 0;
result += sumUpBucketDifferances(iov1.hgRed, iov2.hgRed);
result += sumUpBucketDifferances(iov1.hgGreen, iov2.hgGreen);
result += sumUpBucketDifferances(iov1.hgBlue, iov2.hgBlue);
return result;
}
BOOL isDuplicate(const IMAGE_OVERVIEW iov)
{
size_t s = theIcons.size();
for (size_t i = 0;i < s;i++)
{
if ((theIcons[i].dimensions.cx != iov.dimensions.cx) ||
(theIcons[i].dimensions.cy != iov.dimensions.cy))
continue;
if (compareImages(theIcons[i], iov) < 0.1)
return TRUE;
}
return FALSE;
}
BOOL CALLBACK EnumIcons(HMODULE hModule, LPCTSTR lpszType, LPTSTR lpszName, LONG_PTR lParam)
{
if (!lParam)
return FALSE;
string folderPath = (char *) lParam;
HRSRC hRes = FindResource(hModule, lpszName, lpszType);
if (!hRes)
{
nFail++;
return TRUE;
}
DWORD icoSize = SizeofResource(hModule, hRes);
if (!icoSize)
{
nFail++;
return TRUE;
}
HGLOBAL hIcoData = LoadResource(hModule, hRes);
if (!hIcoData)
{
nFail++;
return TRUE;
}
BYTE* icoData = (BYTE *) LockResource(hIcoData);
if (!icoData)
{
FreeResource(hIcoData);
nFail++;
return TRUE;
}
HICON hIcon = CreateIconFromResource(icoData, icoSize, TRUE, ICO_VER);
if (!hIcon)
{
FreeResource(hIcoData);
nFail++;
return TRUE;
}
ICONINFOEX ii = {sizeof(ICONINFOEX)};
if (!GetIconInfoEx(hIcon, &ii))
{
DestroyIcon(hIcon);
FreeResource(hIcoData);
nFail++;
return TRUE;
}
BITMAP bmp;
if (!GetObject(ii.hbmColor, sizeof(BITMAP), &bmp))
{
DeleteObject(ii.hbmColor);
DeleteObject(ii.hbmMask);
DestroyIcon(hIcon);
FreeResource(hIcoData);
nFail++;
return TRUE;
}
ICONDIR icoDir = {0};
icoDir.idType = 1;
icoDir.idCount = 1;
icoDir.idEntries[0].bWidth = (BYTE) bmp.bmWidth;
icoDir.idEntries[0].bHeight = (BYTE) bmp.bmHeight;
icoDir.idEntries[0].wPlanes = bmp.bmPlanes;
icoDir.idEntries[0].wBitCount = bmp.bmBitsPixel;
icoDir.idEntries[0].dwBytesInRes = icoSize;
icoDir.idEntries[0].dwImageOffset = sizeof(ICONDIR);
//Checking whether the icon is duplicate
IMAGE_OVERVIEW iov;
ZeroMemory(&iov, sizeof(IMAGE_OVERVIEW));
BOOL r = createImageOverview(ii.hbmColor, &iov);
DeleteObject(ii.hbmColor);
DeleteObject(ii.hbmMask);
DestroyIcon(hIcon);
if (!r)
{
FreeResource(hIcoData);
nFail++;
return TRUE;
}
if (isDuplicate(iov))
{
FreeResource(hIcoData);
nFail++;
return TRUE;
}
theIcons.push_back(iov);
//End of checking whether the icon is dupplicate
//Note that the first icon is saved as 1.ico,
//the second is saved as 2.ico, and so on. So
//the number of the current icon is equal to
//the number of the previously saved icons + 1
int icoNumber = nSucceed + 1;
char* strIcoNumber = (char *) malloc(10);
itoa(icoNumber, strIcoNumber, 10);
string icoPath(folderPath);
icoPath.append(strIcoNumber);
icoPath.append(".ico");
HANDLE ico = CreateFile(icoPath.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (ico == INVALID_HANDLE_VALUE)
{
FreeResource(hIcoData);
nFail++;
return TRUE;
}
DWORD bytesWritten;
r = WriteFile(ico, &icoDir, sizeof(ICONDIR), &bytesWritten, NULL);
if (!r)
{
FreeResource(hIcoData);
CloseHandle(ico);
nFail++;
return TRUE;
}
r = WriteFile(ico, icoData, icoSize, &bytesWritten, NULL);
FreeResource(hIcoData);
CloseHandle(ico);
if (!r)
{
nFail++;
return TRUE;
}
nSucceed++;
return TRUE;
}
void main()
{
cout << "Enter the path where your EXE or DLL is located:" << endl;
string modulePath;
getline(cin, modulePath);
cout << "Now, enter the path of the folder where the .ico files are to be saved:" << endl;
string folderPath;
getline(cin, folderPath);
//If the folder path doesn't have a trailing backslash, add one!
if (folderPath.rfind('\') != folderPath.length() - 1)
folderPath.push_back('\');
cout << "Extracting icons..." << endl;
HMODULE hModule = LoadLibraryEx(modulePath.c_str(), NULL, LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE | LOAD_LIBRARY_AS_IMAGE_RESOURCE);
if (!hModule)
{
cout << "Unable to load the DLL or EXE." << endl;
getch();
return;
}
BOOL result = EnumResourceNames(hModule, MAKEINTRESOURCE(RT_ICON), (ENUMRESNAMEPROC) EnumIcons, (LONG_PTR) (folderPath.c_str()));
FreeLibrary(hModule);
if (!result)
{
cout << "Unable to extract icons." << endl;
getch();
return;
}
cout << nSucceed << " icons were successfully extracted." << endl;
cout << nFail << " icons were dupplicates or could not be extracted." << endl;
getch();
}
图标文件应以图标 header ICONDIR
开头,而不是 BITMAPFILEHEADER
。参见 Icon reference
从 FindResource
获得的字节可能包含 PNG 数据,而不是位图。最好避免在图标和位图之间进行转换。
初始化并写入图标header后,可以直接写入从FindResource
获取的字节,避免任何转换。
EnumResourceNames(... RT_ICON ...)
将单独枚举图标,因此每个目标文件包含一个图标。
EnumResourceNames(... RT_GROUP_ICON ...)
将枚举图标组ps。您可以浏览组ps 并将所有图标添加到一个文件中。
不清楚您查找重复项的标准是什么。使用 RT_ICON
枚举,你会得到不同大小和颜色深度的相同图标,它们不是完全相同的。您可以选择特定尺寸,例如 32x32 尺寸。要找到精确的重复项,您可以计算资源中字节的哈希值,然后比较哈希值。
#include <string>
#include <fstream>
#include <Windows.h>
#pragma pack(push, 2)
typedef struct
{
BYTE bWidth;
BYTE bHeight;
BYTE bColorCount;
BYTE bReserved;
WORD wPlanes;
WORD wBitCount;
DWORD dwBytesInRes;
DWORD dwImageOffset;
} ICONDIRENTRY, *LPICONDIRENTRY;
typedef struct
{
WORD idReserved;
WORD idType;
WORD idCount;
ICONDIRENTRY idEntries[1];
} ICONDIR, *LPICONDIR;
#pragma pack(pop)
BOOL CALLBACK EnumIcons(HMODULE hmodule, LPCTSTR type, LPTSTR lpszName, LONG_PTR ptr)
{
if(!ptr)
return FALSE;
std::string path = (const char*)ptr;
HRSRC hRes = FindResource(hmodule, lpszName, type);
if(!hRes)
return TRUE;
DWORD size = SizeofResource(hmodule, hRes);
HGLOBAL hg = LoadResource(hmodule, hRes);
BYTE* bytes = (BYTE*)LockResource(hg);
HICON hicon = CreateIconFromResource(bytes, size, TRUE, 0x00030000);
if(!hicon)
return TRUE;
if (IS_INTRESOURCE(lpszName))
path += std::to_string((int)lpszName);
else
path += lpszName;
path += std::string(".ico");
std::ofstream fout(path, std::ios::binary);
if(!fout)
return TRUE;
ICONINFOEX ii = { sizeof(ii) };
GetIconInfoEx(hicon, &ii);
ICONDIR hdr = { 0 };
hdr.idType = 1;
hdr.idCount = 1;
BITMAP bm;
GetObject(ii.hbmColor, sizeof(bm), &bm);
hdr.idEntries[0].bWidth = (BYTE)bm.bmWidth;
hdr.idEntries[0].bHeight = (BYTE)bm.bmHeight;
hdr.idEntries[0].wPlanes = bm.bmPlanes;
hdr.idEntries[0].wBitCount = bm.bmBitsPixel;
hdr.idEntries[0].dwBytesInRes = size;
hdr.idEntries[0].dwImageOffset = sizeof(ICONDIR);
fout.write((char*)&hdr, hdr.idEntries[0].dwImageOffset);
fout.write((char*)bytes, size);
DestroyIcon(hicon);
return TRUE;
}
int main()
{
std::string modulepath = "file.exe";
const char *dir = "c:\test\";
HMODULE hmodule = LoadLibraryEx(modulepath.c_str(), NULL,
LOAD_LIBRARY_AS_IMAGE_RESOURCE);
if(hmodule)
EnumResourceNames(hmodule, RT_ICON, (ENUMRESNAMEPROC)EnumIcons, (LONG_PTR)dir);
return 0;
}
ps,不需要VirtualAlloc
为自己的进程分配内存。对于 C++,您可以使用 malloc
或 new
。更好的是,使用 std::string
及其方法。
最近做了一个程序,它加载一个可执行文件或DLL文件,枚举其中的所有图标资源,然后将每个图标资源保存为一个文件夹中的.ico文件。但它使用直方图(如 this post 中讨论的那样)来避免保存重复图标(看起来相同的图标)。
该程序首先从用户那里获取两条路径(用户必须在控制台中键入它们 window)。其中一个是exe或dll文件的路径,另一个是.ico文件所在文件夹的路径。
然后程序通过调用 LoadLibraryEx
加载 exe 或 dll 文件,并通过调用 EnumResourceNames
.
枚举图标资源
传递给 EnumResourceNames
的回调函数将每个非重复图标资源保存为具有唯一名称的 .ico 文件。保存文件的名称是这样的:1.ico
、2.ico
、3.ico
、4.ico
、.etc
我的完整代码和我的问题
问题是我的程序创建了损坏的或未知的 .ico 文件,而不是真实的。
我的意思是,有些保存的图标有奇怪的图像,有些根本看不到!尽管我使用 WriteFile
函数为每个 .ico 文件编写了 BITMAPFILEHEADER
结构。
只需编译 运行 我的代码,然后在控制台 window 中键入以下内容:
C:\Windows\System32\shell32.dll
然后键入要保存 .ico 文件的现有文件夹的路径。
接下来,等待至少 4 到 7 秒,直到操作完成。
然后打开那个文件夹,然后看到图标!他们太可笑了!
以下是我的代码。抱歉,如果注释中或变量和函数名称中存在拼写错误。反正只有main()
和EnumIcons()
两个函数重要,一定要看代码下面的解释。
#include <iostream>
#include <conio.h>
#include <Windows.h>
#include <string>
#include <vector>
using namespace std;
string folderPath;
//Variables to store the number of
//successfully extracted icons.
UINT nSucceed = 0, nFail = 0;
typedef struct tagHISTOGRAM {
float bucket1; //Range 0-63
float bucket2; //Range 64-127
float bucket3; //Range 128-191
float bucket4; //Range 192-255
} HISTOGRAM, *PHISTOGRAM;
typedef struct tagIOV {
SIZE dimensions; //Width and height of the image
HISTOGRAM hgRed; //Histogram for red channel
HISTOGRAM hgGreen; //Histogram for green channel
HISTOGRAM hgBlue; //Histogram for blue channel
} IMAGE_OVERVIEW, *PIMAGE_OVERVIEW;
#define isInRange(n, s, e) (s <= n) && (n <= e)
//Constant used for the last parameter of CreateIconFromResource
#define ICO_VER 0x00030000
//Vector which stores IMAGE_OVERVIEW stuctures discribing all
//icons processed. When a new icon is being processed, the
//program checks all previous structures in the vector and
//then add the corresponding IMAGE_OVERVIEW structure to the
//vector.
vector<IMAGE_OVERVIEW> theIcons;
inline void incrementAppropriateBucket(PHISTOGRAM phg, BYTE pixel)
{
if (isInRange(pixel, 0, 63))
phg->bucket1++;
else if (isInRange(pixel, 64, 127))
phg->bucket2++;
else if (isInRange(pixel, 128, 191))
phg->bucket3++;
else if (isInRange(pixel, 192, 255))
phg->bucket4++;
}
//The following function divides each bucket total by the number
//of pixels in the entire image.
inline void finalizeHistogram(PHISTOGRAM phg, DWORD nPixels)
{
phg->bucket1 /= nPixels;
phg->bucket2 /= nPixels;
phg->bucket3 /= nPixels;
phg->bucket4 /= nPixels;
}
BOOL createImageOverview(HBITMAP hBmp, PIMAGE_OVERVIEW iov)
{
BITMAPINFO bmpInfo = {};
bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
HDC DC = CreateCompatibleDC(NULL);
SelectObject(DC, hBmp);
if (!GetDIBits(DC, hBmp, 0, 0, NULL, &bmpInfo, DIB_RGB_COLORS))
{
ReleaseDC(NULL, DC);
DeleteDC(DC);
return FALSE;
}
bmpInfo.bmiHeader.biBitCount = 32;
bmpInfo.bmiHeader.biCompression = BI_RGB;
bmpInfo.bmiHeader.biHeight = abs(bmpInfo.bmiHeader.biHeight);
iov->dimensions.cx = bmpInfo.bmiHeader.biWidth;
iov->dimensions.cy = bmpInfo.bmiHeader.biHeight;
BYTE* pixels = new BYTE[bmpInfo.bmiHeader.biSizeImage];
if (!GetDIBits(DC, hBmp, 0, bmpInfo.bmiHeader.biHeight, (PVOID) pixels, &bmpInfo, DIB_RGB_COLORS))
{
delete[] pixels;
ReleaseDC(NULL, DC);
DeleteDC(DC);
return FALSE;
}
for (LONG Y = bmpInfo.bmiHeader.biHeight - 1;Y >= 0;Y--)
for (LONG X = 0;X < bmpInfo.bmiHeader.biWidth;X++)
{
BYTE R = pixels[(bmpInfo.bmiHeader.biWidth * Y + X) * 4 + 2];
incrementAppropriateBucket(&(iov->hgRed), R);
BYTE G = pixels[(bmpInfo.bmiHeader.biWidth * Y + X) * 4 + 1];
incrementAppropriateBucket(&(iov->hgGreen), G);
BYTE B = pixels[(bmpInfo.bmiHeader.biWidth * Y + X) * 4];
incrementAppropriateBucket(&(iov->hgBlue), B);
}
DWORD nPixels = bmpInfo.bmiHeader.biWidth * bmpInfo.bmiHeader.biHeight;
finalizeHistogram(&(iov->hgRed), nPixels);
finalizeHistogram(&(iov->hgGreen), nPixels);
finalizeHistogram(&(iov->hgBlue), nPixels);
delete[] pixels;
ReleaseDC(NULL, DC);
DeleteDC(DC);
return TRUE;
}
float sumUpBucketDifferances(const HISTOGRAM hg1, const HISTOGRAM hg2)
{
float result = 0;
result += abs(hg1.bucket1 - hg2.bucket1);
result += abs(hg1.bucket2 - hg2.bucket2);
result += abs(hg1.bucket3 - hg2.bucket3);
result += abs(hg1.bucket4 - hg2.bucket4);
return result;
}
float compareImages(const IMAGE_OVERVIEW iov1, const IMAGE_OVERVIEW iov2)
{
float result = 0;
result += sumUpBucketDifferances(iov1.hgRed, iov2.hgRed);
result += sumUpBucketDifferances(iov1.hgGreen, iov2.hgGreen);
result += sumUpBucketDifferances(iov1.hgBlue, iov2.hgBlue);
return result;
}
BOOL isDuplicate(const IMAGE_OVERVIEW iov)
{
size_t s = theIcons.size();
for (size_t i = 0;i < s;i++)
{
if ((theIcons[i].dimensions.cx != iov.dimensions.cx) ||
(theIcons[i].dimensions.cy != iov.dimensions.cy))
continue;
if (compareImages(theIcons[i], iov) < 0.1)
return TRUE;
}
return FALSE;
}
BOOL CALLBACK EnumIcons(HMODULE hModule, LPCTSTR lpszType, LPTSTR lpszName, LONG_PTR lParam)
{
HRSRC hRes = FindResource(hModule, lpszName, lpszType);
if (!hRes)
{
nFail++;
return TRUE;
}
DWORD icoSize = SizeofResource(hModule, hRes);
if (!icoSize)
{
nFail++;
return TRUE;
}
HGLOBAL hIcoData = LoadResource(hModule, hRes);
if (!hIcoData)
{
nFail++;
return TRUE;
}
BYTE* icoData = (BYTE *) LockResource(hIcoData);
if (!icoData)
{
FreeResource(hIcoData);
nFail++;
return TRUE;
}
//Checking whether the icon is dupplicate
HICON hIcon = CreateIconFromResource(icoData, icoSize, TRUE, ICO_VER);
if (!hIcon)
{
FreeResource(hIcoData);
nFail++;
return TRUE;
}
ICONINFO ii;
if (!GetIconInfo(hIcon, &ii))
{
DestroyIcon(hIcon);
FreeResource(hIcoData);
nFail++;
return TRUE;
}
IMAGE_OVERVIEW iov;
ZeroMemory(&iov, sizeof(IMAGE_OVERVIEW));
BOOL r = createImageOverview(ii.hbmColor, &iov);
DeleteObject(ii.hbmColor);
DeleteObject(ii.hbmMask);
DestroyIcon(hIcon);
if (!r)
{
FreeResource(hIcoData);
nFail++;
return TRUE;
}
if (isDuplicate(iov))
{
FreeResource(hIcoData);
nFail++;
return TRUE;
}
theIcons.push_back(iov);
//End of checking whether the icon is dupplicate
//Note that the first icon is saved as 1.ico,
//the second is saved as 2.ico, and so on. So
//the number of the current icon is equal to
//the number of the previously saved icons + 1
int icoNumber = nSucceed + 1;
PSTR strIcoNumber = (PSTR) VirtualAlloc((LPVOID) NULL, 10, MEM_COMMIT, PAGE_READWRITE);
itoa(icoNumber, strIcoNumber, 10);
string icoPath(folderPath);
icoPath.append(strIcoNumber);
icoPath.append(".ico");
VirtualFree(strIcoNumber, 0, MEM_RELEASE);
PCSTR strIcoPath = icoPath.c_str();
HANDLE ico = CreateFile(strIcoPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (ico == INVALID_HANDLE_VALUE)
{
FreeResource(hIcoData);
nFail++;
return TRUE;
}
DWORD bytesWritten;
BITMAPFILEHEADER bfh;
bfh.bfType = 0x4d42;
bfh.bfSize = icoSize + sizeof(BITMAPFILEHEADER);
bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
bfh.bfReserved1 = bfh.bfReserved2 = 0;
r = WriteFile(ico, &bfh, sizeof(BITMAPFILEHEADER), &bytesWritten, NULL);
if (!r)
{
FreeResource(hIcoData);
CloseHandle(ico);
nFail++;
return TRUE;
}
r = WriteFile(ico, icoData, icoSize, &bytesWritten, NULL);
FreeResource(hIcoData);
CloseHandle(ico);
if (!r)
{
nFail++;
return TRUE;
}
nSucceed++;
return TRUE;
}
void main()
{
cout << "Enter the path where your EXE or DLL is located:" << endl;
string modulePath;
getline(cin, modulePath);
cout << "Now, enter the path of the folder where the .ico files are to be saved:" << endl;
getline(cin, folderPath);
//If the folder path doesn't have a trailing backslash, add one!
if (folderPath.rfind('\') != folderPath.length() - 1)
folderPath.push_back('\');
cout << "Extracting icons..." << endl;
PCSTR strModulePath = modulePath.c_str();
HMODULE hModule = LoadLibraryEx(strModulePath, NULL, LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE | LOAD_LIBRARY_AS_IMAGE_RESOURCE);
if (!hModule)
{
cout << "Unable to load the DLL or EXE." << endl;
getch();
return;
}
BOOL result = EnumResourceNames(hModule, MAKEINTRESOURCE(RT_ICON), (ENUMRESNAMEPROC) EnumIcons, NULL);
FreeLibrary(hModule);
if (!result)
{
cout << "Unable to extract icons." << endl;
getch();
return;
}
cout << nSucceed << " icons were successfully extracted." << endl;
cout << "Failed to extract " << nFail << " icons." << endl;
getch();
}
嗯,我们有四个全局变量。其中之一是 folderPath
。正如我之前提到的,我们从用户那里获取文件夹的路径。此操作在 main()
函数中执行,路径存储在 folderPath
变量中。我将此变量定义为全局变量,因为它可以被 EnumIcons()
函数和 main()
函数访问。
现在,看看 main()
函数。简单易懂。
所以在 main()
函数中,在获取用户的输入后,我们调用 LoadLibraryEx
然后 EnumResourceNames
,传递给它一个指向 EnumIcons
回调函数的指针。 EnumIcons
函数为每个成功保存的图标递增 nSucceed
全局变量,并为每个未能保存的图标或重复的图标递增 nFail
。
现在,看看 EnumIcons
函数,它保存在 EXE 或 DLL 文件中找到的每个非重复图标。这个函数里面的代码很清楚,除了其中一部分检查图标是否重复。
该部分代码以以下注释开头:
//Checking whether the icon is dupplicate
并以以下评论结束:
//End of checking whether the icon is dupplicate
我只是建议你跳过那部分功能,因为它与我的主要问题无关。在 EnumIcons
函数中,我调用了 FindResource
、LoadResource
、LockResource
、SizeofResource
、CreateFile
和 WriteFile
等函数。
只需检查我是否调用了这些函数并正确地进行了错误检查。不要注意检测重复图标的部分。
解决方案:
今天,我使用@Barmak Shemirani 的回答中的信息对我的代码进行了一些更改并解决了问题。我只是想知道 #pragma
指令是什么(在定义 ICONDIR
和 ICONDIRENTRY
结构之前和之后使用的指令)。这是我修改后的代码:
编辑: 代码已更新并做了一些小的改进。
#include <iostream>
#include <conio.h>
#include <Windows.h>
#include <string>
#include <vector>
using namespace std;
//Variables to store the number of
//successfully extracted icons and
//the icons that are dupplicate or
//failed to extract.
UINT nSucceed = 0, nFail = 0;
typedef struct tagHISTOGRAM {
float bucket1; //Range 0-63
float bucket2; //Range 64-127
float bucket3; //Range 128-191
float bucket4; //Range 192-255
} HISTOGRAM, *PHISTOGRAM;
typedef struct tagIOV {
SIZE dimensions; //Width and height of the image
HISTOGRAM hgRed; //Histogram for red channel
HISTOGRAM hgGreen; //Histogram for green channel
HISTOGRAM hgBlue; //Histogram for blue channel
} IMAGE_OVERVIEW, *PIMAGE_OVERVIEW;
#define isInRange(n, s, e) (s <= n) && (n <= e)
//Constant used for the last parameter of CreateIconFromResource
#define ICO_VER 0x00030000
//Vector which stores IMAGE_OVERVIEW stuctures discribing all
//icons processed. When a new icon is being processed, the
//program checks all previous structures in the vector and
//then add the corresponding IMAGE_OVERVIEW structure to the
//vector.
vector<IMAGE_OVERVIEW> theIcons;
#pragma pack(push, 2)
typedef struct
{
BYTE bWidth;
BYTE bHeight;
BYTE bColorCount;
BYTE bReserved;
WORD wPlanes;
WORD wBitCount;
DWORD dwBytesInRes;
DWORD dwImageOffset;
} ICONDIRENTRY, *LPICONDIRENTRY;
typedef struct
{
WORD idReserved;
WORD idType;
WORD idCount;
ICONDIRENTRY idEntries[1];
} ICONDIR, *LPICONDIR;
#pragma pack(pop)
inline void incrementAppropriateBucket(PHISTOGRAM phg, BYTE pixel)
{
if (isInRange(pixel, 0, 63))
phg->bucket1++;
else if (isInRange(pixel, 64, 127))
phg->bucket2++;
else if (isInRange(pixel, 128, 191))
phg->bucket3++;
else if (isInRange(pixel, 192, 255))
phg->bucket4++;
}
//The following function divides each bucket total by the number
//of pixels in the entire image.
inline void finalizeHistogram(PHISTOGRAM phg, DWORD nPixels)
{
phg->bucket1 /= nPixels;
phg->bucket2 /= nPixels;
phg->bucket3 /= nPixels;
phg->bucket4 /= nPixels;
}
BOOL createImageOverview(HBITMAP hBmp, PIMAGE_OVERVIEW iov)
{
BITMAPINFO bmpInfo = {};
bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
HDC DC = CreateCompatibleDC(NULL);
SelectObject(DC, hBmp);
if (!GetDIBits(DC, hBmp, 0, 0, NULL, &bmpInfo, DIB_RGB_COLORS))
{
DeleteDC(DC);
return FALSE;
}
bmpInfo.bmiHeader.biBitCount = 32;
bmpInfo.bmiHeader.biCompression = BI_RGB;
bmpInfo.bmiHeader.biHeight = abs(bmpInfo.bmiHeader.biHeight);
iov->dimensions.cx = bmpInfo.bmiHeader.biWidth;
iov->dimensions.cy = bmpInfo.bmiHeader.biHeight;
BYTE* pixels = new BYTE[bmpInfo.bmiHeader.biSizeImage];
if (!GetDIBits(DC, hBmp, 0, bmpInfo.bmiHeader.biHeight, (PVOID) pixels, &bmpInfo, DIB_RGB_COLORS))
{
delete[] pixels;
DeleteDC(DC);
return FALSE;
}
for (LONG Y = bmpInfo.bmiHeader.biHeight - 1;Y >= 0;Y--)
for (LONG X = 0;X < bmpInfo.bmiHeader.biWidth;X++)
{
BYTE R = pixels[(bmpInfo.bmiHeader.biWidth * Y + X) * 4 + 2];
incrementAppropriateBucket(&(iov->hgRed), R);
BYTE G = pixels[(bmpInfo.bmiHeader.biWidth * Y + X) * 4 + 1];
incrementAppropriateBucket(&(iov->hgGreen), G);
BYTE B = pixels[(bmpInfo.bmiHeader.biWidth * Y + X) * 4];
incrementAppropriateBucket(&(iov->hgBlue), B);
}
DWORD nPixels = bmpInfo.bmiHeader.biWidth * bmpInfo.bmiHeader.biHeight;
finalizeHistogram(&(iov->hgRed), nPixels);
finalizeHistogram(&(iov->hgGreen), nPixels);
finalizeHistogram(&(iov->hgBlue), nPixels);
delete[] pixels;
DeleteDC(DC);
return TRUE;
}
float sumUpBucketDifferances(const HISTOGRAM hg1, const HISTOGRAM hg2)
{
float result = 0;
result += abs(hg1.bucket1 - hg2.bucket1);
result += abs(hg1.bucket2 - hg2.bucket2);
result += abs(hg1.bucket3 - hg2.bucket3);
result += abs(hg1.bucket4 - hg2.bucket4);
return result;
}
float compareImages(const IMAGE_OVERVIEW iov1, const IMAGE_OVERVIEW iov2)
{
float result = 0;
result += sumUpBucketDifferances(iov1.hgRed, iov2.hgRed);
result += sumUpBucketDifferances(iov1.hgGreen, iov2.hgGreen);
result += sumUpBucketDifferances(iov1.hgBlue, iov2.hgBlue);
return result;
}
BOOL isDuplicate(const IMAGE_OVERVIEW iov)
{
size_t s = theIcons.size();
for (size_t i = 0;i < s;i++)
{
if ((theIcons[i].dimensions.cx != iov.dimensions.cx) ||
(theIcons[i].dimensions.cy != iov.dimensions.cy))
continue;
if (compareImages(theIcons[i], iov) < 0.1)
return TRUE;
}
return FALSE;
}
BOOL CALLBACK EnumIcons(HMODULE hModule, LPCTSTR lpszType, LPTSTR lpszName, LONG_PTR lParam)
{
if (!lParam)
return FALSE;
string folderPath = (char *) lParam;
HRSRC hRes = FindResource(hModule, lpszName, lpszType);
if (!hRes)
{
nFail++;
return TRUE;
}
DWORD icoSize = SizeofResource(hModule, hRes);
if (!icoSize)
{
nFail++;
return TRUE;
}
HGLOBAL hIcoData = LoadResource(hModule, hRes);
if (!hIcoData)
{
nFail++;
return TRUE;
}
BYTE* icoData = (BYTE *) LockResource(hIcoData);
if (!icoData)
{
FreeResource(hIcoData);
nFail++;
return TRUE;
}
HICON hIcon = CreateIconFromResource(icoData, icoSize, TRUE, ICO_VER);
if (!hIcon)
{
FreeResource(hIcoData);
nFail++;
return TRUE;
}
ICONINFOEX ii = {sizeof(ICONINFOEX)};
if (!GetIconInfoEx(hIcon, &ii))
{
DestroyIcon(hIcon);
FreeResource(hIcoData);
nFail++;
return TRUE;
}
BITMAP bmp;
if (!GetObject(ii.hbmColor, sizeof(BITMAP), &bmp))
{
DeleteObject(ii.hbmColor);
DeleteObject(ii.hbmMask);
DestroyIcon(hIcon);
FreeResource(hIcoData);
nFail++;
return TRUE;
}
ICONDIR icoDir = {0};
icoDir.idType = 1;
icoDir.idCount = 1;
icoDir.idEntries[0].bWidth = (BYTE) bmp.bmWidth;
icoDir.idEntries[0].bHeight = (BYTE) bmp.bmHeight;
icoDir.idEntries[0].wPlanes = bmp.bmPlanes;
icoDir.idEntries[0].wBitCount = bmp.bmBitsPixel;
icoDir.idEntries[0].dwBytesInRes = icoSize;
icoDir.idEntries[0].dwImageOffset = sizeof(ICONDIR);
//Checking whether the icon is duplicate
IMAGE_OVERVIEW iov;
ZeroMemory(&iov, sizeof(IMAGE_OVERVIEW));
BOOL r = createImageOverview(ii.hbmColor, &iov);
DeleteObject(ii.hbmColor);
DeleteObject(ii.hbmMask);
DestroyIcon(hIcon);
if (!r)
{
FreeResource(hIcoData);
nFail++;
return TRUE;
}
if (isDuplicate(iov))
{
FreeResource(hIcoData);
nFail++;
return TRUE;
}
theIcons.push_back(iov);
//End of checking whether the icon is dupplicate
//Note that the first icon is saved as 1.ico,
//the second is saved as 2.ico, and so on. So
//the number of the current icon is equal to
//the number of the previously saved icons + 1
int icoNumber = nSucceed + 1;
char* strIcoNumber = (char *) malloc(10);
itoa(icoNumber, strIcoNumber, 10);
string icoPath(folderPath);
icoPath.append(strIcoNumber);
icoPath.append(".ico");
HANDLE ico = CreateFile(icoPath.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (ico == INVALID_HANDLE_VALUE)
{
FreeResource(hIcoData);
nFail++;
return TRUE;
}
DWORD bytesWritten;
r = WriteFile(ico, &icoDir, sizeof(ICONDIR), &bytesWritten, NULL);
if (!r)
{
FreeResource(hIcoData);
CloseHandle(ico);
nFail++;
return TRUE;
}
r = WriteFile(ico, icoData, icoSize, &bytesWritten, NULL);
FreeResource(hIcoData);
CloseHandle(ico);
if (!r)
{
nFail++;
return TRUE;
}
nSucceed++;
return TRUE;
}
void main()
{
cout << "Enter the path where your EXE or DLL is located:" << endl;
string modulePath;
getline(cin, modulePath);
cout << "Now, enter the path of the folder where the .ico files are to be saved:" << endl;
string folderPath;
getline(cin, folderPath);
//If the folder path doesn't have a trailing backslash, add one!
if (folderPath.rfind('\') != folderPath.length() - 1)
folderPath.push_back('\');
cout << "Extracting icons..." << endl;
HMODULE hModule = LoadLibraryEx(modulePath.c_str(), NULL, LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE | LOAD_LIBRARY_AS_IMAGE_RESOURCE);
if (!hModule)
{
cout << "Unable to load the DLL or EXE." << endl;
getch();
return;
}
BOOL result = EnumResourceNames(hModule, MAKEINTRESOURCE(RT_ICON), (ENUMRESNAMEPROC) EnumIcons, (LONG_PTR) (folderPath.c_str()));
FreeLibrary(hModule);
if (!result)
{
cout << "Unable to extract icons." << endl;
getch();
return;
}
cout << nSucceed << " icons were successfully extracted." << endl;
cout << nFail << " icons were dupplicates or could not be extracted." << endl;
getch();
}
图标文件应以图标 header ICONDIR
开头,而不是 BITMAPFILEHEADER
。参见 Icon reference
从 FindResource
获得的字节可能包含 PNG 数据,而不是位图。最好避免在图标和位图之间进行转换。
初始化并写入图标header后,可以直接写入从FindResource
获取的字节,避免任何转换。
EnumResourceNames(... RT_ICON ...)
将单独枚举图标,因此每个目标文件包含一个图标。
EnumResourceNames(... RT_GROUP_ICON ...)
将枚举图标组ps。您可以浏览组ps 并将所有图标添加到一个文件中。
不清楚您查找重复项的标准是什么。使用 RT_ICON
枚举,你会得到不同大小和颜色深度的相同图标,它们不是完全相同的。您可以选择特定尺寸,例如 32x32 尺寸。要找到精确的重复项,您可以计算资源中字节的哈希值,然后比较哈希值。
#include <string>
#include <fstream>
#include <Windows.h>
#pragma pack(push, 2)
typedef struct
{
BYTE bWidth;
BYTE bHeight;
BYTE bColorCount;
BYTE bReserved;
WORD wPlanes;
WORD wBitCount;
DWORD dwBytesInRes;
DWORD dwImageOffset;
} ICONDIRENTRY, *LPICONDIRENTRY;
typedef struct
{
WORD idReserved;
WORD idType;
WORD idCount;
ICONDIRENTRY idEntries[1];
} ICONDIR, *LPICONDIR;
#pragma pack(pop)
BOOL CALLBACK EnumIcons(HMODULE hmodule, LPCTSTR type, LPTSTR lpszName, LONG_PTR ptr)
{
if(!ptr)
return FALSE;
std::string path = (const char*)ptr;
HRSRC hRes = FindResource(hmodule, lpszName, type);
if(!hRes)
return TRUE;
DWORD size = SizeofResource(hmodule, hRes);
HGLOBAL hg = LoadResource(hmodule, hRes);
BYTE* bytes = (BYTE*)LockResource(hg);
HICON hicon = CreateIconFromResource(bytes, size, TRUE, 0x00030000);
if(!hicon)
return TRUE;
if (IS_INTRESOURCE(lpszName))
path += std::to_string((int)lpszName);
else
path += lpszName;
path += std::string(".ico");
std::ofstream fout(path, std::ios::binary);
if(!fout)
return TRUE;
ICONINFOEX ii = { sizeof(ii) };
GetIconInfoEx(hicon, &ii);
ICONDIR hdr = { 0 };
hdr.idType = 1;
hdr.idCount = 1;
BITMAP bm;
GetObject(ii.hbmColor, sizeof(bm), &bm);
hdr.idEntries[0].bWidth = (BYTE)bm.bmWidth;
hdr.idEntries[0].bHeight = (BYTE)bm.bmHeight;
hdr.idEntries[0].wPlanes = bm.bmPlanes;
hdr.idEntries[0].wBitCount = bm.bmBitsPixel;
hdr.idEntries[0].dwBytesInRes = size;
hdr.idEntries[0].dwImageOffset = sizeof(ICONDIR);
fout.write((char*)&hdr, hdr.idEntries[0].dwImageOffset);
fout.write((char*)bytes, size);
DestroyIcon(hicon);
return TRUE;
}
int main()
{
std::string modulepath = "file.exe";
const char *dir = "c:\test\";
HMODULE hmodule = LoadLibraryEx(modulepath.c_str(), NULL,
LOAD_LIBRARY_AS_IMAGE_RESOURCE);
if(hmodule)
EnumResourceNames(hmodule, RT_ICON, (ENUMRESNAMEPROC)EnumIcons, (LONG_PTR)dir);
return 0;
}
ps,不需要VirtualAlloc
为自己的进程分配内存。对于 C++,您可以使用 malloc
或 new
。更好的是,使用 std::string
及其方法。