クリップボード


アプリケーション間データ転送

Windows はアプリケーション間でデータを転送する手段として
クリップボードと呼ばれる保存領域を提供しています
アプリケーションはこれにアクセスし、データを受け取ったり渡したりできます

クリップボードにデータを転送するには、専用のメモリを確保しなければいけません
しかし、ANSI C標準関数 malloc() ではこれに不適切なのです
この場では、テキストデータをクリップボードに転送する基本的な方法を説明します

まず、データを転送するにはメモリブロックを確保する必要があります
メモリブロックの確保には GlobalAlloc() 関数を使用します

HGLOBAL GlobalAlloc(UINT uFlags , DWORD dwBytes);

uFlags は、メモリ確保の方法を示す定数を指定します
dwBytes は、確保するバイト数を指定します
成功すればメモリのハンドルが、失敗すれば NULL が返ります

メモリオブジェクトのハンドルは HGLOBAL 型です
クリップボードにデータを転送するには、このハンドルが必要になります
uFlags に指定できる定数は、下記のリファレンスを参照してください
クリップボード転送には、通常 GHND と GMEM_SHARE を指定します

GHND は GMEM_MOVEABLE と GMEM_ZEROINIT を表すフラグです
GMEM_MOVEABLE は移動可能メモリであり、単純にポインタにキャストできません
しかし、これをを指定することで Windows は仮想メモリ内でメモリブロックを移動できます
頻繁にメモリを確保したり解放したりする場合、断片化を防ぐために指定します

GMEM_FIXED を指定すれば、固定メモリを確保できます
この場合は、グローバルメモリのハンドルをそのままポインタにキャストできます
GMEM_ZOROINIT を指定していると、確保領域は全て 0 で初期化されます

HGLOBAL 型のメモリハンドルは「グローバルハンドル」と呼びます
実際に確保したバイト数は、指定したバイト数よりも多くなることがあります
確保したメモリブロックの実際のバイト数は GlobalSize() を使います

DWORD GlobalSize(HGLOBAL hMem);

hMem には、有効なグローバルハンドルを指定します
ハンドルのバイト数が返りますが無効なハンドルや破棄されている場合は 0 です

確保したメモリブロックは、malloc() と free() の組み合わせ同様に
不必要になれば GlobalFree() で解放できます

HGLOBAL GlobalFree(HGLOBAL hMem);

hMem に、解放する有効なグローバルハンドルを指定します
この関数は、解放に成功すると NULL が返ります
失敗すれば、指定したハンドルと同じハンドルが返ります

移動可能メモリを確保した場合、その状態ではアクセスできません
移動可能メモリブロックは、一度ロックして固定させポインタに変換します
この作業は GlobalLock() 関数が行ってくれます

LPVOID GlobalLock(HGLOBAL hMem);

hMem にはロックするグローバルハンドルを指定します
戻り値はメモリブロックの先頭へのポインタですが、失敗すれば NULL です

ロックしたメモリはアクセスが終われば解除しなければいけません
ロックの解除には GlobalUnlock() 関数を使用します

BOOL GlobalUnlock(HGLOBAL hMem);

hMem に、解除するグローバルハンドルのポインタを指定します
これで、Windows は仮想メモリにおいてメモリブロックを移動できるようになります

これらのロック処理は、実際にはカウンタで行われており
GlobalLock() はロックカウントを1加算し、GlobalUnlock() は1減算します
カウントの初期値は0で、GlobalUnlock() 関数の戻り値は
カウントが1以上なら 0 以外を返し、そうでなければ 0 を返します


さて、これでメモリブロックとそのポインタを確保することができます
あとは、このデータをクリップボードへ転送するだけです
それには、まずクリップボードを OpenClipboard() で書きこむ権利を取得します

BOOL OpenClipboard(HWND hWndNewOwner);

hWndNewOwner はクリップボードを開くウィンドウのハンドルを指定します
NULL を指定すれば現在のタスクがクリップボードを開きます
クリップボードを開ければ 0 以外が返りますが
他のプログラムやウィンドウが使用中の場合は 0 が返ります

クリップボードを開いた時点では、クリップボードに何らかのデータがある可能性が高いです
そのため、通常は EmptyClipboard() 関数でこれを空にします

BOOL EmptyClipboard(VOID)

成功すると 0 以外、失敗すると 0 が返ります

クリップボードを開いたら、必ず CloseClipboard() で閉じましょう
そうしなければ、他のウィンドウがクリップボードを開けなくなります

BOOL CloseClipboard(VOID)

関数が成功すると 0 以外、失敗すると 0 が返ります
通常、これらの3つの動作はワンセットとなることが多いです

クリップボードにデータを書きこむには SetClipboardData() を使います
この関数を使う時は、呼び出し側のウィンドウが現在のクリップボードのオーナーで
かつ、クリップボードが開いている状態でなければいけません

HANDLE SetClipboardData(UINT uFormat , HANDLE hMem);

uFormat にはクリップボードのデータ形式を表す値です
標準クリップボードのデータ形式か、または独自の形式も指定できます
標準クリップボードデータ形式は様々にありますが、ビットマップなどは後記します
この場では、CF_TEXT を指定し文字列を渡します(他の定数は、下記のリファレンス参照)

hMem には、クリップボードに渡すデータが格納されているグローバルハンドルです
NULL を渡すことも可能ですが、この方法は後記します
成功すればデータのハンドル、失敗すれば NULL が返ります

クリップボードに渡した hMem データは Windows によって管理されます。
そのため、hMem データをプログラムが解放する必要はありません

ずいぶん長くなりましたが、まとめるとこうなります

GlobalAlloc()メモリブロックのグローバルハンドルの取得
|
GlobalLock()メモリブロックのポインタを取得
(移動可能メモリブロックの場合)
|
GlobalUnlock()メモリのロックを解除
|
メモリブロックにデータを保存
|
OpenClipboard()クリップボードを開く
|
EmptyClipboard()クリップボードを初期化
|
SetClipboardData()クリップボードにデータを渡す
|
CloseClipboard()クリップボードを閉じる

このようにして、クリップボードにデータを転送することができます
クリップボードのオープンとクローズは一瞬のうちに行わなければならず
最低、ユーザーが他のウィンドウに移行する前にはそれを閉じる必要があります
#include <windows.h>

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	HGLOBAL hg;
	PTSTR	strMem;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		if (!OpenClipboard(hwnd)) return 0;
		EmptyClipboard();

		hg = GlobalAlloc(GHND | GMEM_SHARE , 128);
		strMem = (PTSTR)GlobalLock(hg);
		lstrcpy(strMem , TEXT("Kitty on your lap"));
		GlobalUnlock(hg);

		SetClipboardData(CF_TEXT , hg);

		CloseClipboard();
		return 0;
	}
	return DefWindowProc(hwnd , msg , wp , lp);
}

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

	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= 0;
	winc.cbWndExtra	= DLGWINDOWEXTRA;
	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") , TEXT("Kitty on your lap") ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);

	if (hwnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}
このプログラムはウィンドウが表示されるだけですが
その時、クリップボードには "Kitty on your lap" という文字列が渡されています
試しに、メモ帳などにペーストするかクリップボードビューワなどでご覧下さい

メモリブロックのポインタに、文字列をコピーする時には
strcpy() 関数のWindows版にあたる lstrcpy() を使っています

LPTSTR lstrcpy(LPTSTR lpString1, LPCTSTR lpString2);

lpString1 には十分な大きさを持つバッファを指定します
lpString2 は、バッファに複製する文字列へのポインタを指定します
成功すればバッファへのポインタ、失敗すれば NULL が返ります

また、クリップボードは異なるデータ形式であれば
複数のフォーマットを転送、格納することが可能です


クリップボードからデータを取得する

クリップボードにデータを転送する時は、とにかくデータを転送するだけでしたが
クリップボードからデータを受け取る場合、先にクリップボードを調べなければいけません

クリップボードには、ビットマップなどテキスト以外のデータもありえるからです
そのため、まず IsClipboardFormatAvailable() 関数で調べます

BOOL IsClipboardFormatAvailable(UINT format);

format にSetClipboardData() 関数で使う、クリップボードのデータを指定します
クリップボードに指定したデータが格納されていれば 0 以外
そうでない場合は 0 が返るので、CF_TEXT を渡して調べます

次に、クリップボードが所有するグローバルハンドルを取得します
注意しなければならないのですが、このハンドルはクリップボードのものです
クリップボードのグローバルハンドルは GetClipboardData() で得られます

HANDLE GetClipboardData(UINT uFormat);

uFormat は、クリップボードのデータ形式を指定します
成功すればクリップボードのグローバルハンドルが返りますが
データ形式が異なるなど、失敗すれば NULL が返ります

データ形式といえ、たとえば Unicode や OME テキストの場合はどうしましょう?
それぞれも別に扱わなければならないのでしょうか
実は、このような場合は嬉しい事にOS が自動的に変換 してくれます
つまり、Unicode や OME の場合でも、CF_TEXT として取り出すことができるのです

この関数で得たグローバルハンドルをロックしてアクセスできますが
これはクリップボードが所有するデータなので、書きかえることなどはできません
当然、解放するなどの行為もイリーガルです
#include <windows.h>

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	HDC hdc;
	PAINTSTRUCT ps;
	HGLOBAL hg;
	PTSTR strText , strClip;
	RECT rect;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hwnd , &ps);

		if (OpenClipboard(hwnd) && (hg = GetClipboardData(CF_TEXT))) {
			strText = malloc(GlobalSize(hg));
			strClip = GlobalLock(hg);
			lstrcpy(strText , strClip);

			GlobalUnlock(hg);
			CloseClipboard();

			GetClientRect(hwnd , &rect);
			DrawText(hdc , strText , -1 , &rect , DT_LEFT);
			free(strText);
		}

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

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

	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= 0;
	winc.cbWndExtra	= DLGWINDOWEXTRA;
	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") , TEXT("Kitty on your lap") ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);

	if (hwnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}
プログラムを起動すると、クリップボードのテキストを表示します
テキスト以外のデータがクリップボードにある場合、表示しません


GlobalAlloc()

HGLOBAL GlobalAlloc(UINT uFlags , DWORD dwBytes);

ヒープから指定されたバイト数のメモリを確保します

uFlags - メモリの確保方法を示す定数を指定します
dwBytes - 確保するバイト数を指定します

戻り値 - メモリオブジェクトのハンドル。失敗すると NULL
uFlags には以下の定数を矛盾のない組み合わせで指定できます

定数解説
GMEM_FIXED 固定メモリを確保します
GMEM_MOVEABLE や GMEM_DISCARDABLE と組み合わせることはできません
戻り値は、メモリブロックへのポインタになります
戻り値を単純にポインタ型にキャストすると、メモリにアクセスできます
GMEM_MOVEABLE 移動可能メモリを確保します
GMEM_FIXED と組み合わせることはできません

戻り値は、メモリオブジェクトのハンドルになります
ハンドルをポインタに変換するには、GlobalLock 関数を使用します
GPTR GMEM_FIXEDと GMEM_ZEROINIT との組み合わせです
GHND GMEM_MOVEABLE と GMEM_ZEROINIT との組み合わせです
GMEM_DDESHARE
GMEM_SHARE
ダイナミックデータエクスチェンジ (DDE) 対話のために
DDE 関数が使用するメモリを確保します
Windows 3.x とは異なり、このメモリはグローバルには共有されませんが
互換性のために使用できます
プロセス間通信のために DDE またはクリップボードを使用するプロセスにおいてのみ
このフラグを指定してください
GMEM_DISCARDABLE 廃棄可能メモリを確保します
GMEM_FIXED と組み合わせることはできません
Win32 アプリケーションの一部は、このフラグを無視することがあります
GMEM_LOWER 無視されます
Windows の以前のバージョンとの互換性のために残されています
GMEM_NOCOMPACT メモリ確保の要求を満たすためのメモリの圧縮または廃棄を行わないようにします
GMEM_NODISCARD メモリ確保の要求を満たすためのメモリの廃棄を行わないようにします
GMEM_NOT_BANKED メモリ確保の要求を満たすためのメモリの廃棄を行わないようにします
GMEM_NOTIFY 無視されます
Windows の以前のバージョンとの互換性のために残されています
GMEM_SHARE GMEM_DDESHARE と同じです
GMEM_ZEROINIT 確保したメモリの内容を、0 に初期化します

GlobalSize()

DWORD GlobalSize(HGLOBAL hMem);

指定されたグローバルメモリオブジェクトのサイズを、バイト単位で取得します

hMem - グローバルメモリオブジェクトのハンドルを指定します

戻り値 - バイトサイズ、失敗すると 0

GlobalFree()

HGLOBAL GlobalFree(HGLOBAL hMem);

定されたグローバルメモリオブジェクトを解放して、そのハンドルを無効にします

hMem - グローバルメモリオブジェクトのハンドルを指定します

戻り値 - 関数が成功すると NULL。失敗すると指定したハンドル

GlobalLock()

LPVOID GlobalLock(HGLOBAL hMem);

グローバルメモリオブジェクトをロックし、メモリブロックの先頭へのポインタを返します

hMem - グローバルメモリオブジェクトのハンドルを指定します

戻り値 - メモリブロックの先頭へのポインタ、失敗すると NULL

GlobalUnlock()

BOOL GlobalUnlock(HGLOBAL hMem);

グローバルメモリオブジェクトのロックカウントを減らします

hMem - グローバルメモリオブジェクトのハンドルを指定します

戻り値 - 実行後もロックカウントが 1 以上であれば、0 以外、そうでなければ 0

OpenClipboard()

BOOL OpenClipboard(HWND hWndNewOwner);

クリップボードをオープンします
同時に他のアプリケーションがクリップボードの内容を変更できないようにします

hWndNewOwner - クリップボードをオープンするウィンドウのハンドルを指定します

戻り値 - 成功すると 0 以外、失敗すると 0

EmptyClipboard()

BOOL EmptyClipboard(VOID)

クリップボードを空にし、クリップボード内のデータのハンドルを解放します
同時に、現在クリップボードをオープンしているウィンドウに
クリップボードの所有権を与えます

戻り値 - 成功すると 0 以外、失敗すると 0

CloseClipboard()

BOOL CloseClipboard(VOID)

クリップボードをクローズします

戻り値 - 成功すると 0 以外、失敗すると 0

SetClipboardData()

HANDLE SetClipboardData(UINT uFormat , HANDLE hMem);

クリップボードに、指定されたデータ形式でデータを入れます
データを入れるには、呼び出し側のウィンドウが現在のクリップボードのオーナーであり
かつ、OpenClipboard 関数でクリップボードをオープンしていなければなりません

ただし、WM_RENDERFORMAT およびWM_RENDERALLFORMATS メッセージ内での処理では
OpenClipboard 関数を呼ばずに SetClipboardData 関数を呼んで構いません

uFormat - 標準クリップボードデータ形式か、RegisterClipboardFormat() で登録したデータ形式を指定します
hMem - データが入っているグローバルメモリのハンドルを指定します

戻り値 - データのハンドルが返ります。関数が失敗すると、NULL

uFormat には、次の標準クリップボードでータ形式を指定できます

定数解説
CF_BITMAP ビットマップのデータです (HBITMAP)
CF_DIB BITMAPINFO 構造体とビットマップビットから成るメモリオブジェクトです
CF_DIF Software Arts 社の DIF データ交換形式です
CF_DSPBITMAP プライベートな形式のビットマップデータです
CF_DSPENHMETAFILE プライベートな形式の拡張メタファイルデータです
CF_DSPMETAFILEPICT プライベートな形式のメタファイル画像表示形式データです
CF_DSPTEXT プライベートな形式のテキストデータです
CF_ENHMETAFILE 拡張メタファイルのデータです (HENHMETAFILE)
CF_GDIOBJFIRST から
CF_GDIOBJLAST までの値
GDI オブジェクトによって表現される、アプリケーション定義のクリップボード形式です
hMem パラメータは GDI オブジェクトのハンドルではありません
GMEM_DDESHARE と GMEM_MOVEABLE を持つ
GlobalAlloc 関数で割り当てられたハンドルになります
CF_HDROP HDROP 型です
DragQueryFile 関数にこのハンドルを渡すことで
ファイルに関する情報を受け取ることができます
CF_LOCALE テキストデータのロケール ID ハンドルです
CF_METAFILEPICT METAFILEPICT 構造体のメモリオブジェクトです。メタファイル画像形式です
CF_OEMTEXT OEM 文字セットの文字を持つ、テキスト形式データです
各行は改行復帰 (CR-LF) コードで終わります
データの終端は NULL 文字です
CF_OWNERDISPLAY オーナー表示形式です
クリップボードのオーナーが、クリップボードビューアウィンドウの表示と
更新を行なわなければなりません
オーナーは、WM_ASKCBFORMATNAME、WM_HSCROLLCLIPBOARD
WM_PAINTCLIPBOARD、WM_SIZECLIPBOARD
WM_VSCROLLCLIPBOARD メッセージを受け取ります

Hmem パラメータは、NULL でなければなりません
CF_PALETTE カラーパレットのハンドルです
カラーパレットに依存するか
カラーパレットを想定するデータをクリップボードに入れるときには
カラーパレットもクリップボードに入れるようにしてください
また、クリップボードの画像データを表示するときは
このパレットデータも取得してください
CF_PENDATA Windows のペン拡張機能のためのデータです
CF_PRIVATEFIRST から CF_PRIVATELAST までの値 プライベートなクリップボード形式です
プライベートなクリップボード形式に関係づけられたハンドルは
自動的には開放されません
オーナーが明示的に解放しなければなりません
(通常は、WM_DESTROYCLIPBOARD メッセージ処理時)
CF_RIFF RIFF 形式の音声データです
WAVE 形式よりも複雑です
CF_SYLK Microsoft シンボリックリンク (SYLK) 形式のデータです
CF_TEXT テキスト形式のデータです
各行は復帰改行 (CR-LF) コードで終わります
データの終端は NULL 文字です
この形式は、ANSI テキストに使います
CF_WAVE WAVE 形式の音声データです
CF_TIFF TIFF 形式の画像データです
CF_UNICODETEXT Unicode のテキスト形式です
各行は復帰改行 (CR-LF) コードで終わります
データの終端は NULL 文字です
Windows NT でのみサポートされています

lstrcpy()

LPTSTR lstrcpy(LPTSTR lpString1, LPCTSTR lpString2);

指定された文字列を、バッファにコピーします

lpString1 - バッファへのポインタを指定します
lpString2 - コピーする文字列へのポインタを指定します

戻り値 - バッファへのポインタが返ります。関数が失敗すると、NULL

IsClipboardFormatAvailable()

BOOL IsClipboardFormatAvailable(UINT format);

指定されたフォーマットのデータがクリップボードに格納されているかどうか調べます

format - SetClipboardData() 関数で使用できるクリップボードでータ形式を指定します

戻り値 - 指定データが格納されていれば 0 以外。そうでなければ 0

GetClipboardData()

HANDLE GetClipboardData(UINT uFormat);

クリップボードから、指定された形式のデータを取り出します

uFormat - SetClipboardData() 関数で使用できるクリップボードでータ形式を指定します

戻り値 - グローバルメモリブロックのハンドル。関数が失敗すると、NULL



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