依存関係を極限まで排除した軽量 EMF クラスの作成と利用
Windows アプリケーションにおいて、GDI API を活用した描画プログラムを構築する際、外部ライブラリへの依存や複雑なプロジェクト設定は、開発のスピードを鈍らせる要因となります。特に、一時的な実験や小規模なツール開発においては、必要な機能を必要な分だけ、極めてシンプルに実装できることが重要です。
本記事では、EMF (Enhanced Metafile) を用いた描画機能のうち、核心となる操作部分を切り出し、他プロジェクトから完全に独立した「単体コード(ヘッダーファイル)」として再構築しました。プロジェクトにコピー&ペーストするだけで即座に利用できる、軽量かつポータブルな EMF 描画クラスの実装と、その活用方法を紹介します。
1. 最小構成の EMF クラス: E_MetaF.inc
外部ヘッダーへの依存を一切排除し、標準的な C++ と Windows API のみで実装したクラスです。RAII パターンを採用し、CreateEnhMetaFile と CloseEnhMetaFile をスマートに管理します。
// 最小構成の EMF 作成・表示クラス
#pragma once
#include <windows.h>
#include <string>
class E_MetaF {
private:
HDC m_hdc;
HENHMETAFILE m_hemf;
std::wstring m_path;
public:
E_MetaF(const std::wstring& path) : m_path(path), m_hemf(NULL) {
m_hdc = ::CreateEnhMetaFileW(NULL, path.c_str(), NULL, NULL);
}
HDC Get_HDC() const {
return m_hdc;
}
void Close() {
if (m_hdc) {
m_hemf = ::CloseEnhMetaFile(m_hdc);
m_hdc = NULL;
}
}
static void Play(HWND hwnd, const std::wstring& path) {
HENHMETAFILE hemf = ::GetEnhMetaFileW(path.c_str());
if (hemf) {
HDC hdc = ::GetDC(hwnd);
if (hdc) {
RECT rect;
::GetClientRect(hwnd, &rect);
::PlayEnhMetaFile(hdc, hemf, &rect);
::GdiFlush(); // 描画命令の確定
::ReleaseDC(hwnd, hdc);
}
::DeleteEnhMetaFile(hemf);
}
}
~E_MetaF() {
if (m_hdc) {
::CloseEnhMetaFile(m_hdc);
}
if (m_hemf) {
::DeleteEnhMetaFile(m_hemf);
}
}
};
2. コンソールアプリケーションでの活用
コンソール環境(特に Windows 11 のモダンなターミナル統合環境)では、描画タイミングが重要です。起動直後の描画を安定させるために、Sleep を用いて初期化の競合を回避しています。
// T_EMF_2.cpp
#include <iostream>
#include "E_MetaF.inc"
int main() {
::Sleep(10); // Windows 11 環境での表示安定化用
std::wstring path = L"TEST.EMF";
{
E_MetaF emf(path);
HDC hdc = emf.Get_HDC();
if (hdc) {
::Rectangle(hdc, 10, 10, 100, 100);
::TextOutW(hdc, 20, 20, L"EMF Test", 8);
emf.Close();
}
}
{
E_MetaF::Play(::GetConsoleWindow(), path);
}
std::wcout << L"Press Enter to exit." << std::endl;
std::cin.get();
return 0;
}
3. MFC ダイアログでの活用
MFC アプリケーションで EMF を描画する場合は、OnPaint() ハンドラで CPaintDC を利用するのが最も標準的かつ堅牢です。これにより、OS からの再描画命令(WM_PAINT)に対して確実にメタファイルを表示できます。
void CDrawEMFDlg::OnPaint() {
// ... 省略 ...
else {
CPaintDC dc(this);
// EMF をダイアログのクライアント領域に描画
E_MetaF::Play(this->GetSafeHwnd(), L"TEST.EMF");
}
}
補足:ダイアログベースでの基本的な描画手法については、以下の過去記事もあわせて参考にしてください。
MFC ダイアログに描画 - mish.work
4. まとめとソースコード
今回の E_MetaF クラスは、最小限の依存関係で、コンソールアプリから MFC アプリまで、環境を問わず安定した EMF 描画を実現します。
描画ロジックの途中の状態をメタファイルとして逐次出力できるため、デバッグツールとしても重宝します。
今回使用したビルド可能なソース一式は以下からダウンロードできます。
ぜひ、お手元の環境で試してみてください。
【2026-06-30 更新】リファクタリングによる改善のご案内
本記事のコードを見直し、依存関係を極限まで減らした「疎結合な実装」および不具合修正を行った最新版をブログにて公開しました。
ブログ記事:EMF メタファイル操作クラス「E_MetaF」のリファクタリング:不具合修正と疎結合化の実践
※ 配布しているソースコード一式(ZIP)には参考情報が含まれていますが、コードの利用は自己責任にてお願いいたします。
※ 本記事の構成および文章の作成には、生成 AI を利用しています。
