クリップボード
アプリケーション間データ転送
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