Bagikan melalui


Menggunakan Pertukaran Data Dinamis

Bagian ini memiliki sampel kode pada tugas berikut:

Memulai Percakapan

Untuk memulai percakapan Dynamic Data Exchange (DDE), klien mengirim pesan WM_DDE_INITIATE. Biasanya, klien menyiarkan pesan ini dengan memanggil SendMessage, dengan -1 sebagai parameter pertama. Jika aplikasi sudah memiliki handel jendela ke aplikasi server, aplikasi dapat mengirim pesan langsung ke jendela tersebut. Klien menyiapkan atom untuk nama aplikasi dan nama topik dengan memanggil GlobalAddAtom. Klien dapat meminta percakapan dengan aplikasi server potensial dan untuk topik potensial apa pun dengan menyediakan atom NULL (wildcard) untuk aplikasi dan topik.

Contoh berikut menggambarkan bagaimana klien memulai percakapan, di mana aplikasi dan topik ditentukan.

    static BOOL fInInitiate = FALSE; 
    char *szApplication; 
    char *szTopic; 
    atomApplication = *szApplication == 0 ? 
    NULL     : GlobalAddAtom((LPSTR) szApplication); 
    atomTopic = *szTopic == 0 ? 
    NULL     : GlobalAddAtom((LPSTR) szTopic); 
 
    fInInitiate = TRUE; 
    SendMessage((HWND) HWND_BROADCAST, // broadcasts message 
        WM_DDE_INITIATE,               // initiates conversation 
        (WPARAM) hwndClientDDE,        // handle to client DDE window 
        MAKELONG(atomApplication,      // application-name atom 
            atomTopic));               // topic-name atom 
    fInInitiate = FALSE; 
    if (atomApplication != NULL) 
        GlobalDeleteAtom(atomApplication); 
    if (atomTopic != NULL) 
        GlobalDeleteAtom(atomTopic);

Catatan

Jika aplikasi Anda menggunakan atom NULL, Anda tidak perlu menggunakan fungsi GlobalAddAtom dan GlobalDeleteAtom. Dalam contoh ini, aplikasi klien membuat dua atom global yang masing-masing berisi nama server dan nama topik.

 

Aplikasi klien mengirim pesan WM_DDE_INITIATE dengan kedua atom ini dalam parameter lParam pesan. Dalam panggilan ke fungsi SendMessage , handel jendela khusus –1 mengarahkan sistem untuk mengirim pesan ini ke semua aplikasi aktif lainnya. SendMessage tidak kembali ke aplikasi klien sampai semua aplikasi yang menerima pesan, pada gilirannya, mengembalikan kontrol ke sistem. Ini berarti bahwa semua pesan WM_DDE_ACK yang dikirim dalam balasan oleh aplikasi server dijamin telah diproses oleh klien pada saat panggilan SendMessage telah kembali.

Setelah SendMessage kembali, aplikasi klien menghapus atom global.

Aplikasi server merespons sesuai dengan logika yang diilustrasikan dalam diagram berikut.

server application response logic

Untuk mengakui satu atau beberapa topik, server harus membuat atom untuk setiap percakapan (memerlukan atom nama aplikasi duplikat jika ada beberapa topik) dan mengirim pesan WM_DDE_ACK untuk setiap percakapan, seperti yang diilustrasikan dalam contoh berikut.

if ((atomApplication = GlobalAddAtom("Server")) != 0) 
{ 
    if ((atomTopic = GlobalAddAtom(szTopic)) != 0) 
    { 
        SendMessage(hwndClientDDE, 
            WM_DDE_ACK, 
            (WPARAM) hwndServerDDE, 
            MAKELONG(atomApplication, atomTopic)); 
        GlobalDeleteAtom(atomTopic); 
    } 
 
    GlobalDeleteAtom(atomApplication); 
} 
 
if ((atomApplication == 0) || (atomTopic == 0)) 
{ 
    // Handle errors. 
}

Ketika server merespons dengan pesan WM_DDE_ACK , aplikasi klien harus menyimpan handel ke jendela server. Klien yang menerima handel sebagai parameter wParam dari pesan WM_DDE_ACK kemudian mengirim semua pesan DDE berikutnya ke jendela server yang diidentifikasi oleh handel ini.

Jika aplikasi klien Anda menggunakan atom NULL untuk nama aplikasi atau nama topik, harapkan aplikasi menerima pengakuan dari lebih dari satu aplikasi server. Beberapa pengakuan juga dapat berasal dari beberapa instans server DDE, bahkan jika aplikasi klien Anda tidak MENGGUNAKAN atom NULL . Server harus selalu menggunakan jendela unik untuk setiap percakapan. Prosedur jendela dalam aplikasi klien dapat menggunakan handel ke jendela server (disediakan sebagai parameter lParam WM_DDE_INITIATE) untuk melacak beberapa percakapan. Ini memungkinkan satu jendela klien untuk memproses beberapa percakapan tanpa perlu mengakhiri dan menyambungkan kembali dengan jendela klien baru untuk setiap percakapan.

Mentransfer Satu Item

Setelah percakapan DDE dibuat, klien dapat mengambil nilai item data dari server dengan mengeluarkan pesan WM_DDE_REQUEST, atau mengirimkan nilai item data ke server dengan mengeluarkan WM_DDE_POKE.

Mengambil Item dari Server

Untuk mengambil item dari server, klien mengirim pesan WM_DDE_REQUEST server yang menentukan item dan format yang akan diambil, seperti yang ditunjukkan dalam contoh berikut.

if ((atomItem = GlobalAddAtom(szItemName)) != 0) 
{ 
    if (!PostMessage(hwndServerDDE, 
            WM_DDE_REQUEST, 
            (WPARAM) hwndClientDDE, 
            PackDDElParam(WM_DDE_REQUEST, CF_TEXT, atomItem))) 
    {
        GlobalDeleteAtom(atomItem); 
    }
} 
 
if (atomItem == 0) 
{ 
    // Handle errors. 
}

Dalam contoh ini, klien menentukan format clipboard CF_TEXT sebagai format pilihan untuk item data yang diminta.

Penerima (server) pesan WM_DDE_REQUEST biasanya harus menghapus atom item, tetapi jika panggilan PostMessage gagal, klien harus menghapus atom.

Jika server memiliki akses ke item yang diminta dan dapat merendernya dalam format yang diminta, server menyalin nilai item sebagai objek memori bersama dan mengirim pesan WM_DDE_DATA kepada klien, seperti yang diilustrasikan dalam contoh berikut.

// Allocate the size of the DDE data header, plus the data: a 
// string,<CR><LF><NULL>. The byte for the string's terminating 
// null character is counted by DDEDATA.Value[1].

size_t* pcch;
HRESULT hResult;
 
hResult = StringCchLength(szItemValue,STRSAFE_MAX_CCH, pcch);
if (FAILED(hResult))
{
// TODO: Write error handler.
 return;
}
if (!(hData = GlobalAlloc(GMEM_MOVEABLE,
  (LONG) sizeof(DDEDATA) + *pcch + 2)))  
{
    return; 
}
 
if (!(lpData = (DDEDATA FAR*) GlobalLock(hData)))  
{
    GlobalFree(hData); 
    return; 
} 
 
lpData->cfFormat = CF_TEXT;
hResult = StringCchCopy((LPSTR) lpData->Value, *pcch +1, (LPCSTR) szItemValue); // copies value to be sent
if (FAILED(hResult))
{
// TODO: Write error handler.
 return;
}
 
// Each line of CF_TEXT data is terminated by CR/LF. 
hResult = StringCchCat((LPSTR) lpData->Value, *pcch + 3, (LPCSTR) "\r\n");
if (FAILED(hResult)
{
// TODO: Write error handler.
 return;
} 
GlobalUnlock(hData); 
if ((atomItem = GlobalAddAtom((LPSTR) szItemName)) != 0) 
{ 
    lParam = PackDDElParam(WM_DDE_ACK, (UINT) hData, atomItem); 
    if (!PostMessage(hwndClientDDE, 
            WM_DDE_DATA, 
            (WPARAM) hwndServerDDE, 
            lParam)) 
    { 
        GlobalFree(hData); 
        GlobalDeleteAtom(atomItem); 
        FreeDDElParam(WM_DDE_ACK, lParam); 
    } 
} 
 
if (atomItem == 0) 
{ 
    // Handle errors.  
}

Dalam contoh ini, aplikasi server mengalokasikan objek memori untuk berisi item data. Objek data diinisialisasi sebagai struktur DDEDATA.

Aplikasi server kemudian mengatur anggota cfFormat struktur ke CF_TEXT untuk memberi tahu aplikasi klien bahwa data dalam format teks. Klien merespons dengan menyalin nilai data yang diminta ke anggota Nilai dari struktur DDEDATA. Setelah server mengisi objek data, server membuka kunci data dan membuat atom global yang berisi nama item data.

Terakhir, server mengeluarkan pesan WM_DDE_DATA dengan memanggil PostMessage. Handel ke objek data dan atom yang berisi nama item dikemas ke dalam parameter lParam pesan oleh fungsi PackDDElParam.

Jika PostMessage gagal, server harus menggunakan fungsi FreeDDElParam untuk membebaskan parameter lParam yang dikemas. Server juga harus membebaskan parameter lParam yang dikemas untuk pesan WM_DDE_REQUEST yang diterimanya.

Jika server tidak dapat memenuhi permintaan, server mengirimkan pesan WM_DDE_ACK negatif ke klien, seperti yang ditunjukkan dalam contoh berikut.

// Negative acknowledgment. 
 
PostMessage(hwndClientDDE, 
    WM_DDE_ACK, 
    (WPARAM) hwndServerDDE, 
    PackDDElParam(WM_DDE_ACK, 0, atomItem));

Setelah menerima pesan WM_DDE_DATA , klien memproses nilai item data yang sesuai. Kemudian, jika anggota fAckReq yang ditunjukkan dalam pesan WM_DDE_DATA adalah 1, klien harus mengirim pesan WM_DDE_ACK positif kepada server, seperti yang ditunjukkan dalam contoh berikut.

UnpackDDElParam(WM_DDE_DATA, lParam, (PUINT) &hData, 
    (PUINT) &atomItem); 
if (!(lpDDEData = (DDEDATA FAR*) GlobalLock(hData)) 
        || (lpDDEData->cfFormat != CF_TEXT)) 
{ 
    PostMessage(hwndServerDDE, 
        WM_DDE_ACK, 
        (WPARAM) hwndClientDDE, 
        PackDDElParam(WM_DDE_ACK, 0, atomItem)); // Negative ACK. 
} 
 
// Copy data from lpDDEData here. 
 
if (lpDDEData->fAckReq) 
{ 
    PostMessage(hwndServerDDE, 
        WM_DDE_ACK, 
        (WPARAM) hwndClientDDE, 
        PackDDElParam(WM_DDE_ACK, 0x8000, 
            atomItem)); // Positive ACK 
} 
 
bRelease = lpDDEData->fRelease; 
GlobalUnlock(hData); 
if (bRelease) 
    GlobalFree(hData);

Dalam contoh ini, klien memeriksa format data. Jika format tidak CF_TEXT (atau jika klien tidak dapat mengunci memori untuk data), klien mengirim pesan WM_DDE_ACK negatif untuk menunjukkan bahwa ia tidak dapat memproses data. Jika klien tidak dapat mengunci handel data karena handel berisi anggota fAckReq, klien tidak boleh mengirim pesan WM_DDE_ACK negatif. Sebagai gantinya, klien harus mengakhiri percakapan.

Jika klien mengirim pengakuan negatif sebagai respons terhadap pesan WM_DDE_DATA, server bertanggung jawab untuk membebaskan memori (tetapi bukan parameter lParam) yang dirujuk oleh pesan WM_DDE_DATA yang terkait dengan pengakuan negatif.

Jika dapat memproses data, klien memeriksa anggota fAckReq dari struktur DDEDATA untuk menentukan apakah server meminta agar diberi tahu bahwa klien menerima dan memproses data dengan sukses. Jika server meminta informasi ini, klien mengirimkan pesan WM_DDE_ACK positif kepada server.

Karena membuka kunci data membatalkan penunjuk ke data, klien menyimpan nilai anggota fRelease sebelum membuka kunci objek data. Setelah menyimpan nilai, klien kemudian memeriksanya untuk menentukan apakah aplikasi server meminta klien untuk membebaskan memori yang berisi data; klien bertindak sesuai.

Setelah menerima pesan WM_DDE_ACK negatif, klien dapat meminta nilai item yang sama lagi, menentukan format clipboard yang berbeda. Biasanya, klien pertama-tama akan meminta format paling kompleks yang dapat didukungnya, lalu mundur jika perlu melalui format yang semakin sederhana sampai menemukan salah satu server dapat menyediakannya.

Jika server mendukung item Format topik sistem, klien dapat menentukan setelah format clipboard apa yang didukung server, alih-alih menentukannya setiap kali klien meminta item.

Mengirimkan Item ke Server

Klien dapat mengirim nilai item ke server dengan menggunakan pesan WM_DDE_POKE. Klien merender item yang akan dikirim dan mengirim pesan WM_DDE_POKE , seperti yang diilustrasikan dalam contoh berikut.

size_t* pcch;
HRESULT hResult;
 
hResult = StringCchLength(szValue,STRSAFE_MAX_CCH, pcch);
if (FAILED(hResult))
{
// TODO: Write error handler.
 return;
}
if (!(hPokeData = GlobalAlloc(GMEM_MOVEABLE,
  (LONG) sizeof(DDEPOKE) + *pcch + 2))) 
{
    return; 
}
 
if (!(lpPokeData = (DDEPOKE *) GlobalLock(hPokeData))) 
{ 
    GlobalFree(hPokeData); 
    return; 
} 
 
lpPokeData->fRelease = TRUE; 
lpPokeData->cfFormat = CF_TEXT;
hResult = StringCchCopy((LPSTR) lpPokeData->Value, *pcch +1, (LPCSTR) szValue); // copies value to be sent
if (FAILED(hResult))
{
// TODO: Write error handler.
 return;
}  
 
// Each line of CF_TEXT data is terminated by CR/LF. 
hResult = StringCchCat((LPSTR) lpPokeData->Value, *pcch + 3, (LPCSTR) "\r\n");
if (FAILED(hResult)
{
// TODO: Write error handler.
 return;
}
GlobalUnlock(hPokeData); 
if ((atomItem = GlobalAddAtom((LPSTR) szItem)) != 0) 
{ 
 
        if (!PostMessage(hwndServerDDE, 
                WM_DDE_POKE, 
                (WPARAM) hwndClientDDE, 
                PackDDElParam(WM_DDE_POKE, (UINT) hPokeData, 
                    atomItem))) 
        { 
            GlobalDeleteAtom(atomItem); 
            GlobalFree(hPokeData); 
        } 
} 
 
if (atomItem == 0) 
{ 
    // Handle errors. 
} 

Catatan

Mengirim data dengan menggunakan pesan WM_DDE_POKE pada dasarnya sama dengan mengirimnya dengan menggunakan WM_DDE_DATA, kecuali bahwa WM_DDE_POKE dikirim dari klien ke server.

 

Jika server dapat menerima nilai item data dalam format yang dirender oleh klien, server memproses nilai item yang sesuai dan mengirimi klien pesan WM_DDE_ACK positif. Jika tidak dapat memproses nilai item, karena formatnya atau karena alasan lain, server mengirimkan pesan WM_DDE_ACK negatif kepada klien.

UnpackDDElParam(WM_DDE_POKE, lParam, (PUINT) &hPokeData, 
    (PUINT) &atomItem); 
GlobalGetAtomName(atomItem, szItemName, ITEM_NAME_MAX_SIZE); 
if (!(lpPokeData = (DDEPOKE *) GlobalLock(hPokeData)) 
        || lpPokeData->cfFormat != CF_TEXT 
        || !IsItemSupportedByServer(szItemName)) 
{ 
    PostMessage(hwndClientDDE, 
        WM_DDE_ACK, 
        (WPARAM) hwndServerDDE, 
        PackDDElParam(WM_DDE_ACK, 0, atomItem)); // negative ACK  
}
hResult = StringCchLength(szItemValue,STRSAFE_MAX_CCH, pcch);
if (FAILED(hResult))
{
// TODO: Write error handler.
 return;
} 
hResult = StringCchCopy(szItemValue, *pcch +1, lpPokeData->Value); // copies value 
if (FAILED(hResult))
{
// TODO: Write error handler.
 return;
}  
bRelease = lpPokeData->fRelease; 
GlobalUnlock(hPokeData); 
if (bRelease) 
{ 
    GlobalFree(hPokeData); 
} 
 
PostMessage(hwndClientDDE, 
    WM_DDE_ACK, 
    (WPARAM) hwndServerDDE, 
    PackDDElParam(WM_DDE_ACK, 
         0x8000, atomItem));    // positive ACK.

Dalam contoh ini, server memanggil GlobalGetAtomName untuk mengambil nama item yang dikirim klien. Server kemudian menentukan apakah mendukung item dan apakah item dirender dalam format yang benar (yaitu, CF_TEXT). Jika item tidak didukung dan tidak dirender dalam format yang benar, atau jika server tidak dapat mengunci memori untuk data, server mengirimkan pengakuan negatif kembali ke aplikasi klien. Perhatikan bahwa dalam hal ini, mengirim pengakuan negatif sudah benar karena pesan WM_DDE_POKE selalu diasumsikan memiliki anggota fAckReq yang ditetapkan. Server harus mengabaikan anggota.

Jika server mengirim pengakuan negatif sebagai respons terhadap pesan WM_DDE_POKE, klien bertanggung jawab untuk membebaskan memori (tetapi bukan parameter lParam) yang dirujuk oleh pesan WM_DDE_POKE yang terkait dengan pengakuan negatif.

Aplikasi klien dapat menggunakan DDE untuk membuat tautan ke item di aplikasi server. Setelah tautan tersebut dibuat, server mengirimkan pembaruan berkala dari item yang ditautkan ke klien, biasanya, setiap kali nilai item berubah. Dengan demikian, aliran data permanen dibuat di antara kedua aplikasi; aliran data ini tetap ada sampai terputus secara eksplisit.

Klien memulai tautan data dengan memposting pesan WM_DDE_ADVISE, seperti yang ditunjukkan dalam contoh berikut.

if (!(hOptions = GlobalAlloc(GMEM_MOVEABLE, 
        sizeof(DDEADVISE)))) 
    return; 
if (!(lpOptions = (DDEADVISE FAR*) GlobalLock(hOptions))) 
{ 
    GlobalFree(hOptions); 
    return; 
} 
 
lpOptions->cfFormat = CF_TEXT; 
lpOptions->fAckReq = TRUE; 
lpOptions->fDeferUpd = FALSE; 
GlobalUnlock(hOptions); 
if ((atomItem = GlobalAddAtom(szItemName)) != 0) 
{ 
    if (!(PostMessage(hwndServerDDE, 
            WM_DDE_ADVISE, 
            (WPARAM) hwndClientDDE, 
            PackDDElParam(WM_DDE_ADVISE, (UINT) hOptions, 
                atomItem)))) 
    { 
        GlobalDeleteAtom(atomItem); 
        GlobalFree(hOptions); 
        FreeDDElParam(WM_DDE_ADVISE, lParam); 
    } 
} 
 
if (atomItem == 0) 
{ 
    // Handle errors 
 
}

Dalam contoh ini, aplikasi klien mengatur bendera fDeferUpd dari pesan WM_DDE_ADVISE ke FALSE. Ini mengarahkan aplikasi server untuk mengirim data ke klien setiap kali data berubah.

Jika server tidak dapat melayani permintaan WM_DDE_ADVISE, server mengirimkan pesan WM_DDE_ACK negatif kepada klien. Tetapi jika server memiliki akses ke item dan dapat merendernya dalam format yang diminta, server mencatat tautan baru (memanggil kembali bendera yang ditentukan dalam parameter hOptions) dan mengirim pesan WM_DDE_ACK positif kepada klien. Sejak saat itu, sampai klien mengeluarkan pesan WM_DDE_UNADVISE yang cocok, server mengirim data baru ke klien setiap kali nilai item berubah di server.

Pesan WM_DDE_ADVISE menetapkan format data yang akan ditukar selama tautan. Jika klien mencoba membuat tautan lain dengan item yang sama tetapi menggunakan format data yang berbeda, server dapat memilih untuk menolak format data kedua atau mencoba mendukungnya. Jika tautan hangat telah dibuat untuk item data apa pun, server hanya dapat mendukung satu format data pada satu waktu. Ini karena pesan WM_DDE_DATA untuk tautan hangat memiliki handel data NULL, yang jika tidak berisi informasi format. Dengan demikian, server harus menolak semua tautan hangat untuk item yang sudah ditautkan, dan harus menolak semua tautan untuk item yang memiliki tautan hangat. Interpretasi lain mungkin server mengubah format dan status tautan panas atau hangat saat tautan kedua diminta untuk item data yang sama.

Secara umum, aplikasi klien tidak boleh mencoba membuat lebih dari satu tautan pada satu waktu untuk item data.

Aplikasi yang mendukung tautan data panas atau hangat biasanya mendukung format clipboard terdaftar bernama Link. Ketika dikaitkan dengan perintah Salin dan Tempel Tautan aplikasi, format clipboard ini memungkinkan pengguna untuk membuat percakapan DDE antar aplikasi hanya dengan menyalin item data di aplikasi server dan menempelkannya ke aplikasi klien.

Aplikasi server mendukung format Tautkan clipboard dengan menempatkan string di clipboard yang berisi nama aplikasi, topik, dan item saat pengguna memilih perintah Salin dari menu Edit . Berikut ini adalah format Link standar:

application**\0topic\0item\0\0**

Satu karakter null memisahkan nama, dan dua karakter null mengakhiri seluruh string.

Aplikasi klien dan server harus mendaftarkan format Clipboard Tautan, seperti yang ditunjukkan:

cfLink = RegisterClipboardFormat("Link");

Aplikasi klien mendukung format Tautkan clipboard dengan perintah Tempel Tautan pada menu Edit- nya. Ketika pengguna memilih perintah ini, aplikasi klien mengurai nama aplikasi, topik, dan item dari data clipboard format Tautan. Dengan menggunakan nama-nama ini, aplikasi klien memulai percakapan untuk aplikasi dan topik, jika percakapan seperti itu belum ada. Aplikasi klien kemudian mengirim pesan WM_DDE_ADVISE ke aplikasi server, menentukan nama item yang terkandung dalam data clipboard format tautan.

Berikut ini adalah contoh respons aplikasi klien saat pengguna memilih perintah Tempel Tautan.

void DoPasteLink(hwndClientDDE) 
HWND hwndClientDDE; 
{ 
    HANDLE hData; 
    LPSTR lpData; 
    HWND hwndServerDDE; 
    CHAR szApplication[APP_MAX_SIZE + 1]; 
    CHAR szTopic[TOPIC_MAX_SIZE + 1]; 
    CHAR szItem[ITEM_MAX_SIZE + 1]; 
    size_t * nBufLen; 
 HRESULT hResult;
 
    if (OpenClipboard(hwndClientDDE)) 
    { 
        if (!(hData = GetClipboardData(cfLink)) || 
                !(lpData = GlobalLock(hData))) 
        { 
            CloseClipboard(); 
            return; 
        } 
 
        // Parse the clipboard data.
  hResult = StringCchLength(lpData, STRSAFE_MAX_CCH, nBufLen);
 if (FAILED(hResult) || nBufLen == NULL)
 {
// TODO: Write error handler.
  return;
 }
 if (*nBufLen >= APP_MAX_SIZE)
        { 
            CloseClipboard(); 
            GlobalUnlock(hData); 
            return; 
        }
 hResult = StringCchCopy(szApplication, APP_MAX_SIZE +1, lpData);
 if (FAILED(hResult)
 {
// TODO: Write error handler.
  return;
 }
        lpData += (*nBufLen + 1); // skips over null
 hResult = StringCchLength(lpData, STRSAFE_MAX_CCH, nBufLen);
 if (FAILED(hResult) || nBufLen == NULL)
 {
 // TODO: Write error handler.
  return;
 }
 if (*nBufLen >= TOPIC_MAX_SIZE)
        { 
            CloseClipboard(); 
            GlobalUnlock(hData); 
            return; 
        }
 hResult = StringCchCopy(szTopic, TOPIC_MAX_SIZE +1, lpData);
 if (FAILED(hResult)
 {
 // TODO: Write error handler.
  return;
 }
        lpData += (nBufLen + 1); // skips over null
 hResult = StringCchLength(lpData, STRSAFE_MAX_CCH, nBufLen);
 if (FAILED(hResult) || nBufLen == NULL)
 {
 // TODO: Write error handler.
  return;
 }
 if (*nBufLen >= ITEM_MAX_SIZE)
        { 
            CloseClipboard(); 
            GlobalUnlock(hData); 
            return; 
        }

 hResult = StringCchCopy(szItem, ITEM_MAX_SIZE +1, lpData);
 if (FAILED(hResult)
 {
 // TODO: Write error handler.
  return;
 } 
        GlobalUnlock(hData); 
        CloseClipboard(); 
 
        if (hwndServerDDE = 
                FindServerGivenAppTopic(szApplication, szTopic)) 
        { 
            // App/topic conversation is already started. 
 
            if (DoesAdviseAlreadyExist(hwndServerDDE, szItem)) 
            {
                MessageBox(hwndMain, 
                    "Advisory already established", 
                    "Client", MB_ICONEXCLAMATION | MB_OK); 
            }
            else SendAdvise(hwndClientDDE, hwndServerDDE, szItem); 
        } 
        else 
        { 
            // Client must initiate a new conversation first. 
            SendInitiate(szApplication, szTopic); 
            if (hwndServerDDE = 
                    FindServerGivenAppTopic(szApplication, 
                        szTopic)) 
            {
                SendAdvise(hwndClientDDE, hwndServerDDE, szItem); 
            }
        } 
    } 
    return; 
}

Dalam contoh ini, aplikasi klien membuka clipboard dan menentukan apakah aplikasi tersebut berisi data dalam format Tautan (yaitu, cfLink) yang sebelumnya telah didaftarkan. Jika tidak, atau jika tidak dapat mengunci data di clipboard, klien akan kembali.

Setelah aplikasi klien mengambil penunjuk ke data clipboard, aplikasi tersebut mengurai data untuk mengekstrak aplikasi, topik, dan nama item.

Aplikasi klien menentukan apakah percakapan tentang topik sudah ada antara itu dan aplikasi server. Jika percakapan memang ada, klien memeriksa apakah tautan sudah ada untuk item data. Jika tautan seperti itu ada, klien menampilkan kotak pesan kepada pengguna; jika tidak, ia memanggil fungsi SendAdvise sendiri untuk mengirim pesan WM_DDE_ADVISE ke server untuk item tersebut.

Jika percakapan tentang topik belum ada antara klien dan server, klien terlebih dahulu memanggil fungsi SendInitiate sendiri untuk menyiarkan pesan WM_DDE_INITIATE untuk meminta percakapan dan, kedua, memanggil fungsi FindServerGivenAppTopic sendiri untuk membuat percakapan dengan jendela yang merespons atas nama aplikasi server. Setelah percakapan dimulai, aplikasi klien memanggil SendAdvise untuk meminta tautan.

Memberi tahu Klien bahwa Data Telah Berubah

Ketika klien membuat tautan dengan menggunakan pesan WM_DDE_ADVISE, dengan anggota fDeferUpd tidak diatur (yaitu, sama dengan nol) dalam struktur DDEDATA, klien telah meminta server mengirim item data setiap kali nilai item berubah. Dalam kasus seperti itu, server merender nilai baru item data dalam format yang ditentukan sebelumnya dan mengirim pesan WM_DDE_DATA kepada klien, seperti yang ditunjukkan dalam contoh berikut.

// Allocate the size of a DDE data header, plus data (a string), 
// plus a <CR><LF><NULL> 

size_t* pcch;
HRESULT hResult;
 
hResult = StringCchLength(szItemValue,STRSAFE_MAX_CCH, pcch);
if (FAILED(hResult))
{
// TODO: Write error handler.
 return;
}
if (!(hData = GlobalAlloc(GMEM_MOVEABLE,
  sizeof(DDEDATA) + *pcch + 3))) 
{
    return; 
}
if (!(lpData = (DDEDATA FAR*) GlobalLock(hData))) 
{ 
    GlobalFree(hData); 
    return; 
} 
lpData->fAckReq = bAckRequest;       // as in original WM_DDE_ADVISE 
lpData->cfFormat = CF_TEXT;
hResult = StringCchCopy(lpData->Value, *pcch +1, szItemValue); // copies value to be sent
if (FAILED(hResult))
{
// TODO: Write error handler.
 return;
}
// add CR/LF for CF_TEXT format
hResult = StringCchCat(lpData->Value, *pcch + 3, "\r\n");
if (FAILED(hResult))
{
// TODO: Write error handler.
 return;
}
GlobalUnlock(hData); 
if ((atomItem = GlobalAddAtom(szItemName)) != 0) 
{ 
    if (!PostMessage(hwndClientDDE, 
            WM_DDE_DATA, 
            (WPARAM) hwndServerDDE, 
            PackDDElParam(WM_DDE_DATA, (UINT) hData, atomItem))) 
    { 
        GlobalFree(hData); 
        GlobalDeleteAtom(atomItem); 
        FreeDDElParam(WM_DDE_DATA, lParam); 
    } 
} 
 
if (atomItem == 0) 
{ 
    // Handle errors. 
 
}

Dalam contoh ini, klien memproses nilai item yang sesuai. Jika bendera fAckReq untuk item diatur, klien mengirim server pesan WM_DDE_ACK positif.

Ketika klien membuat tautan, dengan set anggota fDeferUpd (yaitu, sama dengan 1), klien telah meminta agar hanya pemberitahuan, bukan data itu sendiri, dikirim setiap kali data berubah. Dalam kasus seperti itu, ketika nilai item berubah, server tidak merender nilai tetapi cukup mengirimi klien pesan WM_DDE_DATA dengan handel data null, seperti yang diilustrasikan dalam contoh berikut.

if (bDeferUpd)      // check whether flag was set in WM_DDE_ADVISE
{
    if ((atomItem = GlobalAddAtom(szItemName)) != 0) 
    { 
        if (!PostMessage(hwndClientDDE, 
                WM_DDE_DATA, 
                (WPARAM) hwndServerDDE, 
                PackDDElParam(WM_DDE_DATA, 0, 
                    atomItem)))                  // NULL data
        {
            GlobalDeleteAtom(atomItem); 
            FreeDDElParam(WM_DDE_DATA, lParam); 
        } 
    } 
} 
 
if (atomItem == 0) 
{ 
     // Handle errors. 
} 

Jika perlu, klien dapat meminta nilai terbaru item data dengan mengeluarkan pesan WM_DDE_REQUEST normal, atau hanya dapat mengabaikan pemberitahuan dari server bahwa data telah berubah. Dalam kedua kasus, jika fAckReq sama dengan 1, klien diharapkan untuk mengirim pesan WM_DDE_ACK positif ke server.

Jika klien meminta agar tautan data tertentu dihentikan, klien mengirimkan pesan WM_DDE_UNADVISE kepada server, seperti yang ditunjukkan dalam contoh berikut.

if ((atomItem = GlobalAddAtom(szItemName)) != 0) 
{ 
    if (!PostMessage(hwndServerDDE, 
            WM_DDE_UNADVISE, 
            (WPARAM) hwndClientDDE, 
            PackDDElParam(WM_DDE_UNADVISE, 0, atomItem))) 
    { 
        GlobalDeleteAtom(atomItem); 
        FreeDDElParam(WM_DDE_UNADVISE, lParam); 
    } 
} 
 
if (atomItem == 0) 
{ 
    // Handle errors. 
}

Server memeriksa apakah klien saat ini memiliki tautan ke item tertentu dalam percakapan ini. Jika ada tautan, server mengirimkan pesan WM_DDE_ACK positif kepada klien; server tidak lagi diperlukan untuk mengirim pembaruan tentang item tersebut. Jika tidak ada tautan, server mengirimkan pesan WM_DDE_ACK negatif kepada klien.

Pesan WM_DDE_UNADVISE menentukan format data. Format nol menginformasikan server untuk menghentikan semua tautan untuk item yang ditentukan, bahkan jika beberapa tautan panas dibuat dan masing-masing menggunakan format yang berbeda.

Untuk mengakhiri semua tautan untuk percakapan, aplikasi klien mengirimkan pesan WM_DDE_UNADVISE kepada server dengan atom item null. Server menentukan apakah percakapan memiliki setidaknya satu tautan yang saat ini dibuat. Jika ada tautan, server mengirimkan pesan WM_DDE_ACK positif kepada klien; server tidak perlu lagi mengirim pembaruan apa pun dalam percakapan. Jika tidak ada tautan, server mengirimkan pesan WM_DDE_ACK negatif kepada klien.

Melakukan Perintah di Aplikasi Server

Aplikasi dapat menggunakan pesan WM_DDE_EXECUTE untuk menyebabkan perintah atau serangkaian perintah tertentu dilakukan di aplikasi lain. Untuk melakukan ini, klien mengirim server pesan WM_DDE_EXECUTE yang berisi handel ke string perintah, seperti yang ditunjukkan dalam contoh berikut.

HRESULT hResult;
  
if (!(hCommand = GlobalAlloc(GMEM_MOVEABLE, 
        sizeof(szCommandString) + 1))) 
{
    return; 
}
if (!(lpCommand = GlobalLock(hCommand))) 
{ 
    GlobalFree(hCommand); 
    return; 
} 

hResult = StringCbCopy(lpCommand, sizeof(szCommandString), szCommandString);
if (hResult != S_OK)
{
// TODO: Write error handler.
 return;
}
 
GlobalUnlock(hCommand); 
if (!PostMessage(hwndServerDDE, 
        WM_DDE_EXECUTE, 
        (WPARAM) hwndClientDDE, 
        PackDDElParam(WM_DDE_EXECUTE, 0, (UINT) hCommand))) 
{ 
    GlobalFree(hCommand); 
    FreeDDElParam(WM_DDE_EXECUTE, lParam); 
}

Dalam contoh ini, server mencoba melakukan string perintah yang ditentukan. Jika berhasil, server mengirimkan pesan WM_DDE_ACK positif kepada klien; jika tidak, server mengirimkan pesan WM_DDE_ACK negatif. Pesan WM_DDE_ACK ini menggunakan kembali handel hCommand yang diteruskan dalam pesan WM_DDE_EXECUTE asli.

Jika string eksekusi perintah klien meminta server dihentikan, server harus merespons dengan mengirim pesan WM_DDE_ACK positif lalu memposting pesan WM_DDE_TERMINATE sebelum mengakhiri. Semua perintah lain yang dikirim dengan pesan WM_DDE_EXECUTE harus dijalankan secara sinkron; artinya, server harus mengirim pesan WM_DDE_ACK hanya setelah berhasil menyelesaikan perintah.

Mengakhiri Percakapan

Klien atau server dapat mengeluarkan pesan WM_DDE_TERMINATE untuk mengakhiri percakapan kapan saja. Demikian pula, aplikasi klien dan server harus siap menerima pesan ini kapan saja. Aplikasi harus mengakhiri semua percakapannya sebelum mematikan.

Dalam contoh berikut, aplikasi yang mengakhiri percakapan memposting pesan WM_DDE_TERMINATE .

PostMessage(hwndServerDDE, WM_DDE_TERMINATE, 
    (WPARAM) hwndClientDDE, 0);

Ini menginformasikan aplikasi lain bahwa aplikasi pengirim tidak akan mengirim pesan lebih lanjut dan penerima dapat menutup jendelanya. Penerima diharapkan dalam semua kasus untuk segera merespons dengan mengirim pesan WM_DDE_TERMINATE. Penerima tidak boleh mengirim pesan WM_DDE_ACK negatif, sibuk, atau positif.

Setelah aplikasi mengirim pesan WM_DDE_TERMINATE ke mitra dalam percakapan DDE, aplikasi tidak boleh merespons pesan dari mitra tersebut, karena mitra mungkin telah menghancurkan jendela tempat respons akan dikirim.

Jika aplikasi menerima pesan DDE selain WM_DDE_TERMINATE setelah diposting WM_DDE_TERMINATE, aplikasi harus membebaskan semua objek yang terkait dengan pesan yang diterima kecuali handel data untuk pesan WM_DDE_DATA atau WM_DDE_POKE yang tidak memiliki kumpulan anggota fRelease.

Ketika aplikasi akan dihentikan, aplikasi harus mengakhiri semua percakapan DDE aktif sebelum menyelesaikan pemrosesan pesan WM_DESTROY. Namun, jika aplikasi tidak mengakhiri percakapan DDE aktifnya, sistem akan mengakhiri percakapan DDE apa pun yang terkait dengan jendela ketika jendela dihancurkan. Contoh berikut menunjukkan bagaimana aplikasi server mengakhiri semua percakapan DDE.

void TerminateConversations(hwndServerDDE) 
HWND hwndServerDDE; 
{ 
    HWND hwndClientDDE; 
 
    // Terminate each active conversation. 
 
    while (hwndClientDDE = GetNextLink(hwndClientDDE)) 
    { 
        SendTerminate(hwndServerDDE, hwndClientDDE); 
    } 
    return; 
} 
 
BOOL AtLeastOneLinkActive(VOID) 
{ 
    return TRUE; 
} 
 
HWND GetNextLink(hwndDummy) 
    HWND hwndDummy; 
{ 
    return (HWND) 1; 
} 
 
VOID SendTerminate(HWND hwndServerDDE, HWND hwndClientDDE) 
{ 
    return; 
}