2
votes

Recently I made a program which loads an executable or DLL file, enumerates all icon resources in it, and then saves each icon resource as an .ico file in a folder. But it uses Histograms (as discussed in this post) to avoid saving duplicate icons (the icons which appear to be identical).
The program first takes two paths from the user (the user must type them in the console window). One of them is the path of the exe or dll file and the other one is the path of the folder where the .ico files are to be saved.
Then the program loads the exe or dll file with a call to LoadLibraryEx and enumerates the icon resources with a call to EnumResourceNames.
The callback function passed to EnumResourceNames saves each non-duplicate icon resource as an .ico file with a unique name. The names of the saved files are in this manner: 1.ico, 2.ico, 3.ico, 4.ico, .etc

My full code and my problem

The problem is that my program creates corrupted or unknown .ico files, not real ones. I mean, some of the saved icons have weird images, and some of them are not viewable at all! Even though I wrote BITMAPFILEHEADER structure to each .ico file using WriteFile function.
Just compile and run my code, then type the following in the console window:

C:\Windows\System32\shell32.dll

Then type the path of an existing folder where the .ico files are to be saved.
Next, wait at least 4 to 7 seconds until the operation is complete.
Then open that folder, and see the icons! They're laughable!
The following is my code. Sorry if there are some misspellings in the comments, or in the names of variables and functions. Anyway, only the functions main() and EnumIcons() are important, and be sure to read the explanations below the code.

#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();
}

Well, we have four global variables. One of them is folderPath. As I previously mentioned, we take the path of a folder from the user. This action is performed in the main() function and the path is stored in the folderPath variable. I defined this variable as global because it is intended to be accessible by both EnumIcons() function and main() function.
Now, take a look at the main() function. It's just straightforward to understand.
So in the main() function, after taking input from the user, we call LoadLibraryEx and then EnumResourceNames, passing it a pointer to EnumIcons callback function. The EnumIcons function increments the nSucceed global variable for each icon it successfully saves, and increments nFail for each icon it fails to save, or for duplicate icons.
Now, have a look at EnumIcons function which saves each non-duplicate icon found in the EXE or DLL file. The code inside this function is clear, except a part of it which checks whether or not the icon is a duplicate.
That part of code begins with the following comment:

//Checking whether the icon is dupplicate

and ends with the following comment:

//End of checking whether the icon is dupplicate

I just recommend you to skip that part of the function, because it's not related to my primary problem. In EnumIcons function, I called functions like FindResource, LoadResource, LockResource, SizeofResource, CreateFile, and WriteFile.
Just check if I called these functions and did error checking correctly. Don't pay attention to the section which detects duplicate icons.

Solution:

Today, I used the information in @Barmak Shemirani's answer to make some changes to my code and fix the problem. I just wonder what the #pragma directives are (the ones that are used before and after difining ICONDIR and ICONDIRENTRY structures). Here is my modified code:
Edit: The code is updated with minor improvements.

#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();
}
1
I suggest you create a minimal reproducible example if you want help with this problem. There's a lot of unnecessary information and code in what you have posted. The histogram stuff seems completely unrelated to your actual problem.Jonathan Potter
#pragma pack changes structure alignment. For example in struct my_struct { WORD w; int i; }; there is 2 bytes for w and 4 bytes for i. But the total sizeof structure is 8, because the compiler is adding 2 extra bytes padding for w. #pragma pack(push, 2) or #pragma pack(push, 1) tells the compiler not to add padding. #pragam pack(pop) restores default alignment. By the way, you are still using VirtualAlloc. Don't call ReleaseDC except to clean up after GetDC or GetWindowDC. Use DeleteDC to clean up after CreateDC...Barmak Shemirani
@BarmakShemirani Thank you for your explanations about #pragma pack. Well, I edited my question and removed ReleaseDC function calls, and this time I used malloc. If you see VirtualAlloc in any part of my code, plz tell me the function and line number in which you see that. Then I'll fix that. I also defined folderPath as a local variable inside main(), and then passed (LONG_PTR) (folderPath.c_str()) as the last parameter in the call to EnumResources...Javad Bayat

1 Answers

3
votes

The icon file should start with icon header ICONDIR, not BITMAPFILEHEADER. See Icon reference

The bytes obtained from FindResource could contain PNG data, not bitmap. It's best to avoid converting between icon and bitmap.

After initializing and writing the icon header, you can directly write the bytes obtained from FindResource, to avoid any conversion.

EnumResourceNames(... RT_ICON ...) will enumerate the icons as separate, therefore each destination file contains one icon.

EnumResourceNames(... RT_GROUP_ICON ...) will enumerate the icon groups. You can go through the groups and add all the icons in to one file.

It's not clear what is your criteria for finding duplicates. With RT_ICON enumeration, you get the same icon in different sizes and color depth, they are not exact duplicates. You can choose certain sizes, example 32x32 size only. To find exact duplicates, you can calculate the hash of the bytes from resources, then compare the hash values.

#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, you don't need VirtualAlloc to allocate memory for your own process. You can use malloc, or new for C++. Better yet, use std::string and its methods.