Очень часто встречается вопрос: "Как получить иконку файла?". Конечно же, все просто! Windows API, а если не получается, значит ты плохо читал документацию, там ведь все прозрачно и поймет любой.
ExtractIcon, ExtractAssociatedIcon, ExtractIconEx и многие другие, не думаю, что стоит продолжать.
И так, все ли так просто как кажется?
Предисловие
Чтобы все это дело заработало, потребуется скомбинировать чуть ли не все подходы в одну высокоуровневую штуковину, которая бы получала путь к фалу и выдавала нам Bitmap, грубо говоря. С путем к файлу в принципе ясно, как же быть с Bitmap? Для легкости в использовании, в свое время, мне потребовалось связать HBITMAP + BITMAPINFO + Scan0 + HDC + Gdiplus::Bitmap. Что из этого вышло можно глянуть тут (DIB.h/DIB.cpp), приводить код-обвертку я не буду. Сразу оговорю, писал давненько, так что используется MFC, но если очень захотеть, то избавиться от него просто, т.к. используются только CString, CRect и т.п.
Приступим
Нам понадобится научить наш модуль распознавать входящий файл, будь то картинка, иконка, исполняемый файл или что-то другое. Зачем? Ну если рассудить логически, будь то картинка, иконка ее была бы самой уменьшенной картинкой. Будь это иконка, аналогично картинке, но вытянуть придется сначала HICON, а потом преобразовать в наш класс-обвертку CDIB. С исполняемым файлом практически, что и с иконкой. Пробегаемся по ресурсам файла в поиске иконки, если не удача, пробуем получить иконку как в общем случае. В большинстве своих случаев, этого вполне достаточно, но все же, нет ничего идеального.
И так, в итоге мы должны иметь возможность:
- Получить размер иконки по пути файла
- Получить иконку (HICON) по пути файла
- Получить наш класс-обвертку CDIB из иконки (HICON)
- Получить наш класс-обвертку CDIB по пути файла
- Получить иконку (HICON) из модуля (HMODULE) с указанным именем ресурса
- Получить иконку (HICON) из модуля (путь файла) с указанным именем ресурса
- Как дополнение получить системную иконку по идентификатору, подобно MessageBox
#ifndef ICONS_H #define ICONS_H #includeНу чтобы долго не раздумывать и не разжевывать, давайте сразу перейдем к непосредственной реализации выше описанного icons.cpp:#include #include #include "DIB.h" namespace Icons { typedef struct { unsigned char bWidth; unsigned char bHeight; unsigned char bColorCount; unsigned char bReserved; short wPlanes; short wBitCount; unsigned int dwBytesInRes; unsigned int dwImageOffset; } ICONDIRENTRY; typedef struct { short idReserved; // = 0 ? short idType; // = 1 ? short idCount; } ICONDIR; int GetIconSize(CString fileName); HICON GetIcon(CString fileName); void GetDIBFromIcon(HICON icon, CDIB *dib); bool GetIcon(CString fileName, CDIB *dib); HICON GetIcon(HMODULE hModule, CString resName); HICON GetIcon(CString fileName, CString resName); HICON GetSystemIcon(UINT uType); } #endif /* ICONS_H */
#include "icons.h"
using namespace Gdiplus;
int Icons::GetIconSize(CString fileName)
{
int iconSize = GetSystemMetrics(SM_CXICON);
FILE *f = NULL;
_wfopen_s(&f, fileName.GetBuffer(), L"r+b");
if(f)
{
ICONDIR iconDir = {0};
fread(&iconDir, 1, sizeof(ICONDIR), f);
if((iconDir.idReserved == 0) && (iconDir.idType == 1))
{
void *iconEntry = malloc(iconDir.idCount * sizeof(ICONDIRENTRY));
fread(iconEntry, sizeof(ICONDIRENTRY), iconDir.idCount, f);
ICONDIRENTRY *p = (ICONDIRENTRY*)iconEntry;
for(int i = 0; i < iconDir.idCount; i++, p++)
{
if(iconSize < p->bWidth)
{
iconSize = p->bWidth;
}
}
free(iconEntry);
}
fclose(f);
}
return iconSize;
}
typedef struct {
CString name;
int index;
int counter;
} ENUMDATA;
BOOL WINAPI EnumResNameProc(HMODULE, LPCTSTR, LPTSTR lpszName, LONG_PTR lParam)
{
ENUMDATA *data = (ENUMDATA*)lParam;
if(data->counter == data->index)
{
if(IS_INTRESOURCE(lpszName))
{
data->name.Format(L"#%d", (ULONG_PTR)lpszName);
}
else
{
data->name = lpszName;
}
return FALSE;
}
else
{
data->counter++;
return TRUE;
}
}
bool AnalizeIcon256(HICON icon)
{
CDIB dib;
Icons::GetDIBFromIcon(icon, &dib);
if(!dib.Ready())
{
return false;
}
DIB_ARGB *p = (DIB_ARGB*)((int)dib.scan0 + (48 * dib.Width() + 48) * 4);
int size = (dib.Width() - 48) * (dib.Height() - 48);
for(int i = 0; i < size; i++, p++)
{
if(p->c != 0)
{
return true;
}
}
return false;
}
HICON Icons::GetIcon(CString fileName)
{
if(fileName.IsEmpty())
{
return NULL;
}
HICON icon = NULL;
SHFILEINFO sfi = {0};
LPITEMIDLIST pidl = SHSimpleIDListFromPath(fileName.GetBuffer());
if(pidl && (fileName.Mid(0, 3) == L"::{"))
{
SHGetFileInfo((LPCWSTR)pidl, 0, &sfi, sizeof(SHFILEINFO), SHGFI_ICONLOCATION | SHGFI_PIDL);
if(wcscmp(sfi.szDisplayName, L"\0") != 0)
{
fileName = sfi.szDisplayName;
}
}
CString ext;
int i = fileName.ReverseFind(L'.');
if(i >= 0)
{
ext = fileName.Mid(i).MakeLower();
}
if((ext == L".exe") || (ext == L".ocx") || (ext == L".dll") ||
(ext == L".scr") || (ext == L".bin") || (ext == L".cpl"))
{
HMODULE hModule = LoadLibraryEx(fileName.GetBuffer(), 0, LOAD_LIBRARY_AS_DATAFILE);
if(hModule)
{
ENUMDATA enumData;
if(sfi.iIcon >= 0)
{
enumData.index = sfi.iIcon;
enumData.counter = 0;
EnumResourceNames(hModule, MAKEINTRESOURCE(3 + DIFFERENCE), EnumResNameProc, (LONG_PTR)&enumData);
}
else
{
enumData.name.Format(L"#%d", -sfi.iIcon);
}
icon = GetIcon(hModule, enumData.name);
FreeLibrary(hModule);
}
}
else
if((ext == L".ico") || (ext == L".icon"))
{
int iconSize = GetIconSize(fileName);
icon = (HICON)LoadImage(0, fileName.GetBuffer(), IMAGE_ICON, iconSize, iconSize, LR_LOADFROMFILE | LR_COLOR);
}
if(!icon)
{
IImageList* imageList;
OSVERSIONINFO osinf = {0};
osinf.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&osinf);
if((osinf.dwMajorVersion >= 6) && SUCCEEDED(SHGetImageList(SHIL_JUMBO, IID_IImageList, (void**)&imageList)))
{
SHGetFileInfo(fileName.GetBuffer(), 0, &sfi, sizeof(sfi), SHGFI_SYSICONINDEX);
imageList->GetIcon(sfi.iIcon, ILD_IMAGE | ILD_TRANSPARENT, &icon);
if(!AnalizeIcon256(icon))
{
DestroyIcon(icon);
icon = NULL;
}
}
if(!icon && SUCCEEDED(SHGetImageList(SHIL_EXTRALARGE, IID_IImageList, (void**)&imageList)))
{
SHGetFileInfo(fileName.GetBuffer(), 0, &sfi, sizeof(sfi), SHGFI_SYSICONINDEX);
imageList->GetIcon(sfi.iIcon, ILD_IMAGE | ILD_TRANSPARENT, &icon);
}
}
if(!icon && pidl && SHGetFileInfo((LPCWSTR)pidl, 0, &sfi, sizeof(SHFILEINFO),
SHGFI_ICON | SHGFI_LARGEICON | SHGFI_PIDL))
{
icon = sfi.hIcon;
}
if(pidl)
{
ILFree(pidl);
}
return icon;
}
HICON Icons::GetIcon(HMODULE hModule, CString resName)
{
HICON icon = NULL;
HRSRC hResource = FindResource(hModule, resName.GetBuffer(), MAKEINTRESOURCE(3 + DIFFERENCE));
if(hResource)
{
HGLOBAL hMem = LoadResource(hModule, hResource);
LPVOID lpResource = LockResource(hMem);
if(hMem && lpResource)
{
int nId = LookupIconIdFromDirectoryEx((PBYTE)lpResource, TRUE, 0x200, 0x200, LR_DEFAULTCOLOR);
hResource = FindResource(hModule, MAKEINTRESOURCE(nId), MAKEINTRESOURCE(3));
if(hResource)
{
hMem = LoadResource(hModule, hResource);
lpResource = LockResource(hMem);
if(hMem && lpResource)
{
icon = CreateIconFromResourceEx((PBYTE)lpResource, SizeofResource(hModule, hResource),
TRUE, 0x00030000, 0, 0, LR_DEFAULTCOLOR);
}
}
}
}
return icon;
}
HICON Icons::GetIcon(CString fileName, CString resName)
{
if(fileName.IsEmpty() || resName.IsEmpty())
{
return NULL;
}
HICON icon = NULL;
HMODULE hModule = (HMODULE)LoadLibraryEx(fileName.GetBuffer(), NULL, LOAD_LIBRARY_AS_DATAFILE);
if(hModule)
{
icon = GetIcon(hModule, resName);
FreeLibrary(hModule);
}
return icon;
}
void Icons::GetDIBFromIcon(HICON icon, CDIB *dib)
{
if(!icon || !dib)
{
return;
}
ICONINFO ii;
GetIconInfo(icon, &ii);
BITMAP bitmap;
GetObject(ii.hbmColor, sizeof(BITMAP), &bitmap);
HDC mainDC = CreateCompatibleDC(0);
HDC maskDC = CreateCompatibleDC(0);
HGDIOBJ mainOld = SelectObject(mainDC, ii.hbmColor);
HGDIOBJ maskOld = SelectObject(maskDC, ii.hbmMask);
dib->Resize(bitmap.bmWidth, bitmap.bmHeight);
if(bitmap.bmBitsPixel < 32)
{
BitBlt(dib->dc, 0, 0, bitmap.bmWidth, bitmap.bmHeight, maskDC, 0, 0, SRCCOPY);
BitBlt(dib->dc, 0, 0, bitmap.bmWidth, bitmap.bmHeight, maskDC, 0, 0, DSTINVERT);
BitBlt(dib->dc, 0, 0, bitmap.bmWidth, bitmap.bmHeight, mainDC, 0, 0, SRCAND);
}
else
{
BitBlt(dib->dc, 0, 0, bitmap.bmWidth, bitmap.bmHeight, mainDC, 0, 0, SRCCOPY);
}
dib->ReflectVertical();
// Check old icons >> 32 bits
bool check = true;
DIB_ARGB *p = dib->scan0;
for(int i = 0; i < dib->Width() * dib->Height(); i++, p++)
{
if(p->a)
{
check = false;
break;
}
}
if(check)
{
p = dib->scan0;
for(int i = 0; i < dib->Width() * dib->Height(); i++, p++)
{
if((p->r != 0x0) && (p->g != 0x0) && (p->b != 0x0))
{
p->a = 255;
}
}
}
SelectObject(maskDC, maskOld);
SelectObject(mainDC, mainOld);
DeleteDC(maskDC);
DeleteDC(mainDC);
DeleteObject(ii.hbmMask);
DeleteObject(ii.hbmColor);
}
bool Icons::GetIcon(CString fileName, CDIB *dib)
{
if(!fileName.IsEmpty())
{
HICON icon = GetIcon(fileName);
if(icon)
{
GetDIBFromIcon(icon, dib);
DestroyIcon(icon);
return true;
}
}
return false;
}
HICON Icons::GetSystemIcon(UINT uType)
{
CString resName;
if((uType & MB_ICONEXCLAMATION) == MB_ICONEXCLAMATION)
{
resName = L"#101";
}
else
if((uType & MB_ICONQUESTION) == MB_ICONQUESTION)
{
resName = L"#102";
}
else
if((uType & MB_ICONERROR) == MB_ICONERROR)
{
resName = L"#103";
}
else
if((uType & MB_ICONINFORMATION) == MB_ICONINFORMATION)
{
resName = L"#104";
}
else
{
return NULL;
}
return GetIcon(L"user32.dll", resName);
}Как так вот. Надеюсь не очень страшно, и разжевывать дословно не стоит. Если какие то вопросы, прошу, спрашивайте не стесняйтесь.
А у проводника с его ShellAPI нет разве доступного интерфейса, которым сам проводник пользуется? Наверняка есть, и наверняка оно через него чуть проще.
ReplyDeleteДа тут как раз и ShellAPI и используется. Проводник, я думаю, как то так же мучается.
ReplyDeleteОн мучается по-разному, к нему можно плагины писать, которые будут помогать ему мучаться для различных типов файлов, у него разные картинки для разных режимов - не говоря о встроенных в иконки изображений разного размера, можно вспомнить режим "Эскизы страниц", где он глубже в файлы лезет. И всем этим можно тоже пользоваться из своих программ, если подходы найти.
ReplyDeleteА иконку обычную достать из файла так, как это делает проводник - можно одним вызовом. Вот как это у меня на Delphi написано:
if FileName <> '' then
begin
// Получаем системную иконку для файла в тек. строке
SHGetFileInfo(PChar(FileName),
FILE_ATTRIBUTE_NORMAL,
SFI,
0,
SHGFI_USEFILEATTRIBUTES or SHGFI_ICON or SHGFI_SMALLICON);
Icon.Picture.Icon.Handle := SFI.hIcon;
ACanvas.Draw(ARect.Left, ARect.Top, Icon.Picture.Graphic);
ADone := True;
end;