【Windows API】リソースをバイナリで出力する方法
Windows向けにプログラムを書いているときに、とあるやりたいことに出くわしました。
内容は以下のようなことです。
やりたいこと
実行ファイルAを実行すると、デスクトップに実行ファイルBを作成する。
ただし、作成する実行ファイルBは、元の実行ファイルAがリソースとして保持していなければならない。
@イメージ図
結論から言うと、この問題は解決できました!
なかなかピンポイントかつマニアックな内容ですが、この記事で解説しようと思います(^o^)
目次
スポンサーリンク
方針
まず、実行ファイルBを実行ファイルAにリソースとして保持させます。
実行ファイルAは内部リソースの実行ファイルBを、バイナリ形式で読み取り、新しくファイルを作り、その中にfwriteで書き込みます。
これで、解決できました(・o・)
解説
具体的に解説していこうと思います。
実行ファイルBを作成
まず、サンプルとして実行ファイルBを作成します。
ファイルの内容は以下の通りです。
// 【実行ファイルB】 // メッセージボックスで //「実行ファイルBです!」と表示する #define _CRT_SECURE_NO_WARNINGS #include <Windows.h> // WIndowsプログラムのエントリーポイント int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { // メッセージボックス MessageBox( NULL, L"実行ファイルBです!", L"実行ファイルB", MB_ICONINFORMATION | MB_OK); return 0; }
実行ファイルBは、クリックすると「実行ファイルBです!」というメッセージボックスが現れるという単純なプログラムです。
実行ファイルAを作成
次に、本題の実行ファイルAの作成に取り掛かります。
まずは、リソースに実行ファイルBを持たせましょう。リソースファイルとリソースヘッダに以下の記述を追加します。(リソースファイルとリソースヘッダの書き方がわからない方は調べてみてください!)
リソースファイルに以下の記述を追加します。
IDR_EXE1 EXE "exeB.exe"
リソースの名前(ID)はIDR_EXE1、リソースの種類はEXEにします。最後に、実行ファイルBの名前はexeB.exeなので、”exeB.exe”と記述します。
リソースヘッダに以下の記述を追加します。
#define IDR_EXE1 101
IDR_EXE1の値は101と定義します。
最後に、main関数の記述を書きます!
// 【実行ファイルA】 // デスクトップに実行ファイルBを作成する #define _CRT_SECURE_NO_WARNINGS #include <Windows.h> #include <stdio.h> #include "resource.h" // WIndowsプログラムのエントリーポイント int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { // ---------- リソースに関して ---------- // // リソースのハンドルを取得(リソースの場所を特定) const HRSRC hResInfo = FindResource( hCurInst, // モジュール(リソース)のハンドル MAKEINTRESOURCE(IDR_EXE1), // リソースの名前 L"EXE"); // リソースの種類 // リソースを読みだす(リソースに関連付けられたデータのハンドルを取得) const HGLOBAL hResData = LoadResource( hCurInst, // モジュール(リソース)のハンドル hResInfo); // リソースのハンドル // リソースデータへのポインタを取得 const LPVOID pvResData = LockResource(hResData); // ------- ファイルの作成に関して -------- // // ファイルポインタ FILE *fp; // ファイルオープン(書き込みモードでオープン) fp = fopen( "C:\\Users\\meideru\\Desktop\\exeB.exe", // exeB.exeを作成するパス "wb"); // 書き込みモード if (fp == NULL) // ファイルオープンできなかった { return 0; } // リソースの容量を取得 DWORD ResSize = SizeofResource( hCurInst, // モジュール(リソース)のハンドル hResInfo); // リソースのハンドル // ファイルの書き込み fwrite( pvResData, // 書き込むファイルのポインタ ResSize, // 書き込むファイルのサイズ 1, // 書き込むファイルの個数 fp); // 書き込み先のファイルポインタ // ファイルクローズ fclose(fp); return 0; }
上のソースを用いて少し、具体的に説明します。
初めに、リソースヘッダをインクルードしてください。忘れると実行できません^^;
#include "resource.h"
リソースハンドルを取得します。これは、リソースの場所を特定しているのに等しいです。
// リソースのハンドルを取得(リソースの場所を特定) const HRSRC hResInfo = FindResource( hCurInst, // モジュール(リソース)のハンドル MAKEINTRESOURCE(IDR_EXE1), // リソースの名前 L"EXE"); // リソースの種類
モジュール(リソース)のハンドルは、winmain関数の引数と同じものでよいです。
それと、本来ならば、MAKEINTRESOURCE(IDR_EXE1)の代わりにL”IDR_EXE1″としても良いはずなのですが、これをやると何故か上手く動作しませんでした。デバッグして確認したところ、リソースのハンドルの場所がNULLで返ってきてしまいました。原因は不明です^^;
もし知っている方がいれば、是非ともコメントで教えてください^^;
リソースを読み出します。
// リソースを読みだす(リソースに関連付けられたデータのハンドルを取得) const HGLOBAL hResData = LoadResource( hCurInst, // モジュール(リソース)のハンドル hResInfo); // リソースのハンドル
リソースに関連付けられたデータのハンドルを取得しています。
リソースデータのポインタを取得します。
// リソースデータへのポインタを取得 const LPVOID pvResData = LockResource(hResData);
これで、pvResDataにリソースデータのポインタ変数が格納されます。
以上で完成です!
ビルドして実行ファイルA実行すると、無事、デスクトップに実行ファイルBが作成されるはずです。
最後に・・・
ちょっと話は変わります。勘の良い方はあることに、気づかれたかと思います。それは、「メモリの開放」についてです。
LoadResourceでリソースを読みだし、LockResourceでリソースデータへのポインタを取得した時点で、メモリを動的に確保していることになりますよね。
動的にメモリを確保したときは、プログラマがメモリを開放しなければいけません。なのに、上のプログラムだと開放していません。
結論から言うと、win32apiでは開放しなくて良いそうです。自動でやってくれるとMSDNに書いてありました。
以下はコピペです。
Win32 アプリケーションでは、LoadResource 関数でロードしたリソースを解放する必要がありません。LoadResource 関数で取得したリソースは、それをロードしたモジュールがアンロードされると自動的に解放されます。
出典:https://msdn.microsoft.com/ja-jp/library/cc364598.aspx
ノシ
スポンサーリンク
関連記事