ビューアチェイン
クリップボードビューアを作る
クリップボードビューアとは、クリップボードの状態を表示するアプリケーションのことで
Windows には Microsoft の CLIPBRD.EXE が標準で付属しています
クリップボードビューワは、私たちも作ることができます
基本的には、GetClipboardData() を定期的に呼び出しそれを表示すればよいのです
問題は、これを実行するタイミングです
定期的にタイマーでこれを取得するというのも方法ですが、これは合理的ではないでしょう
やはり、クリップボードの内容が変更された時というのがもっとも理想的です
実は、Windows はクリップボードに変更があるとビューアに
WM_DRAWCLIPBOARD メッセージを通知します
このメッセージにパラメータや戻り値はありません
ただし、Windows はこのメッセージをクリップボードビューアにしか通知しません
ビューワに登録するには SetClipboardViewer() 関数を使います
HWND SetClipboardViewer(HWND hWndNewViewer);
hWndNewViewer は、ビューアのウィンドウのハンドルを指定します
戻り値は、このビューアの次に登録されているビューアのハンドルです
この戻り値の値が意味するものは、クリップボードビューアの作成に重要です
実は、Windows は1つのビューアにしかメッセージを通知しません
つまり、最後に SetClipboardViewer() を用いて登録したビューアです
しかし、一度に複数のビューアが実行されることも十分ありえます
そのため、プログラムは SetClipboardViewer() が返したウィンドウに
WM_DRAWCLIPBOARD メッセージを送信する必要があるのです
こうして、上から下へ、鎖にようにビューアプログラムがメッセージを流します
このクリップボードビューアの構造をクリップボードビューアチェインと呼びます
クリップボードビューアは、チェインから自分自身を削除することができます
通知の必要がなくなれば ChangeClipboardChain() を使います
BOOL ChangeClipboardChain(HWND hWndRemove , HWND hWndNewNext);
hWndRemove には、チェインから削除するウィンドウのハンドルを
hWndNewNext は、自分の次のチェインのハンドルを指定します
戻り値は通常は FALSE になりますが、チェインにウィンドウが1つしかなかったときは TRUE です
この関数が呼び出されると、Windows はチェインの最上のウィンドウに
(つまり、最初に WM_DRAWCLIPBOARD を受け取るウィンドウ)
WM_CHANGECBCHAIN メッセージが送られます
WPARAM にはチェインから外れるウィンドウのハンドルが
LPARAM には WPARAM のウィンドウの次のウィンドウのハンドルが格納されています
この値は、ChangeClipChain() 関数の引数と同じです。戻り値は 0 を指定します
チェインに参加している全てのウィンドウは、このメッセージを処理する義務があります
もし、WPARAM の値が次のウィンドウと等しければ
これを削除し、LPARAM のハンドルに対して今後メッセージを送るようにします
WPARAM が次のウィンドウでなければ、自分の次のウィンドウにこのメッセージを送ります
#include <windows.h>
LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
static HWND hNextWnd;
HGLOBAL hg;
PTSTR strText;
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
switch (msg) {
case WM_DESTROY:
ChangeClipboardChain(hwnd , hNextWnd);
PostQuitMessage(0);
return 0;
case WM_CREATE:
hNextWnd = SetClipboardViewer(hwnd);
return 0;
case WM_DRAWCLIPBOARD:
if (hNextWnd) SendMessage(hNextWnd , msg , wp , lp);
InvalidateRect(hwnd , NULL , TRUE);
return 0;
case WM_CHANGECBCHAIN:
if ((HWND)wp == hNextWnd) hNextWnd = (HWND)lp;
else if (hNextWnd) SendMessage(hNextWnd , msg , wp , lp);
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd , &ps);
if (OpenClipboard(hwnd) && (hg = GetClipboardData(CF_TEXT))) {
strText = (PTSTR)GlobalLock(hg);
GetClientRect(hwnd , &rect);
DrawText(hdc , strText , -1 , &rect , DT_LEFT);
GlobalUnlock(hg);
CloseClipboard();
}
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;
}
このプログラムをコンパイルし、2つ同時に実行した状態で
何らかの文字列をクリップボードに転送すると、2つのウィンドウとも
即座に変更が通知され、文字列を再表示します
もし、あなたがクリップボードチェインの約束を破り
メッセージをあなたのウィンドウで止めるようなことをした場合
このプログラム以下のウィンドウは、例えチェインに登録していてもメッセージは受け取りません
SetClipboardViewer()
HWND SetClipboardViewer(HWND hWndNewViewer);
クリップボードビューアチェインに、指定されたウィンドウを追加します
hWndNewViewer - クリップボードチェインに追加するウィンドウを指定します
戻り値 - 次に位置するウィンドウのハンドル。失敗、または次がない場合は NULL
ChangeClipboardChain()
BOOL ChangeClipboardChain(HWND hWndRemove , HWND hWndNewNext);
クリップボードビューアのチェインから、指定されたウィンドウを削除します
hWndRemove - 削除するウィンドウのハンドルを指定します
hWndNewNext - hWndRemove の次のウィンドウのハンドルを指定します
戻り値 - 通常は FALSE、ウィンドウが1つしかなかったときは TRUE