次の方法で共有


クリップボードの使用

このセクションには、次のタスクのコード サンプルがあります。

[切り取り]、[コピー]、[貼り付け] コマンドを実装する

このセクションでは、標準の 切り取りコピー貼り付け コマンドをアプリケーションに実装する方法について説明します。 このセクションの例では、これらのメソッドを使用して、登録済みのクリップボード形式、 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 関数によって、現在の選択範囲がクリップボードにコピーされます。 この関数は、次のことを行います。

  1. OpenClipboard 関数を呼び出してクリップボードを開きます。
  2. EmptyClipboard 関数を呼び出してクリップボードを空にします。
  3. アプリケーションが提供するクリップボード形式ごとに SetClipboardData 関数を 1 回呼び出します。
  4. 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 関数によってクリップボードの内容が貼り付けられます。 この関数は、次のことを行います。

  1. OpenClipboard 関数を呼び出してクリップボードを開きます。
  2. 取得可能なクリップボードの形式を決定する。
  3. GetClipboardData 関数を呼び出して、選択した形式のデータへのハンドルを取得します。
  4. データのコピーをドキュメントに挿入します。 GetClipboardData によって返されるハンドルは引き続きクリップボードによって所有されているため、アプリケーションで解放したり、ロックしたままにしたりしないでください。
  5. 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_RENDERFORMATWM_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_HSCROLLCLIPBOARDWM_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 メッセージを処理するときに、ウィンドウで次の操作を行う必要があります。

  1. 表示する使用可能なクリップボード形式を決定します。
  2. クリップボード データを取得し、ウィンドウに表示します。 または、クリップボード形式が CF_OWNERDISPLAYされている場合は、 WM_PAINTCLIPBOARD メッセージをクリップボードの所有者に送信します。
  3. クリップボード ビューアー チェーンの次のウィンドウにメッセージを送信します。

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; 
} 

キーボード アクセラレータの