Menggunakan Clipboard

Bagian ini memiliki sampel kode untuk tugas-tugas berikut:

Menerapkan Perintah Potong, Salin, dan Tempel

Bagian ini menjelaskan bagaimana perintah Potong, Salin, dan Tempel standar diterapkan dalam aplikasi. Contoh di bagian ini menggunakan metode ini untuk menempatkan data di clipboard menggunakan format clipboard terdaftar, CF_OWNERDISPLAY format, dan CF_TEXT format . Format terdaftar digunakan untuk mewakili jendela teks persegi panjang atau elips, yang disebut label.

Memilih Data

Sebelum informasi dapat disalin ke clipboard, pengguna harus memilih informasi tertentu untuk disalin atau dipotong. Aplikasi harus menyediakan sarana bagi pengguna untuk memilih informasi dalam dokumen dan beberapa jenis umpan balik visual untuk menunjukkan data yang dipilih.

Membuat Menu Edit

Aplikasi harus memuat tabel akselerator yang berisi akselerator keyboard standar untuk perintah menu Edit . Fungsi TranslateAccelerator harus ditambahkan ke perulangan pesan aplikasi agar akselerator berlaku. Untuk informasi selengkapnya tentang akselerator keyboard, lihat Akselerator Keyboard.

Memproses WM_INITMENUPOPUP Pesan

Tidak semua perintah clipboard tersedia untuk pengguna pada waktu tertentu. Aplikasi harus memproses WM_INITMENUPOPUP pesan untuk mengaktifkan item menu untuk perintah yang tersedia dan menonaktifkan perintah yang tidak tersedia.

Berikut ini adalah WM_INITMENUPOPUP kasus untuk aplikasi bernama Label.

case WM_INITMENUPOPUP:
    InitMenu((HMENU) wParam);
    break;

Fungsi ini InitMenu didefinisikan sebagai berikut.

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

Memproses WM_COMMAND Pesan

Untuk memproses perintah menu, tambahkan kasus ke WM_COMMAND prosedur jendela utama aplikasi Anda. Berikut ini adalah WM_COMMAND kasus untuk prosedur jendela aplikasi Label.

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; 

Untuk melakukan perintah Salin dan Potong , prosedur jendela memanggil fungsi yang ditentukan EditCopy aplikasi. Untuk informasi selengkapnya, lihat Menyalin Informasi ke Clipboard. Untuk melakukan perintah Tempel , prosedur jendela memanggil fungsi yang ditentukan EditPaste aplikasi. Untuk informasi selengkapnya tentang fungsi ini EditPaste , lihat Menempelkan Informasi dari Clipboard.

Menyalin Informasi ke Clipboard

Dalam aplikasi Label, fungsi EditCopy yang ditentukan aplikasi menyalin pilihan saat ini ke clipboard. Fungsi ini melakukan hal berikut:

  1. Membuka papan klip dengan memanggil OpenClipboard fungsi.
  2. Empties clipboard dengan memanggil EmptyClipboard fungsi .
  3. SetClipboardData Memanggil fungsi sekali untuk setiap format clipboard yang disediakan aplikasi.
  4. Menutup papan klip dengan memanggil CloseClipboard fungsi.

Bergantung pada pilihan saat ini, fungsi EditCopy menyalin rentang teks atau menyalin struktur yang ditentukan aplikasi yang mewakili seluruh label. Struktur, yang disebut LABELBOX, didefinisikan sebagai berikut.

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

Berikut ini adalah fungsinya 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; 
}

Menempelkan Informasi dari Clipboard

Dalam aplikasi Label, fungsi yang ditentukan EditPaste aplikasi menempelkan konten clipboard. Fungsi ini melakukan hal berikut:

  1. Membuka papan klip dengan memanggil OpenClipboard fungsi.
  2. Menentukan format clipboard mana yang tersedia untuk diambil.
  3. Mengambil handel ke data dalam format yang dipilih dengan memanggil GetClipboardData fungsi .
  4. Menyisipkan salinan data ke dalam dokumen. Handel yang dikembalikan oleh GetClipboardData masih dimiliki oleh clipboard, sehingga aplikasi tidak boleh membebaskannya atau membiarkannya terkunci.
  5. Menutup papan klip dengan memanggil CloseClipboard fungsi.

Jika label dipilih dan berisi titik penyisipan, fungsi EditPaste menyisipkan teks dari clipboard pada titik penyisipan. Jika tidak ada pilihan atau jika label dipilih, fungsi membuat label baru, menggunakan struktur yang ditentukan LABELBOX aplikasi pada clipboard. Struktur LABELBOX ditempatkan pada clipboard dengan menggunakan format clipboard terdaftar.

Struktur, yang disebut LABELBOX, didefinisikan sebagai berikut.

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

Berikut ini adalah fungsinya 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(); 
}

Mendaftarkan Format Clipboard

Untuk mendaftarkan format clipboard, tambahkan panggilan ke RegisterClipboardFormat fungsi ke fungsi inisialisasi instans aplikasi Anda, sebagai berikut.

// 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 Memproses pesan dan WM_RENDERALLFORMATS

Jika jendela meneruskan NULL handel ke SetClipboardData fungsi, jendela harus memproses WM_RENDERFORMAT pesan dan WM_RENDERALLFORMATS untuk merender data berdasarkan permintaan.

Jika jendela menunda penyajian format tertentu lalu aplikasi lain meminta data dalam format tersebut, maka pesan WM_RENDERFORMAT dikirim ke jendela. Selain itu, jika jendela menunda penyajian satu atau beberapa format, dan jika beberapa format tersebut tetap tidak dirender ketika jendela akan dihancurkan, maka pesan WM_RENDERALLFORMATS dikirim ke jendela sebelum penghancurannya.

Untuk merender format clipboard, prosedur jendela harus menempatkan handel non-dataNULL pada clipboard menggunakan SetClipboardData fungsi . Jika prosedur jendela merender format sebagai respons terhadap WM_RENDERFORMAT pesan, itu tidak boleh membuka clipboard sebelum memanggil SetClipboardData. Tetapi jika merender satu atau beberapa format sebagai respons terhadap WM_RENDERALLFORMATS pesan, itu harus membuka clipboard dan memeriksa bahwa jendela masih memiliki clipboard sebelum memanggil SetClipboardData, dan harus menutup clipboard sebelum kembali.

Aplikasi Label memproses WM_RENDERFORMAT pesan dan WM_RENDERALLFORMATS sebagai berikut.

case WM_RENDERFORMAT: 
    RenderFormat((UINT) wParam); 
    break; 
 
case WM_RENDERALLFORMATS:
    if (OpenClipboard(hwnd))
    {
        if (GetClipboardOwner() == hwnd)
        {
            RenderFormat(uLabelFormat);
            RenderFormat(CF_TEXT);
        }
        CloseClipboard();
    }
    break;

Dalam kedua kasus, prosedur jendela memanggil fungsi yang ditentukan RenderFormat aplikasi, didefinisikan sebagai berikut.

Struktur, yang disebut LABELBOX, didefinisikan sebagai berikut.

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

Memproses WM_DESTROYCLIPBOARD Pesan

Jendela dapat memproses WM_DESTROYCLIPBOARD pesan untuk membebaskan sumber daya apa pun yang disisihkannya untuk mendukung penyajian yang tertunda. Misalnya aplikasi Label, saat menyalin label ke clipboard, mengalokasikan objek memori lokal. Kemudian membebaskan objek ini sebagai respons terhadap WM_DESTROYCLIPBOARD pesan, sebagai berikut.

case WM_DESTROYCLIPBOARD: 
    if (pboxLocalClip != NULL) 
    { 
        LocalFree(pboxLocalClip); 
        pboxLocalClip = NULL; 
    } 
    break;

Menggunakan Format Clipboard Tampilan Pemilik

Jika jendela menempatkan informasi pada clipboard dengan menggunakan CF_OWNERDISPLAY format clipboard, jendela harus melakukan hal berikut:

  • WM_PAINTCLIPBOARD Proses pesan. Pesan ini dikirim ke pemilik clipboard ketika sebagian jendela penampil clipboard harus dicat ulang.
  • WM_SIZECLIPBOARD Proses pesan. Pesan ini dikirim ke pemilik papan klip ketika jendela penampil papan klip telah diubah ukurannya atau isinya telah berubah. Biasanya, jendela merespons pesan ini dengan mengatur posisi dan rentang gulir untuk jendela penampil clipboard. Sebagai respons terhadap pesan ini, aplikasi Label juga memperbarui SIZE struktur untuk jendela penampil clipboard.
  • WM_HSCROLLCLIPBOARD Memproses pesan dan WM_VSCROLLCLIPBOARD . Pesan ini dikirim ke pemilik clipboard ketika peristiwa bilah gulir terjadi di jendela penampil clipboard.
  • WM_ASKCBFORMATNAME Proses pesan. Jendela penampil clipboard mengirimkan pesan ini ke aplikasi untuk mengambil nama format tampilan pemilik.

Prosedur jendela untuk aplikasi Label memproses pesan ini, sebagai berikut.

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

Memantau Isi Papan Klip

Ada tiga cara memantau perubahan pada clipboard. Metode terlama adalah membuat jendela penampil clipboard. Windows 2000 menambahkan kemampuan untuk mengkueri nomor urutan clipboard, dan Windows Vista menambahkan dukungan untuk pendengar format clipboard. Jendela penampil Clipboard didukung untuk kompatibilitas mundur dengan versi Windows yang lebih lama. Program baru harus menggunakan pendengar format papan klip atau nomor urutan papan klip.

Mengkueri Nomor Urutan Clipboard

Setiap kali konten clipboard berubah, nilai 32-bit yang dikenal sebagai nomor urutan clipboard bertahap. Program dapat mengambil nomor urutan papan klip saat ini dengan memanggil GetClipboardSequenceNumber fungsi. Dengan membandingkan nilai yang dikembalikan terhadap nilai yang dikembalikan oleh panggilan sebelumnya ke GetClipboardSequenceNumber, program dapat menentukan apakah konten clipboard telah berubah. Metode ini lebih cocok untuk program yang hasil cache berdasarkan isi papan klip saat ini dan perlu mengetahui apakah perhitungan masih valid sebelum menggunakan hasil dari cache tersebut. Perhatikan bahwa ini bukan metode pemberitahuan dan tidak boleh digunakan dalam perulangan polling. Untuk diberi tahu ketika konten clipboard berubah, gunakan pendengar format clipboard atau penampil clipboard.

Membuat Pendengar Format Clipboard

Pendengar format clipboard adalah jendela yang telah terdaftar untuk diberi tahu ketika konten clipboard telah berubah. Metode ini direkomendasikan untuk membuat jendela penampil clipboard karena lebih mudah untuk menerapkan dan menghindari masalah jika program gagal mempertahankan rantai penampil clipboard dengan benar atau jika jendela di rantai penampil clipboard berhenti merespons pesan.

Jendela mendaftar sebagai pendengar format clipboard dengan memanggil AddClipboardFormatListener fungsi . Ketika konten clipboard berubah, jendela diposting pesan WM_CLIPBOARDUPDATE . Pendaftaran tetap berlaku sampai jendela membatalkan pendaftaran sendiri dengan memanggil RemoveClipboardFormatListener fungsi .

Membuat Jendela Penampil Clipboard

Jendela penampil papan klip menampilkan isi papan klip saat ini, dan menerima pesan ketika isi papan klip berubah. Untuk membuat jendela penampil clipboard, aplikasi Anda harus melakukan hal berikut:

  • Tambahkan jendela ke rantai penampil clipboard.
  • WM_CHANGECBCHAIN Proses pesan.
  • WM_DRAWCLIPBOARD Proses pesan.
  • Hapus jendela dari rantai penampil clipboard sebelum dihancurkan.

Menambahkan Jendela ke Rantai Penampil Clipboard

Jendela menambahkan dirinya ke rantai penampil clipboard dengan memanggil SetClipboardViewer fungsi . Nilai yang dikembalikan adalah handel ke jendela berikutnya dalam rantai. Jendela harus melacak nilai ini — misalnya, dengan menyimpannya dalam variabel statis bernama hwndNextViewer.

Contoh berikut menambahkan jendela ke rantai penampil clipboard sebagai respons terhadap WM_CREATE pesan.

case WM_CREATE: 
 
    // Add the window to the clipboard viewer chain. 
 
    hwndNextViewer = SetClipboardViewer(hwnd); 
    break;

Cuplikan kode ditampilkan untuk tugas-tugas berikut:

Memproses WM_CHANGECBCHAIN Pesan

Jendela penampil clipboard menerima WM_CHANGECBCHAIN pesan ketika jendela lain menghapus dirinya sendiri dari rantai penampil clipboard. Jika jendela yang dihapus adalah jendela berikutnya dalam rantai, jendela yang menerima pesan harus membatalkan tautan jendela berikutnya dari rantai. Jika tidak, pesan ini harus diteruskan ke jendela berikutnya dalam rantai.

Contoh berikut menunjukkan pemrosesan WM_CHANGECBCHAIN pesan.

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;

Menghapus Jendela dari Rantai Penampil Clipboard

Untuk menghapus dirinya sendiri dari rantai penampil clipboard, jendela memanggil ChangeClipboardChain fungsi. Contoh berikut menghapus jendela dari rantai penampil clipboard sebagai respons terhadap WM_DESTROY pesan.

case WM_DESTROY: 
    ChangeClipboardChain(hwnd, hwndNextViewer); 
    PostQuitMessage(0); 
    break;

Memproses WM_DRAWCLIPBOARD Pesan

Pesan WM_DRAWCLIPBOARD memberi tahu jendela penampil papan klip bahwa isi papan klip telah berubah. Jendela harus melakukan hal berikut saat memproses WM_DRAWCLIPBOARD pesan:

  1. Tentukan format papan klip mana yang tersedia untuk ditampilkan.
  2. Ambil data clipboard dan tampilkan di jendela. Atau jika format clipboard adalah CF_OWNERDISPLAY, kirim pesan WM_PAINTCLIPBOARD ke pemilik clipboard.
  3. Kirim pesan ke jendela berikutnya di rantai penampil clipboard.

Untuk contoh pemrosesan WM_DRAWCLIPBOARD pesan, lihat contoh daftar di Contoh Penampil Clipboard.

Contoh Penampil Papan Klip

Contoh berikut menunjukkan aplikasi penampil clipboard sederhana.

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