スポンサーリンク

SHGetFileInfoでアイコンを取得(1)

WindowsでSHGetFileInfoでアイコンを取得する方法。

  • 引数にSHGFI_ICON を指定した場合
    • アイコン(HICON)が取得できるので、そのアイコンを使用。
    • 使用後はDestroyIconで破棄
  • 引数にSHGFI_SYSICONINDEX を指定した場合
    • 戻り値にIMAGELIST、SHFILEINFOのiIconにそのIMAGELIST内のアイコンの識別子が取得できる
    • hIconは無効なので使ってはいけない
    • IMAGELISTはシステムが管理しているので破棄してはいけない

SHGFI_SYSICONINDEXを指定した場合、同じアイコンはiIconが同じになるので、テーブルを作っておけばメモリを節約できる。

DrawIconEx

ちなみに下で、アイコンの描画は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);

1.アイコンを取得

本来なら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;
}

2.アイコンリストを取得してからアイコン取得

よく考えたら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;
}

/* 上と同じ */

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)


この記事のトラックバックURL: