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

标签 c++ winapi resources icons

最近,我制作了一个程序,该程序加载可执行文件或DLL文件,枚举其中的所有图标资源,然后将每个图标资源另存为.ico文件在文件夹中。但是它使用直方图(如this post中所述)来避免保存重复的图标(看起来相同的图标)。
该程序首先从用户处获取两条路径(用户必须在控制台窗口中键入它们)。其中一个是exe或dll文件的路径,另一个是要保存.ico文件的文件夹的路径。
然后,程序通过调用LoadLibraryEx加载exe或dll文件,并通过调用EnumResourceNames枚举图标资源。
传递给EnumResourceNames的回调函数将每个非重复的图标资源另存为具有唯一名称的.ico文件。保存文件的名称是这样的:1.ico2.ico3.ico4.ico,.etc

我的完整代码和问题

问题是我的程序创建的是损坏或未知的.ico文件,不是真实的文件。
我的意思是,有些保存的图标有奇怪的图像,而有些根本看不到!即使我使用BITMAPFILEHEADER函数将WriteFile结构写入每个.ico文件。
只需编译并运行我的代码,然后在控制台窗口中键入以下内容:

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 ...)将枚举图标组。您可以遍历各个组并将所有图标添加到一个文件中。

目前尚不清楚您查找重复项的标准是什么。使用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为自己的进程分配内存。您可以将mallocnew用于C++。更好的是,使用std::string及其方法。

关于c++ - 将图标资源(不包括重复的图标)保存到磁盘,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51736843/

相关文章:

c++ - 单个 MFC/Win32 控件似乎让我的整个桌面重绘

c++ - 检查元组是否包含某种类型的元素

C++串口库对Win32同步函数的使用?

c++ - 如何从远程线程 DestroyWindow?

android - 无法在android库项目中添加id资源类型文件

c++ - 命名空间编译问题

c++ - CPU指令重排序

c++ - 如何判断屏幕保护程序是否被暂时禁用?

c# - 在 WPF 中作为资源播放声音

c++ - WinAPI C++ 如何从 64 位应用程序加载资源