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();
}
#pragma pack
changes structure alignment. For example instruct my_struct { WORD w; int i; };
there is 2 bytes forw
and 4 bytes fori
. But the totalsizeof
structure is8
, because the compiler is adding 2 extra bytes padding forw
.#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 usingVirtualAlloc
. Don't callReleaseDC
except to clean up afterGetDC
orGetWindowDC
. UseDeleteDC
to clean up afterCreateDC...
– Barmak Shemirani#pragma pack
. Well, I edited my question and removedReleaseDC
function calls, and this time I usedmalloc
. If you seeVirtualAlloc
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 definedfolderPath
as a local variable insidemain()
, and then passed(LONG_PTR) (folderPath.c_str())
as the last parameter in the call toEnumResources
... – Javad Bayat