WindowsでSHGetFileInfoでアイコンを取得する方法。
SHGFI_SYSICONINDEXを指定した場合、同じアイコンはiIconが同じになるので、テーブルを作っておけばメモリを節約できる。
ちなみに下で、アイコンの描画はDrawIconを使っているが、これはアイコン本来のサイズで描画してくれないので、動作確認のためにはDrawIconExを使ったほうが良い。
DrawIconExに、第五、第六、第七引数に0を指定し、且つ DI_NORMAL | DI_COMPAT をつけて描画すると、アイコンの本来のサイズで描画できる。
DrawIconEx(hdc, 0 + 10, 10, icondrive, 0, 0, 0, NULL, DI_NORMAL | DI_COMPAT); DrawIconEx(hdc, 32 + 10, 10, iconfolder, 0, 0, 0, NULL, DI_NORMAL | DI_COMPAT); DrawIconEx(hdc, 64 + 10, 10, iconfile, 0, 0, 0, NULL, DI_NORMAL | DI_COMPAT); DrawIconEx(hdc, 96 + 10, 10, iconexe, 0, 0, 0, NULL, DI_NORMAL | DI_COMPAT);
本来ならCopyIconしないでそのまま返して、WM_DESTROYのところでDestroyIconすればいいが、「SHGFI_ICONで取得したアイコンはDestroyIconが必要」ということを強調するためにあえて行っている。
#include<windows.h> #include<tchar.h> #include <cstdio> #pragma warning(disable:4996)
// アイコンを取得 HICON my_get_icon(const wchar_t* path) { SHFILEINFO shfi; memset(&shfi, 0, sizeof(SHFILEINFO)); DWORD_PTR ret = SHGetFileInfoW(// アイコンを取得 path, 0, &shfi, sizeof(SHFILEINFO), SHGFI_ICON | /* SHGFI_SYSICONINDEX | */ SHGFI_LARGEICON); // retはアイコンが取得できたかどうかを表す if (SUCCEEDED(ret)) { // アイコンをコピー HICON hicon = CopyIcon(shfi.hIcon); // SHGFI_ICON を指定しているので取得したのはアイコン // なのでDestroyIconで破棄する DestroyIcon(shfi.hIcon); return hicon;// コピーしたアイコンを返す } return NULL; }
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { PAINTSTRUCT ps; HDC hdc; // アイコン一覧 static HICON icondrive; static HICON iconfolder; static HICON iconfile; static HICON iconexe; switch (msg) { case WM_CREATE: // コピーしたアイコンを取得 icondrive = my_get_icon(L"C:\\"); iconfolder = my_get_icon(L"C:\\Windows"); iconfile = my_get_icon(L"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.35.32215\\atlmfc\\include\\afx.h"); iconexe = my_get_icon(L"C:\\Windows\\notepad.exe"); return 0; case WM_DESTROY: // コピーしたアイコンを破棄 DestroyIcon(icondrive); DestroyIcon(iconfolder); DestroyIcon(iconfile); DestroyIcon(iconexe); PostQuitMessage(0); return 0; case WM_PAINT: hdc = BeginPaint(hwnd, &ps); // アイコンを描画 DrawIcon(hdc, 0+10, 10, icondrive); DrawIcon(hdc, 32+10, 10, iconfolder); DrawIcon(hdc, 64+10, 10, iconfile); DrawIcon(hdc, 96+10, 10, iconexe); EndPaint(hwnd, &ps); return 0; } return DefWindowProc(hwnd, msg, wp, lp); }
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow) { HWND hwnd; WNDCLASS winc; MSG msg; winc.style = CS_HREDRAW | CS_VREDRAW; winc.lpfnWndProc = WndProc; winc.cbClsExtra = winc.cbWndExtra = 0; winc.hInstance = hInstance; winc.hIcon = LoadIcon(NULL, IDI_APPLICATION); winc.hCursor = LoadCursor(NULL, IDC_ARROW); winc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); winc.lpszMenuName = NULL; winc.lpszClassName = TEXT("SZL-WND"); if (!RegisterClass(&winc)) return 0; hwnd = CreateWindow( TEXT("SZL-WND"), TEXT("icon test"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 300, 100, NULL, NULL, hInstance, NULL ); if (hwnd == NULL) return 0; while (GetMessage(&msg, NULL, 0, 0)) DispatchMessage(&msg); return msg.wParam; }
よく考えたらImageList_GetIconの時もDestroyIconが必要なので上と合わせるべきなのだが間違っているわけではないというかむしろこちらが正攻法だと思うのでこのままにしておく。
#include<windows.h> #include<tchar.h> // HIMAGELIST // ImageList_GetIcon // 表示用に ImageList_Draw も使える #include <commctrl.h> #pragma comment(lib, "comctl32.lib") #include <cstdio> #pragma warning(disable:4996) // アイコンを取得 HICON my_get_icon(const wchar_t* path) { SHFILEINFO shfi; memset(&shfi, 0, sizeof(SHFILEINFO)); DWORD_PTR ret = SHGetFileInfoW( path, 0, &shfi, sizeof(SHFILEINFO), /*SHGFI_ICON |*/ SHGFI_SYSICONINDEX | SHGFI_LARGEICON); HIMAGELIST imagelist = (HIMAGELIST)ret; // アイコンリストが取得できたかどうかを表す if (imagelist != nullptr) { // imagelistからアイコンを取得 // ImageList_GetIconはアイコンのコピーを渡すので、後でDestroyIconが必要 HICON hicon = ImageList_GetIcon(imagelist, shfi.iIcon, ILD_NORMAL); // SHGFI_SYSICONINDEX を指定した場合、hIconは無効 //DestroyIcon(shfi.hIcon); return hicon;// コピーしたアイコンを返す } return NULL; } /* 上と同じ */