実行可能ファイルからのアイコンの抽出
実行可能ファイルからアイコンを抽出する簡単な方法は、ExtractIcon または ExtractIconEx (Win32 のみ) を使います。その定義は以下の通りです。
[C/C++]
HICON STDAPICALLTYPE ExtractIconA(HINSTANCE hInst, LPCSTR lpszExeFileName, UINT nIconIndex); HICON STDAPICALLTYPE ExtractIconW(HINSTANCE hInst, LPCWSTR lpszExeFileName, UINT nIconIndex); UINT STDAPICALLTYPE ExtractIconExA(LPCSTR lpszFile, int nIconIndex, HICON *phiconLarge, HICON *phiconSmall, UINT nIcons); UINT STDAPICALLTYPE ExtractIconExW(LPCWSTR lpszFile, int nIconIndex, HICON *phiconLarge, HICON *phiconSmall, UINT nIcons); #ifdef UNICODE #define ExtractIcon ExtractIconW #define ExtractIconEx ExtractIconExW #else #define ExtractIcon ExtractIconA #define ExtractIconEx ExtractIconExA #endif // !UNICODE
[VB 6.0]
Declare Function ExtractIcon Lib "shell32.dll" Alias "ExtractIconA" _ (ByVal hInst As Long, ByVal lpszExeFileName As String, _ ByVal nIconIndex As Long) As Long Declare Function ExtractIconEx Lib "shell32.dll" Alias "ExtractIconExA" _ (ByVal lpszFile As String, ByVal nIconIndex As Long, _ ByRef phiconLarge As Long, ByRef phiconSmall As Long, _ ByVal nIcons As Long) As Long
[VB.NET]
Declare Ansi Function ExtractIcon Lib "shell32.dll" Alias "ExtractIconA" _ (ByVal hInst As System.IntPtr, ByVal lpszExeFileName As String, _ ByVal nIconIndex As Integer) As System.IntPtr Declare Ansi Function ExtractIconEx Lib "shell32.dll" Alias "ExtractIconExA" _ (ByVal lpszFile As String, ByVal nIconIndex As Integer, _ ByRef phiconLarge As System.IntPtr, ByRef phiconSmall As System.IntPtr, _ ByVal nIcons As Integer) As Integer
hInst | アプリケーションのインスタンスハンドルを指定します。(取得できない場合は、ExtractIconEx を使ったほうが良いかもしれません。) |
lpszExeFileName / lpszFile | アイコンを取得するファイル名を指定します。このファイル名は、EXE、DLL、ICO などの拡張子のファイルです。 |
nIconIndex | 取得するアイコンのインデックスを、0 から始まる値で指定します。また、負の値(-100 など)を指定すると、その絶対値(-100 → 100)を ID に持つアイコンを取得します。 ExtractIconEx の場合は、ここで指定した値に当たるアイコンから nIcons で指定した数だけアイコンを取得します。たとえば、ここで 1 を指定し、nIcons で 4 を指定すると、phiconLarge および phiconSmall に、インデックス 1~4 のアイコンが格納されます。 |
phiconLarge, phiconSmall | アイコンを受け取る HICON 型([VB] Long、[VB.NET] System.IntPtr) の配列を指定します。この配列のアイテム数は、nIcons の値以上でなければなりません。phiconLarge には大きいアイコン(32 x 32)、phiconSmall には小さいアイコン(16 x 16)が格納されます。 phiconLarge と phiconSmall の両方に NULL (0) を指定し、nIconIndex に -1、nIcons に 0 (これは念のため)を指定することで、ファイルに含まれるアイコンの総数を取得することができます。 |
nIcons | 取得するアイコンの数を指定します。 |
※ ExtractIcon の戻り値は HICON ([VB] Long、[VB.NET] System.IntPtr) です。取得したアイコンのハンドル(HICON)は、使用後必ず DestroyIcon で破棄する必要があります。
これらの関数は、データ内からアイコンデータを取り出し、そのデータを元に自動的にアイコンのハンドルを作成してくれます。
ところが、これらの関数では、リソース ID からアイコンを作成するのに失敗することがあります(どの条件で失敗するかは忘れた・・・)。レジストリのファイル関連付けのデータにあるような、アイコンを含むファイル名と ID (ここでのデータはマイナスになっていて、絶対値が ID の値になってます) からアイコンを作成したり、16x16 や 32x32 以外のアイコンを取得する(ExtractIcon(Ex)ではできない)には、LoadLibraryEx、FindResource、LookupIconIdFromDirectoryEx、CreateIconFromResource などの関数を使って取得することが出来ます。
以下に例を示します。
[C/C++]
// EnumResourceNames で使うデータ struct _CMyExtractIconData { // 取得するアイコンのインデックス int nIndex; // 現在列挙しているアイコンのインデックス int nNowPos; // 見つかったかどうか bool bFound; // true の場合は uID、false の場合は lpszName を使う // (リソースの ID が文字列の場合を考慮) bool bIsID; // リソース ID (数値) UINT uID; // リソース ID (文字列) LPTSTR lpszName; }; // EnumResourceNames のコールバック関数 extern "C" BOOL CALLBACK _MyEnumResNameProc(HINSTANCE hInstance, LPCTSTR lpszType, LPTSTR lpszName, _CMyExtractIconData FAR* pData) { // 現在のインデックスが欲しいアイコンのインデックスと一致するかどうか if (pData->nNowPos == pData->nIndex) { // 一致 → 見つかった pData->bFound = true; // lpszName の上位ワードが 0 のときは ID が数値 // (文字列のポインタで上位ワードが 0 のものはまず無い) if (pData->bIsID = (HIWORD(lpszName) == 0)) pData->uID = LOWORD(lpszName); else // 文字列のコピーを作成する pData->lpszName = _tcsdup(lpszName); // FALSE を返して列挙を終了する return FALSE; } // 次のインデックスにしておく pData->nNowPos++; return TRUE; } // lpszPathName: アイコンのデータが入っているアプリケーション/DLL // nIndexOrID: 正の値(0 含む)ならインデックス、負の値なら ID // bSmallIcon: 16x16 のサイズのアイコンを取得するかどうか (false なら 32x32) HICON MyExtractIcon(LPCTSTR lpszPathName, int nIndexOrID, bool bSmallIcon) { HINSTANCE hInstance; HICON hIconRet; _CMyExtractIconData data; HRSRC hRes, hRes2; HGLOBAL hMem, hMem2; LPVOID lpv, lpv2; if (!lpszPathName) return NULL; // 実行はしないので LOAD_LIBRARY_AS_DATAFILE を指定する hInstance = LoadLibraryEx(lpszPathName, NULL, LOAD_LIBRARY_AS_DATAFILE | LOAD_WITH_ALTERED_SEARCH_PATH); if (!hInstance) return NULL; hIconRet = NULL; if (nIndexOrID < 0) { // 負の値 = 識別子 // LoadIcon や LoadImage だと Win95/98/Me で読み込めない・・・ data.bIsID = true; data.uID = (UINT) (-nIndexOrID); } else { // 正の値 = インデックス // リソースを列挙して読み込み data.nIndex = nIndexOrID; data.nNowPos = 0; data.bFound = false; EnumResourceNames(hInstance, RT_GROUP_ICON, (ENUMRESNAMEPROC) _MyEnumResNameProc, (LPARAM)(_CMyExtractIconData FAR*) &data); // 見つからなかったら終了 if (!data.bFound) { FreeLibrary(hInstance); return NULL; } } // グループアイコン リソースを取得 // (サイズや色情報が複数含まれている可能性もあるため) hRes = FindResource(hInstance, data.bIsID ? MAKEINTRESOURCE(data.uID) : (LPCTSTR) data.lpszName, RT_GROUP_ICON); if (hRes) { // FindResource のあとは LoadResource、LockResource hMem = LoadResource(hInstance, hRes); lpv = LockResource(hMem); // アイコン本体の ID (指定したサイズ、色を持つアイコン) を取得する関数 // (第 2 引数が FALSE の場合カーソルを取得) // (bSmallIcon が true なら 16x16、false なら 32x32) // (色はデフォルトのものを使用する) // ※ ここでサイズを任意のものに指定できます。(48x48 など) data.uID = LookupIconIdFromDirectoryEx((PBYTE) lpv, TRUE, bSmallIcon ? 16 : 32, bSmallIcon ? 16 : 32, LR_DEFAULTCOLOR); // アイコン本体のリソースを取得 hRes2 = FindResource(hInstance, MAKEINTRESOURCE(data.uID), RT_ICON); if (hRes2) { // FindResource のあとは LoadResource、LockResource hMem2 = LoadResource(hInstance, hRes2); lpv2 = LockResource(hMem2); // リソースからアイコンを作成 hIconRet = CreateIconFromResource((PBYTE) lpv2, SizeofResource(hInstance, hRes2), TRUE, 0x00030000); // Win32 ではロック解除、解放はいらない (呼び出せない) //UnlockResource(hMem2); //FreeResource(hRes2); } //UnlockResource(hMem); //FreeResource(hRes); } // 文字列を使用している場合は解放する if (!data.bIsID) free(data.lpszName); FreeLibrary(hInstance); return hIconRet; }
途中で「LoadIcon や LoadImage だと Win95/98/Me で読み込めない・・・」と書いてあるのは、LoadLibraryEx で読み込んだ実行可能ファイルに対して、Win95/98/Me では LoadIcon や LoadImage などでリソースを読み込めないため、結局 FindResource → LoadResource を使う必要がある、という事です。(これは LoadBitmap などでも同じだと思われます。)
[VB 6.0]
※ コメントがほとんど入っていません。動作自体は上と同じなのでそれを参考にしてください。
Public Const LR_DEFAULTCOLOR As Long = &H0& Public Const LR_MONOCHROME As Long = &H1& Public Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" _ (ByRef Dest As Any, ByRef Source As Any, ByVal Length As Long) Public Declare Function lstrlen Lib "kernel32.dll" Alias "lstrlenA" _ (ByVal lpszString As Any) As Long Public Type MyExtractIconData Index As Long NowPos As Long Found As Boolean IsID As Boolean ID As Long Name As String End Type Public Const DONT_RESOLVE_DLL_REFERENCES As Long = &H1& Public Const LOAD_LIBRARY_AS_DATAFILE As Long = &H2& Public Const LOAD_WITH_ALTERED_SEARCH_PATH As Long = &H8& Public Const LOAD_IGNORE_CODE_AUTHZ_LEVEL As Long = &H10& Public Declare Function LoadLibraryEx Lib "kernel32.dll" Alias "LoadLibraryExA" _ (ByVal lpLibFileName As String, ByVal hFile As Long, ByVal dwFlags As Long) As Long Public Declare Function FreeLibrary Lib "kernel32.dll" (ByVal hModule As Long) As Long Public Const RT_CURSOR As Long = 1 Public Const RT_BITMAP As Long = 2 Public Const RT_ICON As Long = 3 Public Const RT_MENU As Long = 4 Public Const RT_DIALOG As Long = 5 Public Const RT_STRING As Long = 6 Public Const RT_FONTDIR As Long = 7 Public Const RT_FONT As Long = 8 Public Const RT_ACCELERATOR As Long = 9 Public Const RT_RCDATA As Long = 10 Public Const RT_MESSAGETABLE As Long = 11 Public Const RT_GROUP_CURSOR As Long = 12 Public Const RT_GROUP_ICON As Long = 14 Public Const RT_VERSION As Long = 16 Public Const RT_DLGINCLUDE As Long = 17 Public Const RT_PLUGPLAY As Long = 19 Public Const RT_VXD As Long = 20 Public Const RT_ANICURSOR As Long = 21 Public Const RT_ANIICON As Long = 22 Public Const RT_HTML As Long = 23 Public Const RT_MANIFEST As Long = 24 Public Declare Function EnumResourceNames Lib "kernel32.dll" Alias "EnumResourceNamesA" _ (ByVal hModule As Long, ByVal lpszType As Any, _ ByVal lpEnumFunc As Long, ByRef lParam As Any) As Long Public Declare Function FindResource Lib "kernel32.dll" Alias "FindResourceA" _ (ByVal hModule As Long, ByVal lpName As Any, ByVal lpType As Any) As Long Public Declare Function LoadResource Lib "kernel32.dll" _ (ByVal hModule As Long, ByVal hResInfo As Long) As Long Public Declare Function LockResource Lib "kernel32.dll" (ByVal hResData As Long) As Long Public Declare Function SizeofResource Lib "kernel32.dll" _ (ByVal hModule As Long, ByVal hResInfo As Long) As Long Public Declare Function LookupIconIdFromDirectoryEx Lib "user32.dll" _ (ByVal pResBits As Long, ByVal fIcon As Long, ByVal cxDesired As Long, _ ByVal cyDesired As Long, ByVal Flags As Long) As Long Public Declare Function CreateIconFromResource Lib "user32.dll" _ (ByVal pResBits As Long, ByVal dwResSize As Long, ByVal fIcon As Long, _ ByVal dwVer As Long) As Long ' ここまで宣言部 ' ここから関数定義 Public Function MyEnumResNameProc(ByVal hInst As Long, ByVal lpszType As Long, _ ByVal lpszName As Long, ByRef Data As MyExtractIconData) As Long Dim ln As Long If Data.NowPos = Data.Index Then Data.Found = True Data.IsID = ((lpszName And &HFFFF0000) = 0) If Not Data.IsID Then ln = lstrlen(lpszName) Data.Name = VBA.Strings.String$(ln + 1, 0) Call CopyMemory(ByVal Data.Name, ByVal lpszName, ln) Data.Name = VBA.Strings.Left$(Data.Name, _ VBA.Strings.InStr(Data.Name, VBA.Chr$(0)) - 1) ' EnumResourceNames で MyExtractIconData を渡すときに ' 文字列の変換が発生するので、ここで対処しておく Data.Name = VBA.Strings.StrConv(Data.Name, vbFromUnicode) Else Data.ID = (lpszName And &HFFFF&) End If MyEnumResNameProc = 0 Exit Function End If Data.NowPos = Data.NowPos + 1 MyEnumResNameProc = 1 End Function Public Function MyExtractIcon(ByVal PathName As String, ByVal IndexOrID As Long, _ ByVal SmallIcon As Boolean) Dim hInstance As Long Dim hRes As Long, hRes2 As Long Dim hMem As Long, hMem2 As Long Dim lpv As Long, lpv2 As Long Dim Data As MyExtractIconData MyExtractIcon = 0 hInstance = LoadLibraryEx(PathName, 0, LOAD_LIBRARY_AS_DATAFILE Or _ LOAD_WITH_ALTERED_SEARCH_PATH) If hInstance = 0 Then Exit Function If IndexOrID < 0 Then Data.IsID = True Data.ID = -(IndexOrID + 1) Data.Found = True Else Data.Index = IndexOrID Data.NowPos = 0 Data.Found = False Call EnumResourceNames(hInstance, RT_GROUP_ICON, AddressOf MyEnumResNameProc, Data) If Not Data.Found Then Call FreeLibrary(hInstance) Exit Function End If End If If Data.IsID Then hRes = FindResource(hInstance, Data.ID, RT_GROUP_ICON) Else hRes = FindResource(hInstance, Data.Name, RT_GROUP_ICON) End If If hRes <> 0 Then hMem = LoadResource(hInstance, hRes) lpv = LockResource(hMem) Data.ID = LookupIconIdFromDirectoryEx(lpv, True, _ VBA.Interaction.IIf(SmallIcon, 16, 32), VBA.Interaction.IIf(SmallIcon, 16, 32), _ LR_DEFAULTCOLOR) If Data.ID <> 0 Then hRes2 = FindResource(hInstance, Data.ID, RT_ICON) hMem2 = LoadResource(hInstance, hRes2) lpv2 = LockResource(hMem2) MyExtractIcon = CreateIconFromResource(lpv2, _ SizeofResource(hInstance, hRes2), 1, &H30000) End If End If Call FreeLibrary(hInstance) End Function
※ [VB 6.0] ハンドルをオブジェクト(IPictureDisp、StdPicture など、ピクチャコントロールなどに設定できる型)にするには、「VB 6.0 における画像ハンドルの変換」を参照してください。
[VB.NET]
ついでに HICON → System.Drawing.Icon も行っています。
外部 DLL を呼び出すサンプルにひょっとしたらなるかもしれません
Imports System.Runtime.InteropServices Module IconTestModule Public Const LR_DEFAULTCOLOR As Integer = &H0I Public Const LR_MONOCHROME As Integer = &H1I ' MyExtractIconData 用のデータ (Structure より Class の方が楽) Public Class MyExtractIconData Public Index As Integer Public NowPos As Integer Public Found As Boolean Public IsID As Boolean Public ID As UInteger Public Name As String End Class Public Const DONT_RESOLVE_DLL_REFERENCES As Integer = &H1I Public Const LOAD_LIBRARY_AS_DATAFILE As Integer = &H2I Public Const LOAD_WITH_ALTERED_SEARCH_PATH As Integer = &H8I Public Const LOAD_IGNORE_CODE_AUTHZ_LEVEL As Integer = &H10I Public Declare Ansi Function LoadLibraryEx Lib "kernel32.dll" Alias "LoadLibraryExA" _ (ByVal lpLibFileName As String, ByVal hFile As System.IntPtr, _ ByVal dwFlags As Integer) As System.IntPtr Public Declare Ansi Function FreeLibrary Lib "kernel32.dll" _ (ByVal hModule As System.IntPtr) As Boolean Public Const RT_CURSOR As Integer = 1 Public Const RT_BITMAP As Integer = 2 Public Const RT_ICON As Integer = 3 Public Const RT_MENU As Integer = 4 Public Const RT_DIALOG As Integer = 5 Public Const RT_STRING As Integer = 6 Public Const RT_FONTDIR As Integer = 7 Public Const RT_FONT As Integer = 8 Public Const RT_ACCELERATOR As Integer = 9 Public Const RT_RCDATA As Integer = 10 Public Const RT_MESSAGETABLE As Integer = 11 Public Const RT_GROUP_CURSOR As Integer = 12 Public Const RT_GROUP_ICON As Integer = 14 Public Const RT_VERSION As Integer = 16 Public Const RT_DLGINCLUDE As Integer = 17 Public Const RT_PLUGPLAY As Integer = 19 Public Const RT_VXD As Integer = 20 Public Const RT_ANICURSOR As Integer = 21 Public Const RT_ANIICON As Integer = 22 Public Const RT_HTML As Integer = 23 Public Const RT_MANIFEST As Integer = 24 ' コールバック関数をデリゲートで宣言します。 Public Delegate Function EnumResNameProc(ByVal hInst As System.IntPtr, _ ByVal lpszType As System.IntPtr, ByVal lpszName As System.IntPtr, _ ByVal lParam As System.IntPtr) As Boolean Public Declare Ansi Function EnumResourceNames Lib "kernel32.dll" _ Alias "EnumResourceNamesA" (ByVal hModule As System.IntPtr, _ ByVal lpszType As System.IntPtr, ByVal lpEnumFunc As EnumResNameProc, _ ByVal lParam As System.IntPtr) As Boolean ' 以下の 2 つは数値でも文字列でも呼び出せるようにしています。 Public Declare Ansi Function FindResource Lib "kernel32.dll" Alias "FindResourceA" _ (ByVal hModule As System.IntPtr, ByVal lpName As System.IntPtr, _ ByVal lpType As System.IntPtr) As System.IntPtr Public Declare Ansi Function FindResource Lib "kernel32.dll" Alias "FindResourceA" _ (ByVal hModule As System.IntPtr, ByVal lpName As String, _ ByVal lpType As System.IntPtr) As System.IntPtr Public Declare Ansi Function LoadResource Lib "kernel32.dll" _ (ByVal hModule As System.IntPtr, ByVal hResInfo As System.IntPtr) As System.IntPtr Public Declare Ansi Function LockResource Lib "kernel32.dll" _ (ByVal hResData As System.IntPtr) As System.IntPtr Public Declare Ansi Function SizeofResource Lib "kernel32.dll" _ (ByVal hModule As System.IntPtr, ByVal hResInfo As System.IntPtr) As Integer Public Declare Ansi Function LookupIconIdFromDirectoryEx Lib "user32.dll" _ (ByVal pResBits As System.IntPtr, ByVal fIcon As Integer, ByVal cxDesired As Integer, _ ByVal cyDesired As Integer, ByVal Flags As Integer) As Integer Public Declare Ansi Function CreateIconFromResource Lib "user32.dll" _ (ByVal pResBits As System.IntPtr, ByVal dwResSize As Integer, ByVal fIcon As Integer, _ ByVal dwVer As Integer) As System.IntPtr Public Declare Auto Function DestroyIcon Lib "user32.dll" _ (ByVal hIcon As System.IntPtr) As Boolean Public Function MyEnumResNameProc(ByVal hInst As System.IntPtr, _ ByVal lpszType As System.IntPtr, ByVal lpszName As System.IntPtr, _ ByVal lParam As System.IntPtr) As Boolean Dim strName As String Dim ptr As GCHandle = GCHandle.FromIntPtr(lParam) Dim Data As MyExtractIconData Data = ptr.Target If Data.NowPos = Data.Index Then Data.Found = True Data.IsID = ((lpszName.ToInt32() And &HFFFF0000I) = 0) If Not Data.IsID Then strName = Marshal.PtrToStringAnsi(lpszName) Data.Name = strName Else Data.ID = (lpszName.ToInt32() And &HFFFFI) End If Return False End If Data.NowPos = Data.NowPos + 1 Return True End Function Public Function MyExtractIcon(ByVal PathName As String, ByVal IndexOrID As Integer, _ ByVal SmallIcon As Boolean) As System.IntPtr Dim hInstance As System.IntPtr Dim hRes As System.IntPtr, hRes2 As System.IntPtr Dim hMem As System.IntPtr, hMem2 As System.IntPtr Dim lpv As System.IntPtr, lpv2 As System.IntPtr Dim Data As MyExtractIconData, ptr As GCHandle hInstance = LoadLibraryEx(PathName, 0, LOAD_LIBRARY_AS_DATAFILE Or _ LOAD_WITH_ALTERED_SEARCH_PATH) If hInstance = 0 Then Return 0 Data = New MyExtractIconData If IndexOrID < 0 Then Data.IsID = True Data.ID = -(IndexOrID + 1) Data.Found = True Else Data.Index = IndexOrID Data.NowPos = 0 Data.Found = False ptr = GCHandle.Alloc(Data) Call EnumResourceNames(hInstance, RT_GROUP_ICON, AddressOf MyEnumResNameProc, ptr) Call ptr.Free() If Not Data.Found Then Call FreeLibrary(hInstance) Return 0 End If End If If Data.IsID Then hRes = FindResource(hInstance, CType(Data.ID, System.IntPtr), _ CType(RT_GROUP_ICON, System.IntPtr)) Else hRes = FindResource(hInstance, Data.Name, CType(RT_GROUP_ICON, System.IntPtr)) End If If hRes <> 0 Then hMem = LoadResource(hInstance, hRes) lpv = LockResource(hMem) Data.ID = LookupIconIdFromDirectoryEx(lpv, True, _ IIf(SmallIcon, 16, 32), IIf(SmallIcon, 16, 32), _ LR_DEFAULTCOLOR) If Data.ID <> 0 Then hRes2 = FindResource(hInstance, CType(Data.ID, System.IntPtr), _ CType(RT_ICON, System.IntPtr)) hMem2 = LoadResource(hInstance, hRes2) lpv2 = LockResource(hMem2) MyExtractIcon = CreateIconFromResource(lpv2, _ SizeofResource(hInstance, hRes2), 1, &H30000I) End If End If Call FreeLibrary(hInstance) End Function ' GetIconImageFromDLL は、DLL 名とインデックス (または絶対値が ID になる負の値) ' を指定するだけでアイコンオブジェクトを取得できます。 ' PathName を省略すると "shell32.dll" になります Public Function GetIconImageFromDLL(ByVal PathName As String, ByVal IndexOrID As Integer, _ Optional ByVal SmallIcon As Boolean = False) As System.Drawing.Icon Dim hIcon As System.IntPtr If PathName = Nothing OrElse PathName = "" Then PathName = "shell32.dll" hIcon = MyExtractIcon(PathName, IndexOrID, SmallIcon) If hIcon = 0 Then Return Nothing Else i = System.Drawing.Icon.FromHandle(hIcon) ' FromHandle では Dispose でアイコンを自動的に削除してくれないので作成しなおす i = i.Clone() Call DestroyIcon(hIcon) Return i End If End Function End Module