クリップボードの使用
このセクションには、次のタスクのコード サンプルがあります。
- 切り取り、コピー、貼り付けコマンドの実装
- クリップボードの内容の監視
- クリップボードシーケンス番号のクエリ
- クリップボード形式リスナーの作成
- クリップボード ビューアー ウィンドウの作成
- クリップボードビューアチェーンへのウィンドウの追加
WM_CHANGECBCHAIN
メッセージの処理- クリップボード ビューアー チェーンからのウィンドウの削除
WM_DRAWCLIPBOARD
メッセージの処理- クリップボード ビューアーの例
切り取り、コピー、貼り付けコマンドの実装
このセクションでは、標準 の 切り取り、 コピー、 貼 り付け コマンドをアプリケーションに実装する方法について説明します。 このセクションの例では、これらのメソッドを使用して、登録済みのクリップボード形式、 CF_OWNERDISPLAY
形式、および CF_TEXT
形式を使用してクリップボードにデータを 配置します。 登録された形式は、ラベルと呼ばれる長方形または楕円のテキスト ウィンドウを表すために使用されます。
データの選択
情報をクリップボードにコピーする前に、ユーザーはコピーまたは切り取る特定の情報を選択する必要があります。 アプリケーションでは、ユーザーがドキュメント内の情報を選択し、選択したデータを示す何らかの視覚的フィードバックを提供する必要があります。
編集メニューの作成
アプリケーションは、[編集] メニュー コマンドの標準キーボード アクセラレータを含むアクセラレータ テーブルを読み込む必要があります。 アクセラレータを有効にするには、 TranslateAccelerator
関数をアプリケーションのメッセージ ループに追加する必要があります。 キーボード アクセラレータの詳細については、「キーボード アクセラレータ」を参照してください。
WM_INITMENUPOPUP
メッセージの処理
特定の時点ですべてのクリップボード コマンドをユーザーが使用できるわけではありません。 アプリケーションは WM_INITMENUPOPUP
メッセージを処理して、使用可能なコマンドのメニュー項目を有効にし、使用できないコマンドを無効にする必要があります。
Label という名前の WM_INITMENUPOPUP
アプリケーションの場合を次に示します。
case WM_INITMENUPOPUP:
InitMenu((HMENU) wParam);
break;
InitMenu
関数の定義は次のようになります。
void WINAPI InitMenu(HMENU hmenu)
{
int cMenuItems = GetMenuItemCount(hmenu);
int nPos;
UINT id;
UINT fuFlags;
PLABELBOX pbox = (hwndSelected == NULL) ? NULL :
(PLABELBOX) GetWindowLong(hwndSelected, 0);
for (nPos = 0; nPos < cMenuItems; nPos++)
{
id = GetMenuItemID(hmenu, nPos);
switch (id)
{
case IDM_CUT:
case IDM_COPY:
case IDM_DELETE:
if (pbox == NULL || !pbox->fSelected)
fuFlags = MF_BYCOMMAND | MF_GRAYED;
else if (pbox->fEdit)
fuFlags = (id != IDM_DELETE && pbox->ichSel
== pbox->ichCaret) ?
MF_BYCOMMAND | MF_GRAYED :
MF_BYCOMMAND | MF_ENABLED;
else
fuFlags = MF_BYCOMMAND | MF_ENABLED;
EnableMenuItem(hmenu, id, fuFlags);
break;
case IDM_PASTE:
if (pbox != NULL && pbox->fEdit)
EnableMenuItem(hmenu, id,
IsClipboardFormatAvailable(CF_TEXT) ?
MF_BYCOMMAND | MF_ENABLED :
MF_BYCOMMAND | MF_GRAYED
);
else
EnableMenuItem(hmenu, id,
IsClipboardFormatAvailable(
uLabelFormat) ?
MF_BYCOMMAND | MF_ENABLED :
MF_BYCOMMAND | MF_GRAYED
);
}
}
}
WM_COMMAND
メッセージの処理
メニュー コマンドを処理するには、アプリケーションのメイン ウィンドウ プロシージャに WM_COMMAND
ケースを追加します。 ラベル アプリケーションの ウィンドウ プロシージャの WM_COMMAND
ケースを次に示します。
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDM_CUT:
if (EditCopy())
EditDelete();
break;
case IDM_COPY:
EditCopy();
break;
case IDM_PASTE:
EditPaste();
break;
case IDM_DELETE:
EditDelete();
break;
case IDM_EXIT:
DestroyWindow(hwnd);
}
break;
Copy コマンドと Cut コマンドを実行するために、ウィンドウ プロシージャはアプリケーション定義 EditCopy
関数を呼び出します。 詳細については、「クリップボードへの情報のコピー」を参照してください。 Paste コマンドを実行するために、ウィンドウ プロシージャはアプリケーション定義 EditPaste
関数を呼び出します。 EditPaste
関数の詳細については、「クリップボードからの情報の貼り付け」を参照してください。
クリップボードへの情報のコピー
Label アプリケーションでは、アプリケーション定義の EditCopy 関数によって、現在の選択範囲がクリップボードにコピーされます。 この関数は、次のことを行います。
OpenClipboard
関数を呼び出してクリップボードを開きます。EmptyClipboard
関数を呼び出してクリップボードを空にします。- アプリケーションが提供するクリップボード形式ごとに
SetClipboardData
関数を 1 回呼び出します。 CloseClipboard
関数を呼び出してクリップボードを閉じます。
現在の選択内容に応じて、EditCopy 関数はテキストの範囲をコピーするか、ラベル全体を表すアプリケーション定義構造をコピーします。 LABELBOX
と呼ばれる構造体は、次のように定義されています。
#define BOX_ELLIPSE 0
#define BOX_RECT 1
#define CCH_MAXLABEL 80
#define CX_MARGIN 12
typedef struct tagLABELBOX { // box
RECT rcText; // coordinates of rectangle containing text
BOOL fSelected; // TRUE if the label is selected
BOOL fEdit; // TRUE if text is selected
int nType; // rectangular or elliptical
int ichCaret; // caret position
int ichSel; // with ichCaret, delimits selection
int nXCaret; // window position corresponding to ichCaret
int nXSel; // window position corresponding to ichSel
int cchLabel; // length of text in atchLabel
TCHAR atchLabel[CCH_MAXLABEL];
} LABELBOX, *PLABELBOX;
次は、 EditCopy
関数です。
BOOL WINAPI EditCopy(VOID)
{
PLABELBOX pbox;
LPTSTR lptstrCopy;
HGLOBAL hglbCopy;
int ich1, ich2, cch;
if (hwndSelected == NULL)
return FALSE;
// Open the clipboard, and empty it.
if (!OpenClipboard(hwndMain))
return FALSE;
EmptyClipboard();
// Get a pointer to the structure for the selected label.
pbox = (PLABELBOX) GetWindowLong(hwndSelected, 0);
// If text is selected, copy it using the CF_TEXT format.
if (pbox->fEdit)
{
if (pbox->ichSel == pbox->ichCaret) // zero length
{
CloseClipboard(); // selection
return FALSE;
}
if (pbox->ichSel < pbox->ichCaret)
{
ich1 = pbox->ichSel;
ich2 = pbox->ichCaret;
}
else
{
ich1 = pbox->ichCaret;
ich2 = pbox->ichSel;
}
cch = ich2 - ich1;
// Allocate a global memory object for the text.
hglbCopy = GlobalAlloc(GMEM_MOVEABLE,
(cch + 1) * sizeof(TCHAR));
if (hglbCopy == NULL)
{
CloseClipboard();
return FALSE;
}
// Lock the handle and copy the text to the buffer.
lptstrCopy = GlobalLock(hglbCopy);
memcpy(lptstrCopy, &pbox->atchLabel[ich1],
cch * sizeof(TCHAR));
lptstrCopy[cch] = (TCHAR) 0; // null character
GlobalUnlock(hglbCopy);
// Place the handle on the clipboard.
SetClipboardData(CF_TEXT, hglbCopy);
}
// If no text is selected, the label as a whole is copied.
else
{
// Save a copy of the selected label as a local memory
// object. This copy is used to render data on request.
// It is freed in response to the WM_DESTROYCLIPBOARD
// message.
pboxLocalClip = (PLABELBOX) LocalAlloc(
LMEM_FIXED,
sizeof(LABELBOX)
);
if (pboxLocalClip == NULL)
{
CloseClipboard();
return FALSE;
}
memcpy(pboxLocalClip, pbox, sizeof(LABELBOX));
pboxLocalClip->fSelected = FALSE;
pboxLocalClip->fEdit = FALSE;
// Place a registered clipboard format, the owner-display
// format, and the CF_TEXT format on the clipboard using
// delayed rendering.
SetClipboardData(uLabelFormat, NULL);
SetClipboardData(CF_OWNERDISPLAY, NULL);
SetClipboardData(CF_TEXT, NULL);
}
// Close the clipboard.
CloseClipboard();
return TRUE;
}
クリップボードからの情報の貼り付け
Label アプリケーションでは、アプリケーション定義 EditPaste
関数によってクリップボードの内容が貼り付けられます。 この関数は、次のことを行います。
OpenClipboard
関数を呼び出してクリップボードを開きます。- 取得する使用可能なクリップボード形式を決定します。
GetClipboardData
関数を呼び出して、選択した形式のデータへのハンドルを取得します。- データのコピーをドキュメントに挿入します。
GetClipboardData
によって返される ハンドルは引き続きクリップボードによって所有されるため、アプリケーションで解放したり、ロックしたままにしたりしてはなりません。 CloseClipboard
関数を呼び出してクリップボードを閉じます。
ラベルが選択されていて、カーソルが含まれている場合、EditPaste 関数はクリップボードのテキストを挿入ポイントに挿入します。 選択されていない場合、またはラベルが選択されている場合、関数はクリップボードのアプリケーション定義 LABELBOX
構造を使用して新しいラベルを作成します。 LABELBOX
構造は、登録済みのクリップボード形式を使用してクリップボードに配置されます。
LABELBOX
と呼ばれる構造体は、次のように定義されています。
#define BOX_ELLIPSE 0
#define BOX_RECT 1
#define CCH_MAXLABEL 80
#define CX_MARGIN 12
typedef struct tagLABELBOX { // box
RECT rcText; // coordinates of rectangle containing text
BOOL fSelected; // TRUE if the label is selected
BOOL fEdit; // TRUE if text is selected
int nType; // rectangular or elliptical
int ichCaret; // caret position
int ichSel; // with ichCaret, delimits selection
int nXCaret; // window position corresponding to ichCaret
int nXSel; // window position corresponding to ichSel
int cchLabel; // length of text in atchLabel
TCHAR atchLabel[CCH_MAXLABEL];
} LABELBOX, *PLABELBOX;
次は、 EditPaste
関数です。
VOID WINAPI EditPaste(VOID)
{
PLABELBOX pbox;
HGLOBAL hglb;
LPTSTR lptstr;
PLABELBOX pboxCopy;
int cx, cy;
HWND hwnd;
pbox = hwndSelected == NULL ? NULL :
(PLABELBOX) GetWindowLong(hwndSelected, 0);
// If the application is in edit mode,
// get the clipboard text.
if (pbox != NULL && pbox->fEdit)
{
if (!IsClipboardFormatAvailable(CF_TEXT))
return;
if (!OpenClipboard(hwndMain))
return;
hglb = GetClipboardData(CF_TEXT);
if (hglb != NULL)
{
lptstr = GlobalLock(hglb);
if (lptstr != NULL)
{
// Call the application-defined ReplaceSelection
// function to insert the text and repaint the
// window.
ReplaceSelection(hwndSelected, pbox, lptstr);
GlobalUnlock(hglb);
}
}
CloseClipboard();
return;
}
// If the application is not in edit mode,
// create a label window.
if (!IsClipboardFormatAvailable(uLabelFormat))
return;
if (!OpenClipboard(hwndMain))
return;
hglb = GetClipboardData(uLabelFormat);
if (hglb != NULL)
{
pboxCopy = GlobalLock(hglb);
if (pboxCopy != NULL)
{
cx = pboxCopy->rcText.right + CX_MARGIN;
cy = pboxCopy->rcText.top * 2 + cyText;
hwnd = CreateWindowEx(
WS_EX_NOPARENTNOTIFY | WS_EX_TRANSPARENT,
atchClassChild, NULL, WS_CHILD, 0, 0, cx, cy,
hwndMain, NULL, hinst, NULL
);
if (hwnd != NULL)
{
pbox = (PLABELBOX) GetWindowLong(hwnd, 0);
memcpy(pbox, pboxCopy, sizeof(LABELBOX));
ShowWindow(hwnd, SW_SHOWNORMAL);
SetFocus(hwnd);
}
GlobalUnlock(hglb);
}
}
CloseClipboard();
}
クリップボード形式の登録
クリップボード形式を登録するには、次のように、 RegisterClipboardFormat
関数の呼び出しをアプリケーションのインスタンス初期化関数に追加します。
// Register a clipboard format.
// We assume that atchTemp can contain the format name and
// a null-terminator, otherwise it is truncated.
//
LoadString(hinstCurrent, IDS_FORMATNAME, atchTemp,
sizeof(atchTemp)/sizeof(TCHAR));
uLabelFormat = RegisterClipboardFormat(atchTemp);
if (uLabelFormat == 0)
return FALSE;
WM_RENDERFORMAT
と WM_RENDERALLFORMATS
メッセージの 処理
ウィンドウが SetClipboardData
関数に NULL
ハンドルを渡す場合は、要求に応じてデータをレンダリングする WM_RENDERFORMAT
メッセージと WM_RENDERALLFORMATS
メッセージを処理する必要があります。
ウィンドウが特定の形式のレンダリングを遅らせ、別のアプリケーションがその形式のデータを要求すると、 WM_RENDERFORMAT
メッセージがウィンドウに送信されます。 さらに、ウィンドウが 1 つ以上の形式のレンダリングを遅らせ、それらの形式の一部が再びメインウィンドウが破棄されようとしたときに、そのウィンドウに WM_RENDERALLFORMATS
メッセージが送信されてから破棄されます。
クリップボード形式をレンダリングするには、ウィンドウ プロシージャは、 SetClipboardData
関数を使用してクリップボードに非NULL
データ ハンドルを配置する必要があります。 ウィンドウ プロシージャが WM_RENDERFORMAT
メッセージに応答して形式をレンダリングする場合は、 SetClipboardData
を呼び出す前にクリップボードを開く必要があります。 ただし、 WM_RENDERALLFORMATS
メッセージに応答して 1 つ以上の形式をレンダリングする場合は、クリップボードを開き、 SetClipboardData
を呼び出す前にウィンドウがまだクリップボードを所有していることをチェックし、戻る前にクリップボードを閉じる必要があります。
Label アプリケーションは、次のように WM_RENDERFORMAT
メッセージと WM_RENDERALLFORMATS
メッセージを処理します。
case WM_RENDERFORMAT:
RenderFormat((UINT) wParam);
break;
case WM_RENDERALLFORMATS:
if (OpenClipboard(hwnd))
{
if (GetClipboardOwner() == hwnd)
{
RenderFormat(uLabelFormat);
RenderFormat(CF_TEXT);
}
CloseClipboard();
}
break;
どちらの場合も、ウィンドウ プロシージャは、次のように定義されたアプリケーション定義 RenderFormat
関数を呼び出します。
LABELBOX
と呼ばれる構造体は、次のように定義されています。
#define BOX_ELLIPSE 0
#define BOX_RECT 1
#define CCH_MAXLABEL 80
#define CX_MARGIN 12
typedef struct tagLABELBOX { // box
RECT rcText; // coordinates of rectangle containing text
BOOL fSelected; // TRUE if the label is selected
BOOL fEdit; // TRUE if text is selected
int nType; // rectangular or elliptical
int ichCaret; // caret position
int ichSel; // with ichCaret, delimits selection
int nXCaret; // window position corresponding to ichCaret
int nXSel; // window position corresponding to ichSel
int cchLabel; // length of text in atchLabel
TCHAR atchLabel[CCH_MAXLABEL];
} LABELBOX, *PLABELBOX;
void WINAPI RenderFormat(UINT uFormat)
{
HGLOBAL hglb;
PLABELBOX pbox;
LPTSTR lptstr;
int cch;
if (pboxLocalClip == NULL)
return;
if (uFormat == CF_TEXT)
{
// Allocate a buffer for the text.
cch = pboxLocalClip->cchLabel;
hglb = GlobalAlloc(GMEM_MOVEABLE,
(cch + 1) * sizeof(TCHAR));
if (hglb == NULL)
return;
// Copy the text from pboxLocalClip.
lptstr = GlobalLock(hglb);
memcpy(lptstr, pboxLocalClip->atchLabel,
cch * sizeof(TCHAR));
lptstr[cch] = (TCHAR) 0;
GlobalUnlock(hglb);
// Place the handle on the clipboard.
SetClipboardData(CF_TEXT, hglb);
}
else if (uFormat == uLabelFormat)
{
hglb = GlobalAlloc(GMEM_MOVEABLE, sizeof(LABELBOX));
if (hglb == NULL)
return;
pbox = GlobalLock(hglb);
memcpy(pbox, pboxLocalClip, sizeof(LABELBOX));
GlobalUnlock(hglb);
SetClipboardData(uLabelFormat, hglb);
}
}
WM_DESTROYCLIPBOARD
メッセージの処理
ウィンドウは、遅延レンダリングをサポートするために確保したリソースを解放するために WM_DESTROYCLIPBOARD
メッセージを処理できます。 たとえば、Label アプリケーションは、クリップボードにラベルをコピーするときに、ローカル メモリ オブジェクトを割り当てます。 その後、次のように、 WM_DESTROYCLIPBOARD
メッセージに応答してこのオブジェクトを解放します。
case WM_DESTROYCLIPBOARD:
if (pboxLocalClip != NULL)
{
LocalFree(pboxLocalClip);
pboxLocalClip = NULL;
}
break;
所有者表示クリップボード形式の使用
ウィンドウがクリップボード形式を使用して CF_OWNERDISPLAY
クリップボードに情報を配置する場合は、次の操作を行う必要があります。
WM_PAINTCLIPBOARD
メッセージを処理する。 このメッセージは、クリップボード ビューアー ウィンドウの一部を再描画する必要があるときに、クリップボードの所有者に送信されます。WM_SIZECLIPBOARD
メッセージを処理する。 このメッセージは、クリップボード ビューアー ウィンドウのサイズが変更されたか、コンテンツが変更されたときに、クリップボードの所有者に送信されます。 通常、ウィンドウはクリップボード ビューアー ウィンドウのスクロール位置と範囲を設定することで、このメッセージに応答します。 このメッセージに応答して、Label アプリケーションはクリップボード ビューアー ウィンドウのSIZE
構造も更新します。WM_HSCROLLCLIPBOARD
とWM_VSCROLLCLIPBOARD
メッセージの処理 これらのメッセージは、クリップボード ビューアー ウィンドウでスクロール バー イベントが発生すると、クリップボードの所有者に送信されます。WM_ASKCBFORMATNAME
メッセージを処理する。 クリップボード ビューアー ウィンドウは、このメッセージをアプリケーションに送信して、所有者表示形式の名前を取得します。
Label アプリケーションのウィンドウ プロシージャは、次のようにこれらのメッセージを処理します。
LRESULT CALLBACK MainWindowProc(hwnd, msg, wParam, lParam)
HWND hwnd;
UINT msg;
WPARAM wParam;
LPARAM lParam;
{
static RECT rcViewer;
RECT rc;
LPRECT lprc;
LPPAINTSTRUCT lpps;
switch (msg)
{
//
// Handle other messages.
//
case WM_PAINTCLIPBOARD:
// Determine the dimensions of the label.
SetRect(&rc, 0, 0,
pboxLocalClip->rcText.right + CX_MARGIN,
pboxLocalClip->rcText.top * 2 + cyText
);
// Center the image in the clipboard viewer window.
if (rc.right < rcViewer.right)
{
rc.left = (rcViewer.right - rc.right) / 2;
rc.right += rc.left;
}
if (rc.bottom < rcViewer.bottom)
{
rc.top = (rcViewer.bottom - rc.bottom) / 2;
rc.bottom += rc.top;
}
// Paint the image, using the specified PAINTSTRUCT
// structure, by calling the application-defined
// PaintLabel function.
lpps = (LPPAINTSTRUCT) GlobalLock((HGLOBAL) lParam);
PaintLabel(lpps, pboxLocalClip, &rc);
GlobalUnlock((HGLOBAL) lParam);
break;
case WM_SIZECLIPBOARD:
// Save the dimensions of the window in a static
// RECT structure.
lprc = (LPRECT) GlobalLock((HGLOBAL) lParam);
memcpy(&rcViewer, lprc, sizeof(RECT));
GlobalUnlock((HGLOBAL) lParam);
// Set the scroll ranges to zero (thus eliminating
// the need to process the WM_HSCROLLCLIPBOARD and
// WM_VSCROLLCLIPBOARD messages).
SetScrollRange((HWND) wParam, SB_HORZ, 0, 0, TRUE);
SetScrollRange((HWND) wParam, SB_VERT, 0, 0, TRUE);
break;
case WM_ASKCBFORMATNAME:
LoadString(hinst, IDS_OWNERDISPLAY,
(LPSTR) lParam, wParam);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
クリップボードの内容の監視
クリップボードへの変更を監視するには、3 つの方法があります。 最も古い方法は、クリップボード ビューアー ウィンドウを作成する方法です。 Windows 2000 では、クリップボードのシーケンス番号を照会する機能が追加され、Windows Vista ではクリップボード形式リスナーのサポートが追加されました。 クリップボード ビューアー ウィンドウは、以前のバージョンの Windows との下位互換性のためにサポートされています。 新しいプログラムでは、クリップボード形式リスナーまたはクリップボードシーケンス番号を使用する必要があります。
クリップボードシーケンス番号のクエリ
クリップボードの内容が変更されるたびに、クリップボードのシーケンス番号と呼ばれる 32 ビット値がインクリメントされます。 プログラムは、 GetClipboardSequenceNumber
関数を呼び出すことによって、現在のクリップボードシーケンス番号を取得できます。 プログラムは、前回の呼び出し GetClipboardSequenceNumber
によって返された値と比較することで、クリップボードの内容が変更されたかどうかを判断できます。 この方法は、現在のクリップボードの内容に基づいて結果をキャッシュするプログラムに適しており、そのキャッシュの結果を使用する前に計算がまだ有効かどうかを知る必要があります。 これは通知メソッドではないため、ポーリング ループでは使用しないでください。 クリップボードの内容が変更されたときに通知を受け取る場合は、クリップボード形式リスナーまたはクリップボード ビューアーを使用します。
クリップボード形式リスナーの作成
クリップボード形式リスナーは、クリップボードの内容が変更されたときに通知されるように登録されたウィンドウです。 この方法は、クリップボード ビューアー ウィンドウの作成よりも推奨されます。実装が簡単で、プログラムがクリップボード ビューアー チェーンを適切にメインできない場合や、クリップボード ビューアー チェーン内のウィンドウがメッセージへの応答を停止した場合の問題を回避するためです。
ウィンドウは、 AddClipboardFormatListener
関数を呼び出してクリップボード形式リスナーとして登録します。 クリップボードの内容が変更されると、ウィンドウに WM_CLIPBOARDUPDATE
メッセージが投稿されます。 登録は RemoveClipboardFormatListener
関数を呼び出してウィンドウ自体の登録を解除するまで有効のままです。
クリップボード ビューアー ウィンドウの作成
クリップボード ビューアー ウィンドウには、クリップボードの現在のコンテンツが表示され、クリップボードのコンテンツが変更されたときにメッセージが受信されます。 クリップボード ビューアー ウィンドウを作成するには、アプリケーションで次の操作を行う必要があります。
- クリップボードビューアチェーンへのウィンドウの追加
WM_CHANGECBCHAIN
メッセージを処理する。WM_DRAWCLIPBOARD
メッセージを処理する。- 破棄する前に、クリップボード ビューアー チェーンからウィンドウを削除します。
クリップボードビューアチェーンへのウィンドウの追加
SetClipboardViewer
関数を呼び出して、ウィンドウがクリップボード ビューアー チェーンに追加されます。 戻り値は、チェーン内の次のウィンドウへのハンドルです。 ウィンドウは、この値を追跡する必要があります。たとえば、 hwndNextViewer
という名前の付いた静的変数に保存します。
次の例では、 WM_CREATE
メッセージに応答して、クリップボード ビューアー チェーンにウィンドウを追加します。
case WM_CREATE:
// Add the window to the clipboard viewer chain.
hwndNextViewer = SetClipboardViewer(hwnd);
break;
次のタスクのコード スニペットが表示されます。
WM_CHANGECBCHAIN
メッセージの処理
クリップボード ビューアー ウィンドウは、別のウィンドウがクリップボード ビューアー チェーンからそれ自体を削除しているときに WM_CHANGECBCHAIN
メッセージを受信します。 削除されるウィンドウがチェーン内の次のウィンドウである場合、メッセージを受信するウィンドウは、チェーンから次のウィンドウのリンクを解除する必要があります。 それ以外の場合は、このメッセージをチェーン内の次のウィンドウに渡す必要があります。
次の例は、 WM_CHANGECBCHAIN
メッセージの処理を示しています。
case WM_CHANGECBCHAIN:
// If the next window is closing, repair the chain.
if ((HWND) wParam == hwndNextViewer)
hwndNextViewer = (HWND) lParam;
// Otherwise, pass the message to the next link.
else if (hwndNextViewer != NULL)
SendMessage(hwndNextViewer, uMsg, wParam, lParam);
break;
クリップボード ビューアー チェーンからのウィンドウの削除
クリップボード ビューアー チェーンからそれ自体を削除するには、ウィンドウによって関数が ChangeClipboardChain
呼び出されます。 次の例では、 WM_DESTROY
メッセージに応答して、クリップボード ビューアー チェーンからウィンドウを削除します。
case WM_DESTROY:
ChangeClipboardChain(hwnd, hwndNextViewer);
PostQuitMessage(0);
break;
WM_DRAWCLIPBOARD
メッセージの処理
WM_DRAWCLIPBOARD
メッセージは、クリップボードの内容が変更されたことをクリップボード ビューアー ウィンドウに通知します。 ウィンドウでは、 WM_DRAWCLIPBOARD
メッセージを処理するときに次の操作を行う必要があります。
- 表示する使用可能なクリップボード形式を決定します。
- クリップボード データを取得し、ウィンドウに表示します。 または、クリップボードの形式が
CF_OWNERDISPLAY
の場合は 、クリップボードの所有者にWM_PAINTCLIPBOARD
メッセージを送信します。 - クリップボード ビューアー チェーンの次のウィンドウにメッセージを送信します。
WM_DRAWCLIPBOARD
メッセージの処理例については、「クリップボード ビューアーの例」の一覧の例を参照してください。
クリップボード ビューアーの例
次の例は、単純なクリップボード ビューアー アプリケーションを示しています。
HINSTANCE hinst;
UINT uFormat = (UINT)(-1);
BOOL fAuto = TRUE;
LRESULT APIENTRY MainWndProc(hwnd, uMsg, wParam, lParam)
HWND hwnd;
UINT uMsg;
WPARAM wParam;
LPARAM lParam;
{
static HWND hwndNextViewer;
HDC hdc;
HDC hdcMem;
PAINTSTRUCT ps;
LPPAINTSTRUCT lpps;
RECT rc;
LPRECT lprc;
HGLOBAL hglb;
LPSTR lpstr;
HBITMAP hbm;
HENHMETAFILE hemf;
HWND hwndOwner;
switch (uMsg)
{
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
// Branch depending on the clipboard format.
switch (uFormat)
{
case CF_OWNERDISPLAY:
hwndOwner = GetClipboardOwner();
hglb = GlobalAlloc(GMEM_MOVEABLE,
sizeof(PAINTSTRUCT));
lpps = GlobalLock(hglb);
memcpy(lpps, &ps, sizeof(PAINTSTRUCT));
GlobalUnlock(hglb);
SendMessage(hwndOwner, WM_PAINTCLIPBOARD,
(WPARAM) hwnd, (LPARAM) hglb);
GlobalFree(hglb);
break;
case CF_BITMAP:
hdcMem = CreateCompatibleDC(hdc);
if (hdcMem != NULL)
{
if (OpenClipboard(hwnd))
{
hbm = (HBITMAP)
GetClipboardData(uFormat);
SelectObject(hdcMem, hbm);
GetClientRect(hwnd, &rc);
BitBlt(hdc, 0, 0, rc.right, rc.bottom,
hdcMem, 0, 0, SRCCOPY);
CloseClipboard();
}
DeleteDC(hdcMem);
}
break;
case CF_TEXT:
if (OpenClipboard(hwnd))
{
hglb = GetClipboardData(uFormat);
lpstr = GlobalLock(hglb);
GetClientRect(hwnd, &rc);
DrawText(hdc, lpstr, -1, &rc, DT_LEFT);
GlobalUnlock(hglb);
CloseClipboard();
}
break;
case CF_ENHMETAFILE:
if (OpenClipboard(hwnd))
{
hemf = GetClipboardData(uFormat);
GetClientRect(hwnd, &rc);
PlayEnhMetaFile(hdc, hemf, &rc);
CloseClipboard();
}
break;
case 0:
GetClientRect(hwnd, &rc);
DrawText(hdc, "The clipboard is empty.", -1,
&rc, DT_CENTER | DT_SINGLELINE |
DT_VCENTER);
break;
default:
GetClientRect(hwnd, &rc);
DrawText(hdc, "Unable to display format.", -1,
&rc, DT_CENTER | DT_SINGLELINE |
DT_VCENTER);
}
EndPaint(hwnd, &ps);
break;
case WM_SIZE:
if (uFormat == CF_OWNERDISPLAY)
{
hwndOwner = GetClipboardOwner();
hglb = GlobalAlloc(GMEM_MOVEABLE, sizeof(RECT));
lprc = GlobalLock(hglb);
GetClientRect(hwnd, lprc);
GlobalUnlock(hglb);
SendMessage(hwndOwner, WM_SIZECLIPBOARD,
(WPARAM) hwnd, (LPARAM) hglb);
GlobalFree(hglb);
}
break;
case WM_CREATE:
// Add the window to the clipboard viewer chain.
hwndNextViewer = SetClipboardViewer(hwnd);
break;
case WM_CHANGECBCHAIN:
// If the next window is closing, repair the chain.
if ((HWND) wParam == hwndNextViewer)
hwndNextViewer = (HWND) lParam;
// Otherwise, pass the message to the next link.
else if (hwndNextViewer != NULL)
SendMessage(hwndNextViewer, uMsg, wParam, lParam);
break;
case WM_DESTROY:
ChangeClipboardChain(hwnd, hwndNextViewer);
PostQuitMessage(0);
break;
case WM_DRAWCLIPBOARD: // clipboard contents changed.
// Update the window by using Auto clipboard format.
SetAutoView(hwnd);
// Pass the message to the next window in clipboard
// viewer chain.
SendMessage(hwndNextViewer, uMsg, wParam, lParam);
break;
case WM_INITMENUPOPUP:
if (!HIWORD(lParam))
InitMenu(hwnd, (HMENU) wParam);
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDM_EXIT:
DestroyWindow(hwnd);
break;
case IDM_AUTO:
SetAutoView(hwnd);
break;
default:
fAuto = FALSE;
uFormat = LOWORD(wParam);
InvalidateRect(hwnd, NULL, TRUE);
}
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return (LRESULT) NULL;
}
void WINAPI SetAutoView(HWND hwnd)
{
static UINT auPriorityList[] = {
CF_OWNERDISPLAY,
CF_TEXT,
CF_ENHMETAFILE,
CF_BITMAP
};
uFormat = GetPriorityClipboardFormat(auPriorityList, 4);
fAuto = TRUE;
InvalidateRect(hwnd, NULL, TRUE);
UpdateWindow(hwnd);
}
void WINAPI InitMenu(HWND hwnd, HMENU hmenu)
{
UINT uFormat;
char szFormatName[80];
LPCSTR lpFormatName;
UINT fuFlags;
UINT idMenuItem;
// If a menu is not the display menu, no initialization is necessary.
if (GetMenuItemID(hmenu, 0) != IDM_AUTO)
return;
// Delete all menu items except the first.
while (GetMenuItemCount(hmenu) > 1)
DeleteMenu(hmenu, 1, MF_BYPOSITION);
// Check or uncheck the Auto menu item.
fuFlags = fAuto ? MF_BYCOMMAND | MF_CHECKED :
MF_BYCOMMAND | MF_UNCHECKED;
CheckMenuItem(hmenu, IDM_AUTO, fuFlags);
// If there are no clipboard formats, return.
if (CountClipboardFormats() == 0)
return;
// Open the clipboard.
if (!OpenClipboard(hwnd))
return;
// Add a separator and then a menu item for each format.
AppendMenu(hmenu, MF_SEPARATOR, 0, NULL);
uFormat = EnumClipboardFormats(0);
while (uFormat)
{
// Call an application-defined function to get the name
// of the clipboard format.
lpFormatName = GetPredefinedClipboardFormatName(uFormat);
// For registered formats, get the registered name.
if (lpFormatName == NULL)
{
// Note that, if the format name is larger than the
// buffer, it is truncated.
if (GetClipboardFormatName(uFormat, szFormatName,
sizeof(szFormatName)))
lpFormatName = szFormatName;
else
lpFormatName = "(unknown)";
}
// Add a menu item for the format. For displayable
// formats, use the format ID for the menu ID.
if (IsDisplayableFormat(uFormat))
{
fuFlags = MF_STRING;
idMenuItem = uFormat;
}
else
{
fuFlags = MF_STRING | MF_GRAYED;
idMenuItem = 0;
}
AppendMenu(hmenu, fuFlags, idMenuItem, lpFormatName);
uFormat = EnumClipboardFormats(uFormat);
}
CloseClipboard();
}
BOOL WINAPI IsDisplayableFormat(UINT uFormat)
{
switch (uFormat)
{
case CF_OWNERDISPLAY:
case CF_TEXT:
case CF_ENHMETAFILE:
case CF_BITMAP:
return TRUE;
}
return FALSE;
}