アイコンを取得・描画してみる

アイコン

Windowsを使っている以上は必ずお目にかかっているはずなので、アイコンが何かとはいまさら説明することもないと思いますが、デスクトップやタスクバー、各ウィンドウの左上など、至るところに表示されているビットマップイメージのことです。

Windowsでは、ユーザーが操作を視覚的に行なうことができるように、画面の表示にグラフィックスを多用し、マウスによって操作を行なうことができるGUI(グラフィカルユーザーインターフェース)が使われています。この中で、アイコンはファイルやフォルダ、ショートカット、アプリケーションなどを表すために使用され、これらをマウス操作することで対象を開いたり実行したりすることができるようになっているのです。

これらのアイコンの実際のデータは、アイコンリソースとして、実行可能ファイル(EXEやDLL)やアイコンファイル(.ICO)の中に含まれています。(『リソース』は広い意味ではプログラム実行時に資源とみなせるあらゆるものを指しますが、ここでは、狭い意味での、Windowsプログラムで使用されるメニュー、アイコン、カーソルなどのようなデータを指しています。)

今回はこれらのファイルからアイコンを読み込んで表示してみたいと思います。

アイコンオブジェクトの作成

Windows上では、GUIを構成するアイコンは、ユーザーオブジェクトの1つである『アイコンオブジェクト』として扱われます。したがって、プログラム中でアイコンを使用するためには、まずアイコンオブジェクトを作成してハンドルを取得し、そのアイコンオブジェクトを識別するハンドル(アイコンハンドル)をアイコン操作関数に渡さなくてはなりません。

アイコンオブジェクトを作成するための方法は何通りかあります。ビットマップイメージから直接作成する方法もありますが、今回は、実行可能ファイルにアイコンリソースとして含まれるアイコンや、アイコンファイル(.ico)に含まれているアイコンのデータを取り出して、そのデータからアイコンオブジェクトを作成する方法を説明しましょう。

ファイルにアイコンリソースとして含まれるアイコンからアイコンオブジェクトを作成するには ExtractIcon 関数または ExtractIconEx 関数を呼び出します。通常、ファイルに含まれるアイコンを1つだけ取り出してアイコンオブジェクトを作成したい場合には ExtractIcon 関数を使います。また、ファイルに含まれる複数のアイコンから複数のアイコンオブジェクトを一度に作成したい場合や、大きいアイコンではなく小さいアイコンのオブジェクトを作成したい場合には ExtractIconEx 関数を使います。

ここでは ExtractIconEx 関数について説明しましょう。

UINT ExtractIconExA(
    PCTSTR pszFile,     // ファイル名
    UINT   nIconIndex,  // アイコンのインデックス
    HICON *phIconLarge, // 大きなアイコンのハンドルを格納する変数
    HICON *phIconSmall, // 小さなアイコンのハンドルを格納する変数
    UINT   nIcons       // 取得するアイコンの数
);

pszFile パラメータにはアイコンリソースを持つファイルのファイル名を表す文字列のアドレスを、 nIconIndex パラメータと nIcons パラメータには取得を始めるアイコンのインデックスと取得する数をそれぞれ指定します。

アイコンはオブジェクトとして扱われるので、他のWindowsオブジェクトと同じようにハンドルで識別されます。これらを格納するための配列変数のアドレスを phIconLarge パラメータと phIconSmall パラメータに指定します。 phIconLarge パラメータには大きいアイコンのハンドルを格納するための配列変数のアドレスを、 phIconSmall パラメータには小さいアイコンのハンドルを格納するための配列変数のアドレスをそれぞれ指定します。また、大きいアイコンだけを取得したい場合には phIconSmall パラメータに 0 (NULL) を、小さいアイコンだけを取得したい場合には phIconLarge パラメータに 0 (NULL) を指定します。

ExtractIconEx 関数は、戻り値として、実際にハンドルが返されたアイコンの数を返します。また、 nIconIndex パラメータに -1 を、 phIconLarge パラメータと phIconSmall パラメータに 0 (NULL) を指定した場合は、戻り値として、指定されたファイルが持っているアイコンの数を返します。

アイコンの描画

今回はアイコンオブジェクトのイメージをウィンドウに描画してみます。

アイコンを描画するには DrawIconEx 関数を使います。

BOOL DrawIconEx(
    HDC    hDC,         // デバイスコンテキスト
    int    x,           // x座標
    int    y,           // y座標
    HICON  hIcon,       // アイコンまたはマウスカーソルハンドル
    int    width,       // 幅
    int    height,      // 高さ
    UINT   iStepAniCur, // フレームインデックス
    HBRUSH hbrFlicker,  // ブラシ
    UINT   uFlags       // フラグ
);

hDC パラメータには描画先であるHSPウィンドウのデバイスコンテキストのハンドルを指定します。HSPウィンドウのデバイスコンテキストのハンドルは BMSCR 構造体から取得することができます。

hIcon パラメータには描画するアイコンのハンドルを指定し、 x パラメータと y パラメータにはカーソルを描画する位置を、 width パラメータと height パラメータには描画サイズをそれぞれ指定します。

今回は iStepAniCur パラメータと hbrFlicker パラメータには 0 を指定しておくことにし、 uFlags パラメータには 3 (DI_NORMAL) を指定します。

アイコンの破棄

作成されたアイコンオブジェクトは、もうそれ以上使用することがないと分かった時点で削除する必要があります。一般に、アイコンオブジェクトを削除して、アイコンのために使用されていたメモリ領域を解放することを「アイコンを破棄する」と呼びます。アイコンを破棄するには、 DestroyIcon 関数を呼び出します。メモリリークを防ぐためにも、描画した後に破棄するようにしましょう。

BOOL DestroyIcon(
    HICON hIcon  // アイコンハンドル
);

hIcon パラメータには、破棄するアイコンのハンドルを指定します。

サンプルスクリプト

さて、実際にスクリプトを書いてみます。

サンプルスクリプト main.as

    #include "llmod.as"

    #module ;######## アイコンオブジェクト操作モジュール ############

    ;===============================================================
    ; ファイルからアイコンオブジェクトを作成しハンドルを取得
    ; GetIconFromFile  v1, s2, n3, n4
    ;     v1 : アイコンハンドルを格納するための配列変数
    ;     s2 : アイコンを持つファイルのファイル名
    ;     n3 : アイコンのインデックス(-1のときアイコンの数取得)
    ;     n4 : 取得するアイコンの数
    ;   stat : 作成されたアイコンオブジェクトの数
    ;===============================================================
    #deffunc GetIconFromFile  val, str, int, int
    mref ret, 16                ; ハンドルを格納する変数
    mref setfilename, 33        ; ファイル名
    mref index, 2               ; アイコンのインデックス
                                ; (-1を指定するとアイコン数を取得)
    mref num, 3                 ; 取得するアイコンの数

    sdim filename, 260
    filename = setfilename      ; いったん別の変数に移す

    ; アイコンオブジェクト作成
    getptr pm.0, filename
    pm.1 = index
    if index != -1 {
        getptr pm.2, ret        ; 配列変数のアドレス
    } else {
        ; ファイルが持つアイコンの数を取得する場合
        pm.2 = 0
    }
    pm.3 = 0
    pm.4 = num
    dllproc "ExtractIconExA", pm, 5, D_SHELL@
    if index == -1 {
        ret = stat              ; ファイルの持つアイコンの数
    }
    return

    ;===============================================================
    ; アイコンオブジェクトの描画中ウィンドウにイメージを描画
    ; DrawIcon  n1, n2, n3, n4, n5
    ;     n1 : アイコンハンドル
    ;     n2 : x座標
    ;     n3 : y座標
    ;     n4 : 幅
    ;     n4 : 高さ
    ;===============================================================
    #deffunc DrawIcon  int, int, int, int, int
    mref hIcon, 0               ; アイコンハンドル
    mref cx, 1                  ; x座標
    mref cy, 2                  ; y座標
    mref sx, 3                  ; 幅
    mref sy, 4                  ; 高さ
    mref bmscr, 67              ; 描画中ウィンドウのBMSCR構造体

    ;アイコンを描画
    pm = bmscr.4, cx, cy, hIcon, sx, sy, 0, 0, 3
    dllproc "DrawIconEx", pm, 9, D_USER@
    return

    ;===============================================================
    ; アイコンを破棄
    ; DestroyIcon  n1
    ;     n1 : アイコンハンドル
    ;===============================================================
    #deffunc DestroyIcon  int
    mref hIcon, 0               ; アイコンハンドル

    ; アイコンの破棄
    pm = hIcon
    dllproc "DestroyIcon", pm, 1, D_USER@
    return

    #global ;############# モジュール終わり ########################


    ; ファイル名を指定(DLL,EXE,ICO)
    sdim fname, 260
    fname = "shell32.dll"

    ; ファイルの持つアイコンの数を取得
    GetIconFromFile num, fname, -1

    if num == 0 {
        mes "指定されたファイルはアイコンを持ちません"
        stop
    }

    ; アイコンハンドルの取得
    dim hIcon, num
    GetIconFromFile hIcon, fname, 0, num

    ; アイコンの描画
    size = 32                   ; 描画サイズ
    numline = winx / size       ; 1行に表示するアイコン数
    repeat num
        x = cnt \ numline * size
        y = cnt / numline * size
        DrawIcon hIcon.cnt, x, y, size, size
    loop
    redraw                      ; 実際の画面の反映させる

    ; アイコンハンドルの破棄
    repeat num
        DestroyIcon hIcon.cnt
    loop

    stop