【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
ノシ
スポンサーリンク
関連記事