レコードの列挙


レコード単位の処理

メタファイルは、GDI 関数の処理をレコード単位で格納したものです
通常の使い方では PlayEnhMetaFile() 関数などで一度にそれを再生しますが
条件によっては、一部の処理を変更したり監視する必要があるかもしれません

これらの変更は、バイナリ単位で行うことも可能です
しかし、そのためには完全にメタファイルの仕様を熟知している必要があるので
あまり推奨される方法ではありませんし、生産性もありません

Windows は単一のレコードにアクセスする手段を提供しています
もちろん、この場合でも最低限のメタファイルの仕様を知っている必要があります

レコード単位にアクセスするには、再生時にレコードを列挙します
レコードを列挙するには EnumEnhMetaFile() 関数を使います
BOOL EnumEnhMetaFile(
	HDC hdc , HENHMETAFILE hemf,
	ENHMFENUMPROC lpEnhMetaFunc,
	LPVOID lpData , CONST RECT *lpRect
);
hdc にはデバイスコンテキストのハンドルを
hemf には拡張メタファイルのハンドルを指定します

lpEnhMetaFunc は、各レコード単位で呼び出されるユーザー定義の
コールバック関数へのポインタを指定します
レコードの最初から順に、レコードの情報と共にこの関数が呼び出されます
関数のポインタは ENHMFENUMPROC 型のものでなければなりません(下記)

lpdata は、コールバック関数に送信する追加情報を
lpRect は、画像の長方形を論理座標で表した RECT 構造体へのポインタを指定します
全ての列挙が成功した時は TRUE、そうでなければ FALSE が返ります

この関数で指定する ENHMFENUMPROC 型のポインタは
レコード毎に呼び出される、次のようなユーザー定義関数です
int CALLBACK EnhMetaFileProc(
	HDC hDC , HANDLETABLE FAR *lpHTable ,
	ENHMETARECORD FAR *lpEMFR ,
	int nObj , LPARAM lpData
);
hDC には拡張メタファイルのデバイスコンテキストを
lpHTable はメタファイル内の GDI オブジェクトの配列テーブルへのポインタです
この配列は先頭に拡張メタファイルのハンドル、それから順に GDI オブジェクトがあります
HANDLETABLE 構造体は次のように定義されています
typedef struct tagHANDLETABLE { // ht 
    HGDIOBJ objectHandle[1]; 
} HANDLETABLE;
objectHandle は GDI オブジェクトのハンドルを格納する配列です
この定義では 1 つの要素になっていますが、当然これは可変の配列です

lpEMFR には、メタファイル内で次に処理するべきレコードのポインタを指定します
バイナリレベルでの仕様を理解していない限り、このデータを編集してはいけません
ENHMETARECORD 構造体は次のように定義されています
typedef struct tagENHMETARECORD { // enmr 
    DWORD iType; 
    DWORD nSize; 
    DWORD dParm[1]; 
} ENHMETARECORD;
iType には、メタデータが呼び出すべき GDI 関数を表した定数が格納されています
GDI 関数の情報以外にも、メタデータの終了やヘッダなどを表すこともあります

nSize には、可変サイズである dParm メンバのサイズを表しています
dParm は1つの要素を持つ配列で宣言されていますが、実践では可変です
ここに、情報や関数に渡すべき引数などが格納されるのです

さて、コールバック関数の引数の説明に戻ります
nObj は、lpHandle の配列の個数を、すなわち GDI オブジェクト個数を指定します
最後の、lpData には、EnumEnhMetaFile() 関数の lpData に指定した値が渡されます
列挙を継続する時は 0 以外、列挙を終了するならば 0 を指定します

あなたが、完全にメタファイルの仕様をしているのならば、これだけ情報があれば
もはや何の助けがなくても、拡張メタファイルを再生することができるでしょう
しかし、そのような頭の痛くなるようなことをしなくてもレコードを処理することができます
レコード単位で再生するには PlayEnhMetaFileRecord() を使います
BOOL PlayEnhMetaFileRecord(
	HDC hdc , LPHANDLETABLE lpHandletable,
	CONST ENHMETARECORD *lpEnhMetaRecord,
	UINT nHandles
);
hdc にはデバイスコンテキストのハンドルを
lpHandletable には、ハンドルテーブルのポインタを指定します
lpEnhMetaRecord は、再生するべき拡張メタファイルのポインタを指定します
nHandles には、lpHandletable が保有するハンドルの個数を指定します
関数が成功すれば 0 以外、失敗すれば 0 が返ります
#include <windows.h>

#define TITLE TEXT("Kitty on your lap")
PSTR strFileName;

int CALLBACK EnhMetaFileProc(HDC hDC , HANDLETABLE FAR *lpHTable ,
		ENHMETARECORD FAR *lpEMFR , int nObj , LPARAM lpData) {
	TCHAR strData[1024];
	wsprintf(strData , 
		TEXT("TYPE = %X\n処理を続けますか?") , lpEMFR->iType);
	if (MessageBox((HWND)lpData , strData ,
		TEXT("選択してください") , MB_YESNO) == IDNO) return FALSE;
	PlayEnhMetaFileRecord(hDC , lpHTable , lpEMFR , nObj);
	return TRUE;
}

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	HDC hdc;
	PAINTSTRUCT ps;
	RECT rect;
	HENHMETAFILE hMeta;
	static BOOL blPlay = TRUE;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hWnd , &ps);
		GetClientRect(hWnd , &rect);
		hMeta = GetEnhMetaFile(strFileName);
		if (hMeta && blPlay) {
			EnumEnhMetaFile(hdc , hMeta ,
				(ENHMFENUMPROC)EnhMetaFileProc ,
				(LPVOID)hWnd , &rect);
			DeleteEnhMetaFile(hMeta);
			blPlay = FALSE;
		}

		EndPaint(hWnd , &ps);
		return 0;
	}
	return DefWindowProc(hWnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow) {
	HWND hWnd;
	MSG msg;
	WNDCLASS winc;

	strFileName = lpCmdLine;

	winc.style 		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return 1;

	hWnd = CreateWindow(
			TEXT("KITTY") , TITLE ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			200 , 100 ,
			NULL , NULL , hInstance , NULL
	);

	if (hWnd == NULL) return 1;

	while (GetMessage(&msg , NULL , 0 , 0 )) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return msg.wParam;
}


このプログラムは、コマンドライン引数から指定された拡張メタファイルを読み込み
その拡張メタファイルに記されているレコードを一つづつ再生します
この時、レコードを処理する度にダイアログを表示して実行するか許可を求めます
このダイアログには、レコードが処理する内容を表す定数も表示します


EnumEnhMetaFile()

BOOL EnumEnhMetaFile(
	HDC hdc , HENHMETAFILE hemf,
	ENHMFENUMPROC lpEnhMetaFunc,
	LPVOID lpData , CONST RECT *lpRect
);
拡張メタファイルをレコード単位で列挙してコールバック関数を呼び出します

hdc - デバイスコンテキストのハンドルを指定します
hemf - 拡張メタファイルのハンドルを指定します
lpEnhMetaFunc - コールバック関数へのポインタを指定します
lpData - コールバック関数に送信する追加情報を指定します
lpRect - 拡張メタファイルの大きさを表す RECT 構造体へのポインタを指定します

戻り値 - 全ての列挙に成功すると TRUE、そうでなければ FALSE

EnhMetaFileProc()

int CALLBACK EnhMetaFileProc(
	HDC hDC , HANDLETABLE FAR *lpHTable ,
	ENHMETARECORD FAR *lpEMFR ,
	int nObj , LPARAM lpData
);
EnumEnhMetaFile() 関数でレコードを列挙する時
レコード毎に呼び出されるユーザー定義のコールバック関数です

hDC - デバイスコンテキストのハンドルを指定します
lpHTable - ハンドルテーブルへのポインタを指定します
lpEMFR - レコードのポインタを指定します
nObj - ハンドルテーブルに格納されているハンドルの個数を指定します
lpData - EnumEnhMetaFile() で指定した追加情報を指定します

戻り値 - 列挙を続けるのなら 0 以外、終了するならば 0

PlayEnhMetaFileRecord()

BOOL PlayEnhMetaFileRecord(
	HDC hdc , LPHANDLETABLE lpHandletable,
	CONST ENHMETARECORD *lpEnhMetaRecord,
	UINT nHandles
);
拡張メタファイルの単一レコードを再生します

hdc - デバイスコンテキストのハンドルを指定します
lpHandletable - ハンドルテーブルへのポインタを指定します
lpEnhMetaRecord - レコードへのポインタを指定します
nHandles - ハンドルテーブルのハンドル数を指定します

戻り値 - 成功すれば 0 以外、失敗すれば 0



前のページへ戻る次のページへ