ひょんなことで始めた前回に続き、今回はMicrosoft Learningの参考(抜粋)コードを補完して、取り敢えず動くようにしてみます。その際、コードはそのままで、コメントのみ英語を日本語に変えてゆきます。(注)
注:前回触れたように、このオリジナルコードには問題があります。しかし、発展の過程をそのまま示すために、バグフィクスして訂正する版まではそのままにしておきます。
【オリジナル】
英文コメント付きのオリジナルです。
【ResourceList01.cpp】
・やたら出てくるTCHARというのは、Microsoft発案の「蝙蝠コード」で、UNICODEが未定義だとcharに、定義するとwcharにマクロ変換してくれます。
・"RT_"という接頭語が出てきますが、これはWin_RTとは何ら関係なく、"Resource Type"を意味していると思われます。
・伝統的なprintf(sprintf)、strlen、strcpy等の関数は使わず、strsafe.hのStringCchPrintfやStringCchLength、StringCchCopyを使っています。
・書き込みバッファーサイズ(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. 色付けが結構面倒なんで、次回からは変更部分とか、既に開設した所は省く等、手を抜く可能性があります。(笑;)