SDK Index Previous page Next page

実行ファイルからアイコンを取り出す


はじめに

実行ファイルに含まれているアイコンを取り出して、アイコンファイルとして保存する方法を説明します。

今回のサンプルファイルです。ウインドウにファイルをドロップするとその中のアイコンをicon???.icoという名前で保存します。

実行ファイル内のアイコン

アイコンは、リソースデータとして実行ファイル内に入っています。基本的にアイコンファイルと同じデータが入っているだけなので、リソース内の位置を特定してファイルに保存するだけで可能です。 ただし、アイコンは複数の大きさや色のデータを持つ事もあるので、それらをすべて集める必要があります。このように複数のデータを持つアイコンをグループアイコンと呼びます。このとき、1つしかデータを持たない場合にも、データが1つのグループアイコンとしてあつかわれています。

次に、アイコンを保存する手順を示します。

  1. 実行ファイルを読み込む〔LoadLibraryEx〕

  2. グループアイコンを列挙する〔EnumResourceNames〕

  3. グループアイコンに含まれているアイコンを集める
    〔FindResource、LoadResource、LockResource〕

  4. それらをまとめてファイルに保存する〔WriteFile〕

実行ファイルの読み込み

実行ファイルを読み込むにはLoadLibraryではなくLoadLibraryExを使います。こちらの関数を使わないと、エントリーポイントのないファイル、つまりリソースだけでプログラムのないファイルを読み込むことが出来ません。

SaveIcons.c内のSaveIcons関数で

hFile=LoadLibraryEx(lpFileName,NULL,LOAD_LIBRARY_AS_DATAFILE | LOAD_WITH_ALTERED_SEARCH_PATH);
として、読み込んでいます。

グループアイコンの列挙

次に、グループアイコンを列挙していきます。リソースの列挙にはEnumResourceNamesを使います。Enumで始まる関数にはコールバック関数が必要なので、それも作っておきます。コールバック関数内では、GetRsrc.c内の関数GetGroupIconDataを呼んで完全なアイコンデータを作って、ファイルに保存しています。
//アイコンの列挙に使われるコールバック関数

static BOOL CALLBACK EnumResNameProc(HANDLE hModule,LPCTSTR lpszType,LPTSTR lpszName,LONG lParam)
{
    DWORD       Size,wSize;
    PBYTE       pMem;
    char        IcoName[MAX_PATH];
    HANDLE      hFile;

    //lpszNameにアイコンリソース名が入っているので、そのアイコンのデータを取得
    Size=GetGroupIconData(hModule,lpszName,&pMem);
    if(Size){
        wsprintf(IcoName,"icon%03d.ico",g_Index);
        hFile=CreateFile(IcoName,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
        if(hFile){
            WriteFile(hFile,pMem,Size,&wSize,NULL);
            CloseHandle(hFile);
            g_Index++;
        }
        GlobalFree(pMem);
    }
    return  TRUE;
}

グループアイコン内のアイコンを集める

グループアイコンリソースに、必要なアイコンの情報が格納されているので、それを一つ一つ調べて、アイコンを集めていきます。このプログラムでは、アイコン情報の構造体ICONRESHEADを定義して分かりやすくしています。ちなみに、unknownとなっているのは、何のデータか分からないところです。
次のソースで、だいたいの流れが分かると思います。
//グループアイコン内の全アイコンデータを取得する
//   HANDLE  hFile   モジュールハンドル
//   LPTSTR  lpStr   リソースの名前
//   =>PBYTE         データバッファアドレス(要:GlobalFree)

DWORD GetGroupIconData(HANDLE hFile,LPTSTR lpStr,PBYTE *pRetMem)
{
    HANDLE          hGIcon,hLoadGIcon;
    DWORD           AllSize;
    PBYTE           pMem;
    LPICONRESHEAD   lpIconResHead;

    *pRetMem=NULL;

    //グループアイコンリソースを検索
    hGIcon=FindResource(hFile,lpStr,RT_GROUP_ICON);
    if(hGIcon==NULL) return 0;

    //見つけたリソースをロード
    hLoadGIcon=LoadResource(hFile,hGIcon);
    if(hLoadGIcon==NULL) return 0;
    
    //リソースをロックして、メモリアドレスを得る
    lpIconResHead=(LPICONRESHEAD)LockResource(hLoadGIcon);
    
    //アイコン全体のサイズを計算する
    AllSize=GetGroupIconSize(lpIconResHead);
    
    pMem=(PBYTE)GlobalAlloc(GMEM_FIXED,AllSize);
    if(pMem==NULL) return 0;
    
    //全てのアイコンをコピー
    if(StoreAllIconData(hFile,lpIconResHead,pMem)==FALSE){
        GlobalFree(pMem);
        return  0;
    }

    *pRetMem=pMem;

    return  AllSize;
}

アイコン全体のサイズの計算

グループアイコン全体のサイズを知る関数がなかったので、ICONRESHEAD内の各アイコンのサイズを合計して求めています。
//グループアイコンの格納に必要なサイズを取得する

static DWORD GetGroupIconSize(LPICONRESHEAD lpIconResHead)
{
    int     i,num;
    DWORD   Size;

    num=(int)lpIconResHead->num;

    //使えそうなAPIがないので、それぞれのアイコンのサイズを合計して
    //全体の大きさを求める
    Size=sizeof(ICONFILEHEAD)+sizeof(ICONFILEINF)*num;
    for(i=0;i<num;i++){
        Size+=lpIconResHead->IconInf[i].size;
    }

    return  Size;
}

リソースからアイコンファイルへ

ICONRESHEAD内には、各アイコンのリソースIDが入っているので、そのリソースを探してコピーしていけばいいのですが、リソースとアイコンファイルではヘッダの構造が少し違っているので、変換が必要になります。

変換が必要なのはICONFILEINF構造体のDWORD adr;の部分で、リソースヘッダではこの部分にはリソースIDが入っていますが、アイコンファイルではここに各アイコンデータがファイルの先頭から何バイト目にあるかという情報を入れます。
その他は同じなので、そのままコピーしています。unknownについては使われてなさそうなのでコピーしていません。ただし、ICONFILEHEADのunknown3は、1にするみたいです(経験則)。

ヘッダを初期化し、そのうしろに各アイコンのデータをコピーしていきます。このときに、先ほどのadrを設定します。 これでアイコンファイルの形になるので、後はファイルに保存して終わりです。

//グループアイコン内の全てのアイコンをバッファにコピーする

static BOOL StoreAllIconData(HANDLE hFile,LPICONRESHEAD lpIconResHead,PBYTE pMem)
{
    int             i,num,HeadSize,IconSize;
    PBYTE           pMemTmp;
    LPICONFILEHEAD  lpIconFileHead;

    //グループアイコン内のアイコンの総数
    num=(int)lpIconResHead->num;

    lpIconFileHead=(LPICONFILEHEAD)pMem;
    //保存するアイコンファイルのヘッダサイズの計算
    HeadSize=sizeof(ICONFILEHEAD)+sizeof(ICONFILEINF)*num;
    ZeroMemory(lpIconFileHead,HeadSize);
    lpIconFileHead->num=num;
    lpIconFileHead->unknown3=1;
    
    pMemTmp=pMem+HeadSize;

    //アイコンを列挙する
    //リソースとアイコンファイルで、少し構造体の中身が違うので変換しつつコピー
    for(i=0;i<num;i++){
        //各アイコンデータを取得
        IconSize=StoreIconData(hFile,lpIconResHead->IconInf[i].ID,pMemTmp);
        if(IconSize==0) return  FALSE;
        //ヘッダ情報の更新
        lpIconFileHead->IconInf[i].cx=lpIconResHead->IconInf[i].cx;
        lpIconFileHead->IconInf[i].cy=lpIconResHead->IconInf[i].cy;
        lpIconFileHead->IconInf[i].col=lpIconResHead->IconInf[i].col;
        lpIconFileHead->IconInf[i].size=IconSize;
        //アイコンデータのあるアドレス
        lpIconFileHead->IconInf[i].adr=pMemTmp-pMem;
        pMemTmp+=IconSize;
    }

    return  TRUE;
}

おわりに

基本的に、アイコン以外のリソースもこの方法で取り出すことが出来ます。といってもあとはビットマップリソースぐらいしか意味はないですが....


to sdk prev next