C#
アイコンの作成
アイコン・ファイルのフォーマットがわかったので、アイコンを作るところもやってみた。思いつく限りをトライして手探りで調べた。忘れないように記録しておく。以下の手順は、アイコン・ファイルを読み込んでICONIMAGE構造体を作成するところまではできているという前提で説明する。
方針
- APIのCreateIconIndirect()でアイコンのハンドルを作成し、Icon.FromHandle()でIconクラスのインスタンスを作成する。
- ICONINFO.hbmMaskには、APIのCreateBitmap()で作成した1ビットカラーのDDB (Device Dependent Bitmap) のハンドルを指定する。
- ICONINFO.hbmColorには、APIのCreateCompatibleBitmap()で作成したDDBのハンドルを指定する。
- ビットマップの内容は、一旦DIB (Device Independent Bitmap) を作ってから、APIのBilBltでDDBに転送する。
手順
- 準備
- マスク・ビットマップ用のカラーテーブル (#1) を用意する。2色でインデックス0は黒、インデックス1は白にする。
- マスク・ビットマップの作成
- CreateDIBSection()で1ビットカラーのDIB (#2) を作成する[*1]。
- ICONIMAGE.icANDの内容を、DIB (#2) のビットマップ内容にコピーする。icANDはDIBなので、そのままコピーすれば良い。
- SetDIBColorTable()でマスク・ビットマップ用のカラーテーブル (#1) を設定する。
- CreateBitmap()で1ビットカラーのDDB (#3) を作成する[*1]。
- BitBltでDIB (#2) からDDB (#3) へコピーする。
- カラー・ビットマップの作成(マスク・ビットマップの作成とだいたい同じ)
- ICONIMAGE.icHeaderの情報を元に、CreateDIBSection()でDIB (#4) を作成する[*1]。
- ICONIMAGE.icXORの内容を、DIB (#4) のビットマップ内容にコピーする。icXORはDIBなので、そのままコピーすれば良い。
- 作成したDIB (#4) がインデックス・カラーの場合は、SetDIBColorTable()でICONIMAGE.icColorsを設定する。
- CreateCompatibleBitmap()でDDB (#5) を作成する[*1]。ディスプレイ・デバイスと互換性のあるビットマップ。多くの場合32ビットカラーになると思う。
- BitBltでDIB (#4) からDDB (#5) へコピーする。
- アイコンの作成
- ICONINFO.hbmMaskにマスク・ビットマップ (#3) のハンドル設定する。
- ICONINFO.hbmColorにカラー・ビットマップ (#5) のハンドルを設定する。
- CreateIconIndirect()でアイコンのハンドルを作成する。
- Icon.FromHandle()でIconクラスのインスタンスを作成する。
- [*1]
- ICONIMAGE.icHeader.biHeightは、2倍の高さになっている。ビットマップを作成するときは元の高さにする必要がある。
失敗
- BitmapクラスのGetHbitmap()メソッドは、CreateCompatibleBitmap()のように、ディスプレイと互換性のあるビットマップを作成する。1ビットカラーのビットマップを作ってそのハンドルを引き取っても無理。32ビットカラーのビットマップが返ってきてしまう。Bitmapクラスでは、マスク・ビットマップのハンドルを作成できない。
- ICONINFO構造体のhbmMaskやhbmColorに、CreateDIBSection()で作成したビットマップのハンドルを指定すると、CreateIconIndirect()で失敗する。
- DIBを作ってDDBに転送する代わりに、CreateDIBitmap()でDDBを作ることはできる。デバイス・コンテキスト次第で1ビットカラーのビットマップも作成できる。BitBltを使うよりずっと簡単だが、残念なことにカラーテーブルが反映されない。惜しい。もう一歩という気がする。何かやり方があるのだろうと思うが、探せなかった。CreateCompatibleBitmap()とSetDIBits()の組み合わせでもうまくいかなかった。CreateDIBitmap()と同じ結果になった。
その他
- 手順には概要を記したが、実際のコーディングではデバイス・コンテキストを作成したり、ビットマップを選択したり、後始末があったりなどして、少々面倒くさい。
- CreateDIBSection()では、BITMAPINFO構造体のbmiColorsを見ていないようだ。
- DIBのビットマップ内容はDWORD(4バイト)境界だが、DDBのビットマップ内容はWORD(2バイト)境界になっているようだ。
- CreateIconIndirect()で使用したマスク・ビットマップとカラー・ビットマップを削除するタイミングはよくわからないが、これらのビットマップを管理し、不要になったら削除しなければならないそうだ。
- IconクラスのDispose()でアイコンのリソースも解放してくれそうだが、Icon.FromHandle()で作成したIconはDispose()してもリソースを解放してくれないので、APIのDestroyIcon()で削除する必要がある。ちなみにCursorクラスでも似たような状況。
- BitmapクラスのインスタンスからIconクラスのインスタンスを作るには、Bitmap.GetHicon()メソッドを使うのが簡単。32ビットARGBのビットマップなら、ちゃんと透過のアイコンを作ってくれる。
- アイコン・ファイルからIconクラスのインスタンスを作るには、Iconクラスのコンストラクタを使う。
- 要するに、あまり必要ない知識か。
(2016/05/25 初稿)