ひょんなことで始めた前回に続き、今回はMicrosoft Learningの参考(抜粋)コードを補完して、取り敢えず動くようにしてみます。その際、コードはそのままで、コメントのみ英語を日本語に変えてゆきます。(

前回触れたように、このオリジナルコードには問題があります。しかし、発展の過程をそのまま示すために、バグフィクスして訂正する版まではそのままにしておきます。

 

オリジナル

英文コメント付きのオリジナルです。

 

【ResourceList01.cpp】

・やたら出てくるTCHARというのは、Microsoft発案の「蝙蝠コード」で、UNICODEが未定義だとcharに、定義するとwcharにマクロ変換してくれます。

・"RT_"という接頭語が出てきますが、これはWin_RTとは何ら関係なく、"Resource Type"を意味していると思われます。

・伝統的なprintf(sprintf)、strlen、strcpy等の関数は使わず、strsafe.hのStringCchPrintfStringCchLengthStringCchCopyを使っています。

・書き込みバッファーサイズ(NULLを含む書き込み可能文字数)を算出する際に"sizeof(szBuffer) / sizeof(TCHAR)"を使っています。しかし、結果的にはANSIベースでの値は"4 / 1"になってしまいます。

//////////////////////////////////////////////////////////////////////////
//https://learn.microsoft.com/ja-jp/windows/win32/menurc/using-resources
//次の例では、Hand.exe ファイル内のすべてのリソースの一覧を作成します。
//リストは、Resinfo.txt ファイルに書き込まれます。
//////////////////////////////////////////////////////////////////////////
/* --- Microsoft Learn ---
このコードでは、実行可能ファイルを読み込み、リソース情報を書き込むファイル
を作成し、EnumResourceTypes関数を呼び出して、モジュールで見つかった各リ
ソースの種類をアプリケーション定義のコールバック関数EnumTypesFuncに送信す
る方法を示します。 この型のコールバック関数については、「EnumResTypeProc」
を参照してください。 このコールバック関数は、EnumResourceNames関数を使用
して、指定した型内のすべてのリソースの名前を別のアプリケーション定義のコー
ルバック関数EnumNamesFuncに渡します。 この型のコールバック関数については、
「EnumResNameProc」を参照してください。
EnumNamesFuncとEnumResourceLanguages関数を使用して、指定した型と名前のす
べてのリソースの言語を3番目のコールバック関数EnumLangsFuncに渡します。
この型のコールバック関数については、「EnumResLangProc」を参照してください。
EnumLangsFuncは、指定した型、名前、言語のリソースに関する情報をResinfo.
txtファイルに書き込みます。
EnumResTypeProcのlpszTypeは、リソースIDまたは文字列へのポインター(リソ
ースID または型名を含む) であることに注意してください。EnumResNameProcと
EnumResLangProcのlpszTypeとlpszNameは似ています。列挙されたリソースを
読み込むには、適切な関数を呼び出すだけです。たとえば、メニューリソース
(RT_MENU) が列挙された場合は、lpszNameをLoadMenuに渡します。カスタム
リソースの場合は、lpszTypeとlpszNameをFindResource に渡します。
<参考:リソース種類について>
https://learn.microsoft.com/ja-jp/windows/win32/menurc/resource-types
*/

#include    <windows.h>
#include    <strsafe.h>   //解説:(私も知らなかったのですが)これはMicrosoftがC++の文字列操作関数のバッファーオーバーランリスクを軽減するために開発したライブラリーのようです。
#include    <stdio.h>
#include    <conio.h>    //getch()使用の為

#define     UNICODE    0    //ANSI(TCHAR == char)

//コールバック関数のプロトタイプ宣言
BOOL EnumTypesFunc(HMODULE hModule, LPTSTR lpType, LONG lParam);
BOOL EnumNamesFunc(HMODULE hModule, LPCTSTR lpType, LPTSTR lpName, LONG lParam);
BOOL EnumLangsFunc(HMODULE hModule, LPCTSTR lpType, LPCTSTR lpName, WORD wLang, LONG lParam);

//外部変数
TCHAR g_FileName[MAX_PATH];        //リソースを取得するEXEファイル名(解説:これは追加しました。)
TCHAR g_ResFileName[MAX_PATH];    //リソースファイル名(拡張子前g_FileName + "_resinfo.txt")(解説:同上)
HANDLE g_hFile;                    //リソースファイルのグローバルハンドル

HMODULE g_hExe;                    //EXEファイルのハンドル(解説:正しくはDLL等を含む実行可能ファイル、ですね。)
TCHAR szBuffer[MAX_PATH];        //infoファイル用プリントバッファ(260です。オリジナルは"80"という定数です。)
DWORD cbWritten;                //resource infoファイルへの書き込みサイズ
size_t cbString;                //szBufferの文字列長(32bitではunsigned int(4bit)、64bitではunsigned long(8bit)
HRESULT hResult;

//main関数(エントリーポイント)-解説:引数無しmain関数を付加しました。
int main() {
    //対象ファイル名の指定(解説:二つの実行可能ファイルでテストしました。)
    //lstrcpy(g_FileName, TEXT("C:\\Users\\(省略)\\TextToSpeech\\Debug\\TextToSpeech.exe"));
    lstrcpy(g_FileName, TEXT("C:\\Users\\(省略)\\ResourceList\\Debug\\ResourceList01.exe"));
    //リソースを列挙するEXEファイルをロードする
    g_hExe = LoadLibrary(TEXT(g_FileName));
    if(g_hExe == NULL) {
        //(読み込み失敗時の処理を記述)
        printf("実行可能ファイルを読み込めません。");
        getch();    //コンソールを閉じない為
        return 1;
    }
    //リソース情報を収容するファイルを開く
    lstrcpyn(TEXT(g_ResFileName), TEXT(g_FileName), lstrlen(TEXT(g_FileName)) - 3);    //".exe"は4文字だが、lstrcpynはNULLを含むので(-4 + 1)となる
    lstrcat(TEXT(g_ResFileName), TEXT("_resinfo.txt"));
    g_hFile = CreateFile(TEXT(TEXT(g_ResFileName)),    //リソースファイル名
        GENERIC_READ | GENERIC_WRITE,                //アクセスモード
        0,                                            //シェアモード
        (LPSECURITY_ATTRIBUTES) NULL,                //デフォルトセキュリティ
        CREATE_ALWAYS,                                //フラグの作成
        FILE_ATTRIBUTE_NORMAL,                        //ファイルアトリビュート
        (HANDLE) NULL);                                //テンプレート無し
    if(g_hFile == INVALID_HANDLE_VALUE) {
        //ErrorHandler(TEXT("リソースファイルを作成できませんでした。"));-解説:↓の通り単なるprintf文にしました。
        printf(TEXT("リソース情報ファイルを作成できませんでした。"));
        getch();    //コンソールを閉じない為
        return 1;
    }
    //ロードされたファイルのすべてのリソースを読み込む(解説:単にwsprintfと同じ処理なので、このコメントは正しくないですね。)
    hResult = StringCchPrintf(szBuffer, sizeof(szBuffer) / sizeof(TCHAR),
        TEXT("%s\r\nのファイルには以下のリソースがあります。\r\n"), g_FileName);
//        TEXT("このファイルには以下のリソースがあります。\r\n"));(解説:ファイル名を表示させました。)
    if(FAILED(hResult)) {
        //(読み込み失敗時の処理を記述)
        printf("実行可能ファイルのリソースを読み込めません。");
        getch();    //コンソールを閉じない為
        return 1;
    }
    hResult = StringCchLength(szBuffer, sizeof(szBuffer) / sizeof(TCHAR), &cbString);
    if(FAILED(hResult)) {
        //(読み込み失敗時の処理を記述)
        printf("文字列が指定した長さを超えました。");
        getch();    //コンソールを閉じない為
        return 1;
    }
    WriteFile(g_hFile,            //resource infoを書き込むファイルハンドル
        szBuffer,                //書き込みバッファー
        (DWORD)cbString,        //szBufferの書き込みサイズ
        &cbWritten,                //書き込みバイト数
        NULL);                    //オーバーラップドI/Oは無し
    EnumResourceTypes(g_hExe,            //読み込みモジュールのハンドル
        (ENUMRESTYPEPROC)EnumTypesFunc,    //コールバックファンクションアドレス
        0);                                //補足パラメーター
    //ロードして、リソースを列挙したた実行可能ファイルを
    //アンロードし、リソース情報ファイルを閉じる。

    FreeLibrary(g_hExe);
    CloseHandle(g_hFile);
    printf("リソース情報ファイルを書き出しました。");
    getch();                    //コンソールを閉じない為
    return 0;
}

//リソース種類取得用コールバック関数
//EnumTypesFunc(HANDLE, LPSTR, LONG)

BOOL EnumTypesFunc(HMODULE hModule, LPTSTR lpType, LONG lParam) {
    TCHAR szBuffer[MAX_PATH];        //リソース情報用印字バッファー
    DWORD cbWritten;        //リソース情報ファイル書き込みバイト数
    size_t cbString;
    HRESULT hResult;

    //リソース情報ファイルにリソース種類を書き込む
    //種類は文字列または符号なし整数であり、事前にテストを行う

    if(!IS_INTRESOURCE(lpType)) {
        hResult = StringCchPrintf(szBuffer, sizeof(szBuffer) / sizeof(TCHAR), TEXT("種類:%s\r\n"), lpType);
        if (FAILED(hResult)) {
            //(読み込み失敗時の処理を記述)
            return FALSE;
        }
    }
    else
    {
        hResult = StringCchPrintf(szBuffer, sizeof(szBuffer) / sizeof(TCHAR), TEXT("種類:%u\r\n"), (USHORT)lpType);
        if (FAILED(hResult)) {
            //(読み込み失敗時の処理を記述)
            return FALSE;
        }
    }
    hResult = StringCchLength(szBuffer, sizeof(szBuffer)/sizeof(TCHAR), &cbString);
    if(FAILED(hResult)) {
        //(読み込み失敗時の処理を記述)
        return FALSE;
    }
    WriteFile(g_hFile, szBuffer, (DWORD) cbString, &cbWritten, NULL);
    //同種のリソースを総て検索する
    EnumResourceNames(hModule, lpType, (ENUMRESNAMEPROC)EnumNamesFunc, 0);
    return TRUE;
}

//リソース名取得用コールバック関数
//EnumNamesFunc(HANDLE, LPSTR, LPSTR, LONG)

 BOOL EnumNamesFunc(HMODULE hModule, LPCTSTR lpType, LPTSTR lpName, LONG lParam) {
    TCHAR szBuffer[MAX_PATH];        //リソース情報用印字バッファー
    DWORD cbWritten;        //リソース情報ファイル書き込みバイト数
    size_t cbString;
    HRESULT hResult;

    //リソース情報ファイルにリソース名を書き込む
    //リソース名は文字列または符号なし整数であり、事前にテストを行う

    if(!IS_INTRESOURCE(lpName)) {
        hResult = StringCchPrintf(szBuffer, sizeof(szBuffer) / sizeof(TCHAR), TEXT("\tリソース名:%s\r\n"), lpName);
        if(FAILED(hResult)) {
            //(読み込み失敗時の処理を記述)
            return FALSE;
        }
    }
    else
    {
        hResult = StringCchPrintf(szBuffer, sizeof(szBuffer) / sizeof(TCHAR), TEXT("\tリソース名:%s\r\n"), (USHORT)lpName);
        if(FAILED(hResult)) {
            //(読み込み失敗時の処理を記述)
        }
    }
    hResult = StringCchLength(szBuffer, sizeof(szBuffer) / sizeof(TCHAR), &cbString);
    if(FAILED(hResult)) {
            //(読み込み失敗時の処理を記述)
        return FALSE;
    }
    WriteFile(g_hFile, szBuffer, (DWORD)cbString, &cbWritten, NULL);
    //同種のリソースの言語を総て検索する
    // Find the languages of all resources of type

    EnumResourceLanguages(hModule, lpType, lpName, (ENUMRESLANGPROC)EnumLangsFunc, 0);
    return TRUE;
}

//リソース言語取得用コールバック関数EnumLangsFunc(HANDLE, LPSTR, LPSTR, WORD, LONG)
BOOL EnumLangsFunc(HMODULE hModule, LPCTSTR lpType, LPCTSTR lpName, WORD wLang, LONG lParam) {
    HRSRC hResInfo;            //リソース情報ハンドル
    TCHAR szBuffer[MAX_PATH];        //リソース情報用印字バッファー
    DWORD cbWritten;        //リソース情報ファイル書き込みバイト数
    size_t cbString;
    HRESULT hResult;

    hResInfo = FindResourceEx(hModule, lpType, lpName, wLang);
    //リソース情報ファイルへリソース情報を書き込む
    hResult = StringCchPrintf(szBuffer, sizeof(szBuffer) / sizeof(TCHAR), TEXT("\t\tリソース言語: %u\r\n"), (USHORT) wLang);
    if(FAILED(hResult)) {
        //(読み込み失敗時の処理を記述)
        return FALSE;
    }
    hResult = StringCchLength(szBuffer, sizeof(szBuffer) / sizeof(TCHAR), &cbString);
    if(FAILED(hResult)) {
        //(読み込み失敗時の処理を記述)
        return FALSE;
    }
    WriteFile(g_hFile, szBuffer, (DWORD) cbString, &cbWritten, NULL); 
    //リソースハンドルとサイズをバッファーに書き込む
    hResult = StringCchPrintf(szBuffer, sizeof(szBuffer) / sizeof(TCHAR), TEXT("\t\tリソースハンドル:%lx、サイズ:%lu\r\n\r\n"), hResInfo, SizeofResource(hModule, hResInfo));
    if(FAILED(hResult)) {
        //(読み込み失敗時の処理を記述)
        return FALSE;
    }
    hResult = StringCchLength(szBuffer, sizeof(szBuffer) / sizeof(TCHAR), &cbString);
    if (FAILED(hResult))
    {
        //(読み込み失敗時の処理を記述)
        return FALSE;
    }
    WriteFile(g_hFile, szBuffer, (DWORD)cbString, &cbWritten, NULL);
    return TRUE;
}

 

全体を概観すると、対象となる実行可能プログラムをロードすると、リソース情報ファイルを作成して書き込みを開始します。情報は専用のコールバック関数を使い、種類(Type)、名前(Name)、言語等(Language etc)を順次出力ファイルに書き込んでゆき、書き込みが完了すると、ファイルを閉じ、ロードした実行可能プログラムを開放して、main関数を終了します。出力されたリソース情報ファイルは次のようになります。

 

ではでは。

 

ps. 色付けが結構面倒なんで、次回からは変更部分とか、既に開設した所は省く等、手を抜く可能性があります。(笑;)