« システムイメージリストを使用したアイコン表示(2) | トップページ | ファイルからアイコンを抽出して、ImageListへ登録 »

2010年11月28日 (日)

ファイルに関連づけされたアイコンをImageクラスで取得

アイコンネタ第2弾です。

アイコンをコントロールに表示する場合、

  1. Image を直接指定する方法
  2. ImageList を指定して、ImageIndex を指定する方法

があります。

今回は、ファイルに関連づけされたアイコンを表示するときに、前者の方法を行なうのに有効な、汎用的なメソッドを作成したものを公開します。後者の方法でも、ImageList への登録にも勿論使えます。
以前似たようなタイトルの記事「ファイルに関連付けられたアイコンの取得」でちょっと触れている Icon.ExtractAssociatedIcon メソッドは使用せず、Win32API の SHGetFileInfo を使用します。これにより、大きいアイコンでも小さいアイコンでも取得出来ます。また、ファイルが実在しなくても取得可能なオプションも指定出来ます。

では、そのソースコードです。

using System;
using System.IO;
using System.Drawing;
using System.Runtime.InteropServices;

namespace Acha_ya.SampleApplication
{
    [ StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto) ]
    struct SHFILEINFO 
    {
        public IntPtr hIcon;
        public IntPtr iIcon;
        public uint dwAttributes;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
        public string szDisplayName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
        public string szTypeName;
    }
    /// <summary>
    /// アイコン系ユーティリティクラス
    /// </summary>
    public class IconUtility
    {
        const uint SHGFI_LARGEICON         = 0x00000000;
        const uint SHGFI_SMALLICON         = 0x00000001;
        const uint SHGFI_USEFILEATTRIBUTES = 0x00000010;
        const uint SHGFI_ICON              = 0x00000100;
        [DllImport("shell32.dll", CharSet=CharSet.Auto)]
        static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbFileInfo, uint uFlags);
        [DllImport("user32.dll", CharSet=CharSet.Auto)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool DestroyIcon(IntPtr hIcon);

        /// <summary>
        /// 指定したファイルパスに関連付けされたアイコンイメージを取得する。
        /// </summary>
        /// <remarks>
        /// このメソッドは、ファイルの存在チェックを行ない、指定されなかった第3パラメータの
        /// 値を決定する。
        /// </remarks>
        /// <param name="path">アイコンイメージ取得対象のファイルのパス</param>
        /// <param name="isLarge">大きいアイコンを取得するとき true、小さいアイコンを取得するとき false</param>
        /// <returns>取得されたアイコンのビットマップイメージを返す。</returns>
        public static Image FileAssociatedImage(string path, bool isLarge)
        {
            return FileAssociatedImage(path, isLarge, File.Exists(path));
        }
        /// <summary>
        /// 指定したファイルパスに関連付けされたアイコンイメージを取得する。
        /// </summary>
        /// <param name="path">アイコンイメージ取得対象のファイルのパス</param>
        /// <param name="isLarge">
        /// 大きいアイコンを取得するとき true、小さいアイコンを取得するとき false
        /// </param>
        /// <param name="isExist">
        /// ファイルが実在するときだけ動作させるとき true、実在しなくて動作させるとき false
        /// </param>
        /// <returns>取得されたアイコンのビットマップイメージを返す。</returns>
        public static Image FileAssociatedImage(string path, bool isLarge, bool isExist)
        {
            SHFILEINFO fileInfo = new SHFILEINFO();
            uint flags = SHGFI_ICON;
            if (!isLarge) flags |= SHGFI_SMALLICON;
            if (!isExist) flags |= SHGFI_USEFILEATTRIBUTES;
            try
            {
                SHGetFileInfo(path, 0, ref fileInfo, (uint)Marshal.SizeOf(fileInfo), flags);
                if (fileInfo.hIcon == IntPtr.Zero)
                    return null;
                else
                    return Icon.FromHandle(fileInfo.hIcon).ToBitmap();
            }
            finally
            {
                if (fileInfo.hIcon != IntPtr.Zero)
                    DestroyIcon(fileInfo.hIcon);
            }
        }
    }
}

コード中のXMLコメントを見れば、使用方法は判ると思います。一応、第3引数を省略した場合、File.Exists を使用して、ファイルが実在するかチェックしています。しかし必要に応じて、第3引数省略時は false を指定したものとみなす様に書き換えてもいいと思います。
注意点としては、SHGetFileInfo でアイコンハンドルを取得しているので、必ず DestroyIcon を実行します。これを忘れるとリソースリークが発生します。

最後にこのソースとこれをテストする簡単なテストAP等を、CAB形式で圧縮してここに置いておきます。
「FileAssoc.cab」をダウンロード


« システムイメージリストを使用したアイコン表示(2) | トップページ | ファイルからアイコンを抽出して、ImageListへ登録 »

C# Tips」カテゴリの記事

コメント

コメントを書く

コメントは記事投稿者が公開するまで表示されません。

(ウェブ上には掲載しません)

トラックバック


この記事へのトラックバック一覧です: ファイルに関連づけされたアイコンをImageクラスで取得:

« システムイメージリストを使用したアイコン表示(2) | トップページ | ファイルからアイコンを抽出して、ImageListへ登録 »