将图标资源(不包括重复的)保存到磁盘

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.ico2.ico3.ico4.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 函数中,我调用了 FindResourceLoadResourceLockResourceSizeofResourceCreateFileWriteFile 等函数。
只需检查我是否调用了这些函数并正确地进行了错误检查。不要注意检测重复图标的部分。

解决方案:

今天,我使用@Barmak Shemirani 的回答中的信息对我的代码进行了一些更改并解决了问题。我只是想知道 #pragma 指令是什么(在定义 ICONDIRICONDIRENTRY 结构之前和之后使用的指令)。这是我修改后的代码:
编辑: 代码已更新并做了一些小的改进。

#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++,您可以使用 mallocnew。更好的是,使用 std::string 及其方法。