Bagikan melalui


Menggunakan Clipboard

Bagian ini memiliki sampel kode untuk tugas-tugas berikut:

Menerapkan Perintah Potong, Salin, dan Tempel

Bagian ini menjelaskan bagaimana perintah Potongstandar , Salin, dan Tempel diterapkan dalam aplikasi. Contoh di bagian ini menggunakan metode ini untuk menempatkan data di clipboard menggunakan format clipboard terdaftar, format CF_OWNERDISPLAY, dan format CF_TEXT. 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 pemicu berfungsi. Untuk informasi selengkapnya tentang akselerator keyboard, lihat Keyboard Accelerators.

Memproses Pesan WM_INITMENUPOPUP

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

Berikut ini adalah kasus WM_INITMENUPOPUP untuk aplikasi bernama Label.

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

Fungsi 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 Pesan WM_COMMAND

Untuk memproses perintah menu, tambahkan kasus WM_COMMAND ke prosedur jendela utama aplikasi Anda. Berikut ini adalah kasus WM_COMMAND 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 Copy dan Cut, prosedur jendela memanggil fungsi EditCopy yang ditentukan aplikasi. Untuk informasi selengkapnya, lihat Menyalin Informasi ke Clipboard. Untuk melakukan perintah Tempel, prosedur jendela memanggil fungsi EditPaste yang ditentukan aplikasi. Untuk informasi selengkapnya tentang fungsi 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 clipboard dengan memanggil fungsi OpenClipboard.
  2. Mengosongkan clipboard dengan memanggil fungsi EmptyClipboard.
  3. Memanggil fungsi SetClipboardData sekali untuk setiap format clipboard yang disediakan aplikasi.
  4. Menutup clipboard dengan memanggil fungsi CloseClipboard.

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 fungsi 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 EditPaste yang ditentukan aplikasi menempelkan konten clipboard. Fungsi ini melakukan hal berikut:

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

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 LABELBOX yang ditentukan 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 fungsi 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 fungsi RegisterClipboardFormat 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;

Memproses Pesan WM_RENDERFORMAT dan WM_RENDERALLFORMATS

Jika jendela meneruskan handel NULL ke fungsi SetClipboardData, jendela harus memproses pesan WM_RENDERFORMAT 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 pegangan data non-NULL pada clipboard menggunakan fungsi SetClipboardData. Jika prosedur jendela merender format sebagai respons terhadap pesan WM_RENDERFORMAT, prosedur tersebut tidak boleh membuka clipboard sebelum memanggil SetClipboardData. Namun, jika sedang merender satu atau beberapa format sebagai respons terhadap pesan WM_RENDERALLFORMATS, maka harus membuka papan klip dan memastikan bahwa jendela masih memiliki papan klip tersebut sebelum memanggil SetClipboardData, dan papan klip harus ditutup sebelum kembali.

Aplikasi Label memproses pesan WM_RENDERFORMAT 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 RenderFormat yang ditentukan aplikasi, yang 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 Pesan WM_DESTROYCLIPBOARD

Jendela dapat memproses pesan WM_DESTROYCLIPBOARD 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 pesan WM_DESTROYCLIPBOARD, sebagai berikut.

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

Menggunakan Format Clipboard Owner-Display

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

  • Proses pesan WM_PAINTCLIPBOARD. Pesan ini dikirim ke pemilik clipboard ketika sebagian jendela penampil clipboard harus dicat ulang.
  • Proses pesan WM_SIZECLIPBOARD. 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. Menanggapi pesan ini, aplikasi Label juga memperbarui struktur SIZE untuk jendela penampil clipboard.
  • Proses pesan WM_HSCROLLCLIPBOARD dan WM_VSCROLLCLIPBOARD. Pesan ini dikirim ke pemilik clipboard ketika peristiwa bilah gulir terjadi di jendela penampil clipboard.
  • Proses pesan WM_ASKCBFORMATNAME. 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 ke belakang 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 isi clipboard diperbarui, sebuah nilai 32-bit yang dikenal sebagai nomor urutan clipboard ditingkatkan. Program dapat mengambil nomor urutan papan klip saat ini dengan memanggil fungsi GetClipboardSequenceNumber. 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 notifikasi dan tidak boleh digunakan dalam perulangan pengambilan suara. 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 dibandingkan dengan membuat jendela penampil clipboard karena lebih mudah untuk diterapkan dan menghindari masalah jika program gagal mempertahankan rantai penampil clipboard dengan benar atau jika ada jendela dalam rantai penampil clipboard yang berhenti merespons pesan.

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

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 rangkaian penampil clipboard.
  • Proses pesan WM_CHANGECBCHAIN.
  • Proses pesan WM_DRAWCLIPBOARD.
  • Hapus jendela dari rantai penampil clipboard sebelum dihancurkan.

Menambahkan Jendela pada Urutan Penampil Clipboard

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

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

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

Cuplikan kode ditampilkan untuk tugas-tugas berikut:

Memproses Pesan WM_CHANGECBCHAIN

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

Contoh berikut menunjukkan pemrosesan pesan 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;

Menghapus Jendela dari Rantai Penampil Clipboard

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

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

Memproses Pesan WM_DRAWCLIPBOARD

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

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

Untuk contoh pemrosesan pesan WM_DRAWCLIPBOARD, 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; 
}