Dinamik Veri Değişimini Kullanma

Bu bölümde aşağıdaki görevlerle ilgili kod örnekleri vardır:

Konuşma Başlatma

Dinamik Veri Değişimi (DDE) konuşması başlatmak için istemci bir WM_DDE_INITIATE iletisi gönderir. Genellikle istemci, ilk parametre olarak –1 ile SendMessage'ı çağırarak bu iletiyi yayınlar. Uygulamanın sunucu uygulamasına pencere tutamacı zaten varsa, iletiyi doğrudan bu pencereye gönderebilir. İstemci, GlobalAddAtom'ı çağırarak uygulama adı ve konu adı için atomları hazırlar. İstemci, uygulama ve konu için NULL (joker karakter) atomları sağlayarak herhangi bir olası sunucu uygulamasıyla ve olası herhangi bir konu için konuşma isteğinde bulunabilir.

Aşağıdaki örnek, istemcinin hem uygulamanın hem de konunun belirtildiği bir konuşmayı nasıl başlattığını gösterir.

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

Uyarı

Uygulamanız NULL atomları kullanıyorsa GlobalAddAtom ve GlobalDeleteAtom işlevlerini kullanmanız gerekmez. Bu örnekte, istemci uygulaması sırasıyla sunucunun adını ve konunun adını içeren iki genel atom oluşturur.

 

İstemci uygulaması, iletinin lParam parametresinde bu iki atomu içeren bir WM_DDE_INITIATE iletisi gönderir. SendMessage işlevine yapılan çağrıda– 1 özel pencere tutamacı sistemi diğer tüm etkin uygulamalara bu iletiyi göndermeye yönlendirir. SendMessage , iletiyi alan tüm uygulamalar sisteme denetim döndürene kadar istemci uygulamasına dönmez. Başka bir deyişle, sunucu uygulamaları tarafından yanıt olarak gönderilen tüm WM_DDE_ACK iletilerin SendMessage çağrısı döndürülene kadar istemci tarafından işlendiği garanti edilir.

SendMessage döndürdüğünde istemci uygulaması genel atomları siler.

Sunucu uygulamaları aşağıdaki diyagramda gösterilen mantığa göre yanıt verir.

sunucu uygulaması yanıt mantığı

Bir veya daha fazla konuyu onaylamak için sunucunun her konuşma için atomlar oluşturması (birden çok konu varsa yinelenen uygulama adı atomları gerektirmesi) ve aşağıdaki örnekte gösterildiği gibi her konuşma için bir WM_DDE_ACK iletisi göndermesi gerekir.

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

Bir sunucu WM_DDE_ACK iletisiyle yanıtladığında, istemci uygulaması sunucu penceresine bir tanıtıcı kaydetmelidir. WM_DDE_ACK iletisininwParam parametresi olarak tanıtıcıyı alan istemci, bu tanıtıcının tanımladiği sunucu penceresine sonraki tüm DDE iletilerini gönderir.

İstemci uygulamanız uygulama adı veya konu adı için NULL atom kullanıyorsa, uygulamanın birden fazla sunucu uygulamasından onaylar almasını bekleyebilirsiniz. İstemci uygulamanız NULL atomları kullanmasa bile birden çok onay, DDE sunucusunun birden çok örneğinden de gelebilir. Bir sunucu her konuşma için her zaman benzersiz bir pencere kullanmalıdır. İstemci uygulamasındaki pencere yordamı, birden çok konuşmayı izlemek için sunucu penceresinin tanıtıcısını (WM_DDE_INITIATElParam parametresi olarak sağlanır) kullanabilir. Bu, tek bir istemci penceresinin her konuşma için yeni bir istemci penceresiyle sonlandırmaya ve yeniden bağlanmaya gerek kalmadan çeşitli konuşmaları işlemesine olanak tanır.

Tek Bir Öğeyi Aktarma

DDE konuşması oluşturulduktan sonra istemci, WM_DDE_REQUEST iletisini vererek bir veri öğesinin değerini sunucudan alabilir veya WM_DDE_POKEvererek sunucuya bir veri öğesi değeri gönderebilir.

Sunucudan Öğe Alma

Sunucudan öğe almak için istemci, aşağıdaki örnekte gösterildiği gibi sunucuya alınacak öğeyi ve biçimi belirten bir WM_DDE_REQUEST iletisi gönderir.

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

Bu örnekte istemci, istenen veri öğesi için tercih edilen biçim olarak pano biçimini CF_TEXT belirtir.

WM_DDE_REQUEST iletisinin alıcısının (sunucu) genellikle öğe atomunu silmesi gerekir, ancak PostMessage çağrısı başarısız olursa istemcinin atomu silmesi gerekir.

Sunucunun istenen öğeye erişimi varsa ve istenen biçimde işleyebilirse, sunucu öğe değerini paylaşılan bellek nesnesi olarak kopyalar ve istemciye aşağıdaki örnekte gösterildiği gibi bir WM_DDE_DATA iletisi gönderir.

// 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_PTR) 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.  
}

Bu örnekte, sunucu uygulaması veri öğesini içerecek bir bellek nesnesi ayırır. Veri nesnesi bir DDEDATA yapısı olarak başlatılır.

Ardından sunucu uygulaması, istemci uygulamasına verilerin metin biçiminde olduğunu bildirmek için yapının cfFormat üyesini CF_TEXT olarak ayarlar. İstemci, istenen verilerin değerini DDEDATA yapısının Value üyesine kopyalayarak yanıt verir. Sunucu veri nesnesini doldurduktan sonra, sunucu verilerin kilidini açar ve veri öğesinin adını içeren bir genel atom oluşturur.

Son olarak, sunucu PostMessage'ı çağırarak WM_DDE_DATA iletisini gönderir. Veri nesnesinin tanıtıcısı ve öğe adını içeren atom, PackDDElParam işlevi tarafından iletinin lParam parametresine paketlenir.

PostMessage başarısız olursa, sunucunun paketlenmiş lParam parametresini boşaltmak için FreeDDElParam işlevini kullanması gerekir. Sunucu, aldığı WM_DDE_REQUEST iletisi için paketlenmiş lParam parametresini de serbest etmelidir.

Sunucu isteği karşılayamıyorsa, aşağıdaki örnekte gösterildiği gibi istemciye negatif bir WM_DDE_ACK iletisi gönderir.

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

bir WM_DDE_DATA iletisi aldıktan sonra istemci, veri öğesi değerini uygun şekilde işler. Ardından , fAckReq üyesi WM_DDE_DATA iletisinde 1 ise, istemcinin sunucuya aşağıdaki örnekte gösterildiği gibi pozitif bir WM_DDE_ACK iletisi göndermesi gerekir.

UnpackDDElParam(WM_DDE_DATA, lParam, (PUINT_PTR) &hData, 
    (PUINT_PTR) &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);

Bu örnekte, istemci verilerin biçimini inceler. Biçim CF_TEXT değilse (veya istemci verilerin belleğini kilitleyemiyorsa), istemci verileri işleyemeyeceğini belirtmek için negatif bir WM_DDE_ACK iletisi gönderir. tanıtıcı fAckReq üyesini içerdiğinden istemci bir veri tutamacını kilitleyemezse, istemci negatif bir WM_DDE_ACK iletisi göndermemelidir. Bunun yerine istemcinin konuşmayı sonlandırması gerekir.

İstemci bir WM_DDE_DATA iletisine yanıt olarak negatif bir bildirim gönderirse, sunucu, negatif bildirimle ilişkili WM_DDE_DATA iletisi tarafından başvurulan belleği (lParam parametresini değil) boşaltmaktan sorumludur.

Verileri işleyebilirse, istemci DDEDATA yapısının fAckReq üyesini inceleyerek sunucunun istemcinin verileri başarıyla alıp işlemediğinin bildirilip istenmediğini belirler. Sunucu bu bilgileri isterse, istemci sunucuya olumlu bir WM_DDE_ACK iletisi gönderir.

Verilerin kilidinin açılması işaretçiyi veriye geçersiz kılması nedeniyle istemci, veri nesnesinin kilidini açmadan önce fRelease üyesinin değerini kaydeder. Değeri kaydettikten sonra istemci, sunucu uygulamasının istemciden verileri içeren belleği boşaltmasını isteyip istemediğini belirlemek için bu değeri inceler; müşteri buna göre hareket eder.

Negatif bir WM_DDE_ACK iletisi aldıktan sonra istemci, farklı bir pano biçimi belirterek aynı öğe değerini yeniden isteyebilir. Genellikle, bir istemci önce destekleyebileceğiniz en karmaşık biçimi ister, ardından gerekirse sunucunun sağlayabileceklerini bulana kadar aşamalı olarak daha basit biçimler aracılığıyla geri adım atabilir.

Sunucu, sistem konusunun Biçimler öğesini destekliyorsa, istemci, her öğe isteğinde bulunmak yerine, sunucunun desteklediği pano biçimlerini bir kez belirleyebilir.

Sunucuya Öğe Gönderme

İstemci, WM_DDE_POKE iletisini kullanarak sunucuya bir öğe değeri gönderebilir. İstemci, gönderilecek öğeyi işler ve aşağıdaki örnekte gösterildiği gibi WM_DDE_POKE iletisini gönderir.

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_PTR) hPokeData, 
                    atomItem))) 
        { 
            GlobalDeleteAtom(atomItem); 
            GlobalFree(hPokeData); 
        } 
} 
 
if (atomItem == 0) 
{ 
    // Handle errors. 
} 

Uyarı

WM_DDE_POKE ileti kullanarak veri göndermek, istemciden sunucuya WM_DDE_POKE gönderilmesi dışında, WM_DDE_DATA kullanarak göndermekle temelde aynıdır.

 

Sunucu, veri öğesi değerini istemci tarafından işlenen biçimde kabul edebiliyorsa, sunucu öğe değerini uygun şekilde işler ve istemciye pozitif bir WM_DDE_ACK iletisi gönderir. Biçimi nedeniyle veya başka nedenlerle öğe değerini işleyemiyorsa, sunucu istemciye negatif bir WM_DDE_ACK iletisi gönderir.

UnpackDDElParam(WM_DDE_POKE, lParam, (PUINT_PTR) &hPokeData, 
    (PUINT_PTR) &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.

Bu örnekte sunucu, istemcinin gönderdiği öğenin adını almak için GlobalGetAtomName'i çağırır. Sunucu daha sonra öğeyi destekleyip desteklemediğini ve öğenin doğru biçimde (yani CF_TEXT) işlenip işlenmediğini belirler. Öğe desteklenmiyorsa ve doğru biçimde işlenmiyorsa veya sunucu verilerin belleğini kilitleyemezse, sunucu istemci uygulamasına geri negatif bir bildirim gönderir. Bu durumda, WM_DDE_POKE iletilerin her zaman fAckReq üye kümesine sahip olduğu varsayıldığından, negatif bildirim göndermenin doğru olduğunu unutmayın. Üyeyi sunucu görmezden gelmelidir.

Bir sunucu WM_DDE_POKE iletiye yanıt olarak negatif bir bildirim gönderirse, istemci, negatif bildirimle ilişkili WM_DDE_POKE iletisi tarafından başvurulan belleği (lParam parametresini değil) boşaltmaktan sorumludur.

İstemci uygulaması, bir sunucu uygulamasındaki bir öğeye bağlantı oluşturmak için DDE kullanabilir. Böyle bir bağlantı kurulduktan sonra, sunucu bağlı öğenin düzenli güncelleştirmelerini genellikle, öğenin değeri her değiştiğinde istemciye gönderir. Bu nedenle, iki uygulama arasında kalıcı bir veri akışı oluşturulur; bu veri akışının bağlantısı açıkça kesilene kadar yerinde kalır.

İstemci, aşağıdaki örnekte gösterildiği gibi bir WM_DDE_ADVISE iletisi göndererek bir veri bağlantısı başlatır.

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_PTR) hOptions, 
                atomItem)))) 
    { 
        GlobalDeleteAtom(atomItem); 
        GlobalFree(hOptions); 
        FreeDDElParam(WM_DDE_ADVISE, lParam); 
    } 
} 
 
if (atomItem == 0) 
{ 
    // Handle errors 
 
}

Bu örnekte, istemci uygulaması WM_DDE_ADVISE iletisinin fDeferUpd bayrağını YANLIŞ olarak ayarlar. Bu, veriler her değiştiğinde sunucu uygulamasını verileri istemciye göndermeye yönlendirir.

Sunucu WM_DDE_ADVISE isteğine hizmet veremiyorsa istemciye negatif bir WM_DDE_ACK iletisi gönderir. Ancak sunucunun öğeye erişimi varsa ve öğeyi istenen biçimde işleyebilirse, sunucu yeni bağlantıyı not alır ( hOptions parametresinde belirtilen bayrakları geri çağırma) ve istemciye olumlu bir WM_DDE_ACK iletisi gönderir. Bundan sonra, istemci eşleşen bir WM_DDE_UNADVISE iletisi gönderene kadar sunucu, öğenin değeri sunucuda her değiştiğinde istemciye yeni verileri gönderir.

WM_DDE_ADVISE iletisi, bağlantı sırasında değiş tokuş edilecek verilerin biçimini belirler. İstemci aynı öğeyle başka bir bağlantı kurmayı denerse ancak farklı bir veri biçimi kullanıyorsa, sunucu ikinci veri biçimini reddetmeyi veya desteklemeyi seçebilir. Herhangi bir veri öğesi için sıcak bağlantı oluşturulduysa, sunucu aynı anda yalnızca bir veri biçimini destekleyebilir. Bunun nedeni, sıcak bağlantının WM_DDE_DATA iletisinin NULL veri tanıtıcısına sahip olmasıdır; diğer durumda, bu tanıtıcı biçim bilgilerini içerir. Bu nedenle, sunucu zaten bağlı olan bir öğenin tüm sıcak bağlantılarını reddetmeli ve sıcak bağlantıları olan bir öğenin tüm bağlantılarını reddetmelidir. Başka bir yorum, aynı veri öğesi için ikinci bir bağlantı istendiğinde sunucunun bağlantının biçimini ve sıcak ya da ılık durumunu değiştirmesi olabilir.

Genel olarak, istemci uygulamaları bir veri öğesi için aynı anda birden fazla bağlantı oluşturmaya çalışmamalıdır.

Sıcak veya ılımlı veri bağlantılarını destekleyen uygulamalar genellikle Link adlı kayıtlı bir panoya biçimini destekler. Uygulamanın Bağlantıyı Kopyala ve Yapıştır komutlarıyla ilişkilendirildiğinde, bu pano biçimi kullanıcının yalnızca sunucu uygulamasındaki bir veri öğesini kopyalayıp istemci uygulamasına yapıştırarak uygulamalar arasında DDE konuşmaları oluşturmasını sağlar.

Sunucu uygulaması, kullanıcı Düzenle menüsünden Kopyala komutunu seçtiğinde, panoya uygulama, konu ve öğe adlarını içeren bir dize yerleştirerek Bağla panosu biçimini destekler. Standart Bağlantı biçimi aşağıdadır:

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

Tek bir null karakter adları ayırır ve iki null karakter dizenin tamamını sonlandırır.

Hem istemci hem de sunucu uygulamalarının, gösterildiği gibi Bağlantı panosu biçimini kaydetmesi gerekir:

cfLink = RegisterClipboardFormat("Link");

İstemci uygulaması, Düzenle menüsündeki Bağlantıyı Yapıştır komutu olarak Link panosu formatını destekler. Kullanıcı bu komutu seçtiğinde, istemci uygulaması bağlantı biçimi pano verilerinden uygulama, konu ve öğe adlarını ayrıştırır. Bu adları kullanarak istemci uygulama, böyle bir konuşma yoksa uygulama ve konu hakkında bir konuşma başlatır. İstemci uygulaması daha sonra sunucu uygulamasına bağlantı biçimi pano verilerinde bulunan öğe adını belirterek bir WM_DDE_ADVISE iletisi gönderir.

Aşağıda, kullanıcı Bağlantıyı Yapıştır komutunu seçtiğinde istemci uygulamasının yanıtına bir örnek verilmiştir.

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

Bu örnekte, istemci uygulaması panoyu açar ve daha önce kaydettiğiniz Bağlantı biçiminde (cfLink) veri içerip içermediğini belirler. Aksi takdirde veya panodaki verileri kilitleyemezse istemci geri döner.

İstemci uygulaması pano verilerine yönelik bir işaretçi aldıktan sonra, uygulamayı, konuyu ve öğe adlarını ayıklamak için verileri ayrıştırır.

İstemci uygulaması, konu başlığındaki bir konuşmanın sunucu uygulamasıyla arasında zaten mevcut olup olmadığını belirler. Bir konuşma varsa, istemci veri öğesi için zaten bir bağlantı olup olmadığını denetler. Böyle bir bağlantı varsa, istemci kullanıcıya bir ileti kutusu görüntüler; aksi takdirde, öğenin sunucusuna bir WM_DDE_ADVISE iletisi göndermek için kendi SendAdvise işlevini çağırır.

Konu başlığındaki bir konuşma istemciyle sunucu arasında zaten yoksa, istemci ilk olarak bir konuşma istemek üzere WM_DDE_INITIATE iletisini yayınlamak için kendi SendInitiate işlevini çağırır ve ikincisi, sunucu uygulaması adına yanıt veren pencereyle konuşma oluşturmak için kendi FindServerGivenAppTopic işlevini çağırır. Konuşma başladıktan sonra, istemci uygulaması bağlantıyı istemek için SendAdvise'i çağırır.

İstemciye Verilerin Değiştiğini Bildirme

İstemci WM_DDE_ADVISE iletisini kullanarak bir bağlantı oluşturduğunda, fDeferUpd üyesi DDEDATA yapısında ayarlanmadı (yani sıfıra eşit), istemci, öğenin değeri her değiştiğinde sunucunun veri öğesini göndermesini istedi. Bu gibi durumlarda, sunucu veri öğesinin yeni değerini daha önce belirtilen biçimde işler ve aşağıdaki örnekte gösterildiği gibi istemciye bir WM_DDE_DATA iletisi gönderir.

// 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_PTR) hData, atomItem))) 
    { 
        GlobalFree(hData); 
        GlobalDeleteAtom(atomItem); 
        FreeDDElParam(WM_DDE_DATA, lParam); 
    } 
} 
 
if (atomItem == 0) 
{ 
    // Handle errors. 
 
}

Bu örnekte, istemci öğe değerini uygun şekilde işler. Öğenin fAckReq bayrağı ayarlanırsa, istemci sunucuya pozitif bir WM_DDE_ACK iletisi gönderir.

İstemci bağlantıyı oluşturduğunda , fDeferUpd üye kümesiyle (yani, 1'e eşit), istemci, veriler her değiştiğinde verilerin değil yalnızca bir bildirimin gönderilmesini istedi. Bu gibi durumlarda, öğe değeri değiştiğinde, sunucu değeri işlemez, ancak aşağıdaki örnekte gösterildiği gibi istemciye null veri tutamacı içeren bir WM_DDE_DATA iletisi gönderir.

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

Gerektiğinde, istemci normal bir WM_DDE_REQUEST iletisi vererek veri öğesinin en son değerini isteyebilir veya verilerin değiştirildiğine dair sunucudan gelen bildirimi yoksayabilir. Her iki durumda da , fAckReq 1'e eşitse istemcinin sunucuya pozitif bir WM_DDE_ACK iletisi göndermesi beklenir.

İstemci belirli bir veri bağlantısının sonlandırılma isteğinde bulunursa, istemci aşağıdaki örnekte gösterildiği gibi sunucuya bir WM_DDE_UNADVISE iletisi gönderir.

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

Sunucu, istemcinin şu anda bu konuşmadaki belirli bir öğeye bağlantısı olup olmadığını denetler. Bir bağlantı varsa, sunucu istemciye olumlu bir WM_DDE_ACK iletisi gönderir; ardından sunucu artık öğeyle ilgili güncelleştirmeleri göndermeye gerek kalmaz. Bağlantı yoksa, sunucu istemciye negatif bir WM_DDE_ACK iletisi gönderir.

WM_DDE_UNADVISE iletisi bir veri biçimi belirtir. Sıfır biçimi, birkaç sık erişim bağlantısı oluşturulsa ve her biri farklı bir biçim kullansa bile, sunucuya belirtilen öğe için tüm bağlantıları durdurmasını bildirir.

Bir konuşmanın tüm bağlantılarını sonlandırmak için istemci uygulaması sunucuya null öğe atomu içeren bir WM_DDE_UNADVISE iletisi gönderir. Sunucu, konuşmanın şu anda kurulmuş en az bir bağlantı olup olmadığını belirler. Bir bağlantı varsa, sunucu istemciye olumlu bir WM_DDE_ACK iletisi gönderir; ardından sunucunun artık konuşmada herhangi bir güncelleştirme göndermesi gerekmez. Bağlantı yoksa, sunucu istemciye negatif bir WM_DDE_ACK iletisi gönderir.

Sunucu Uygulamasında Komut Yürütme

Uygulamalar, belirli bir komutun veya komut dizisinin başka bir uygulamada yürütülmesine neden olmak için WM_DDE_EXECUTE iletisini kullanabilir. Bunu yapmak için istemci, aşağıdaki örnekte gösterildiği gibi sunucuya bir komut dizesine tanıtıcı içeren bir WM_DDE_EXECUTE iletisi gönderir.

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_PTR) hCommand))) 
{ 
    GlobalFree(hCommand); 
    FreeDDElParam(WM_DDE_EXECUTE, lParam); 
}

Bu örnekte, sunucu belirtilen komut dizesini gerçekleştirmeye çalışır. Başarılı olursa, sunucu istemciye olumlu bir WM_DDE_ACK iletisi gönderir; aksi takdirde, negatif bir WM_DDE_ACK iletisi gönderir. Bu WM_DDE_ACK iletisi, özgün WM_DDE_EXECUTE iletide geçirilen hCommand tanıtıcısını yeniden kullanır.

İstemcinin komut yürütme dizesi sunucunun sonlandırmasını isterse, sunucu pozitif bir WM_DDE_ACK iletisi göndererek yanıt vermeli ve sonlandırmadan önce WM_DDE_TERMINATE bir ileti göndermelidir. WM_DDE_EXECUTE iletisiyle gönderilen diğer tüm komutlar zaman uyumlu olarak yürütülmelidir; yani, sunucu yalnızca komutu başarıyla tamamladıktan sonra bir WM_DDE_ACK iletisi göndermelidir.

Konuşmayı Sonlandırma

İstemci veya sunucu, herhangi bir zamanda bir konuşmayı sonlandırmak için bir WM_DDE_TERMINATE iletisi verebilir. Benzer şekilde, hem istemci hem de sunucu uygulamaları bu iletiyi istediği zaman almaya hazır olmalıdır. Bir uygulamanın kapatmadan önce tüm konuşmalarını sonlandırması gerekir.

Aşağıdaki örnekte, konuşmayı sonlandıran uygulama bir WM_DDE_TERMINATE iletisi yayınlar.

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

Bu, gönderen uygulamanın başka ileti göndermeyeceğini ve alıcının penceresini kapatabileceğini diğer uygulamaya bildirir. Alıcının her durumda bir WM_DDE_TERMINATE iletisi göndererek hemen yanıt vermesi beklenir. Alıcı negatif, meşgul veya pozitif bir WM_DDE_ACK iletisi göndermemelidir.

Bir uygulama WM_DDE_TERMINATE iletisini bir DDE konuşmasında iş ortağına gönderdikten sonra, iş ortağı yanıtın gönderileceği pencereyi yok etmiş olabileceğinden bu iş ortağının iletilerine yanıt vermemelidir.

Bir uygulama, WM_DDE_TERMINATE yayımladıktan sonra WM_DDE_TERMINATE dışında bir DDE iletisi alırsa, fRelease üye kümesi olmayanWM_DDE_DATA veya WM_DDE_POKE iletilerin veri tanıtıcıları dışında alınan iletilerle ilişkili tüm nesneleri serbest bırakmalıdır.

Bir uygulama sonlandırılırken, WM_DESTROY iletisini işlemeyi tamamlamadan önce tüm etkin DDE konuşmalarını sonlandırmalıdır. Ancak, bir uygulama etkin DDE konuşmalarını sonlandırmazsa, pencere yok edildiğinde sistem bir pencereyle ilişkili tüm DDE konuşmalarını sonlandırır. Aşağıdaki örnekte, bir sunucu uygulamasının tüm DDE konuşmalarını nasıl sonlandıracağını gösterir.

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