Sdílet prostřednictvím


Použití dynamické výměny dat

Tato část obsahuje ukázky kódu pro následující úlohy:

Zahájení konverzace

Pokud chcete zahájit konverzaci DDE (Dynamic Data Exchange), klient odešle WM_DDE_INITIATE zprávu. Klient obvykle vysílá tuto zprávu voláním SendMessage s –1 jako první parametr. Pokud už aplikace obsahuje popisovač okna pro serverovou aplikaci, může zprávu odeslat přímo do daného okna. Klient připraví atomy pro název aplikace a název tématu voláním GlobalAddAtom. Klient může požádat o konverzace s libovolnou možnou serverovou aplikací a pro jakékoli potenciální téma zadáním atomů NULL (wildcard) pro aplikaci a téma.

Následující příklad ukazuje, jak klient zahájí konverzaci, kde jsou zadány aplikace i téma.

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

Poznámka:

Pokud vaše aplikace používá atomy NULL , nemusíte používat funkce GlobalAddAtom a GlobalDeleteAtom . V tomto příkladu klientská aplikace vytvoří dva globální atomy obsahující název serveru a název tématu.

 

Klientská aplikace odešle WM_DDE_INITIATE zprávu s těmito dvěma atomy v parametru lParam zprávy. Při volání funkce SendMessage speciální úchyt okna –1 směruje systém odeslat tuto zprávu do všech ostatních aktivních aplikací. SendMessage se nevrátí do klientské aplikace, dokud všechny aplikace, které obdrží zprávu, vrátí řízení systému. To znamená, že všechny zprávy WM_DDE_ACK, které byly odeslány serverovými aplikacemi v odpovědi, jsou zaručeně zpracovány klientem do doby, než se vrátí volání SendMessage.

Po ukončení SendMessage klientská aplikace odstraní globální atomy.

Serverové aplikace reagují podle logiky znázorněné v následujícím diagramu.

Logika odezvy serverové aplikace

Pokud chcete potvrdit jedno nebo více témat, musí server vytvořit atomy pro každou konverzaci (vyžaduje duplicitní atomy názvu aplikace, pokud existuje více témat) a odeslat WM_DDE_ACK zprávu pro každou konverzaci, jak je znázorněno v následujícím příkladu.

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

Když server odpoví zprávou WM_DDE_ACK , klientská aplikace by měla uložit popisovač do okna serveru. Klient, který přijímá popisovač jako parametr wParam zprávy WM_DDE_ACK potom odešle všechny následné zprávy DDE do okna serveru, které tento popisovač identifikuje.

Pokud vaše klientská aplikace používá pro název aplikace nebo název tématu atom NULL , počítejte s tím, že aplikace obdrží potvrzení z více než jedné serverové aplikace. Několik potvrzení může také pocházet z více instancí serveru DDE, i když vaše klientská aplikace nepoužívá atomy. Server by měl vždy používat jedinečné okno pro každou konverzaci. Procedura okna v klientské aplikaci může použít popisovač okna serveru (zadaný jako parametr lParam při WM_DDE_INITIATE) ke sledování více konverzací. Díky tomu může jedno okno klienta zpracovávat několik konverzací, aniž by bylo nutné ukončit a znovu připojit nové okno klienta pro každou konverzaci.

Přenos jedné položky

Po vytvoření konverzace DDE může klient buď načíst hodnotu datové položky ze serveru tak, že vydá WM_DDE_REQUEST zprávu, nebo odešle hodnotu datové položky na server vydáním WM_DDE_POKE.

Načtení položky ze serveru

Chcete-li načíst položku ze serveru, klient odešle server WM_DDE_REQUEST zprávu určující položku a formát, které se mají načíst, jak je znázorněno v následujícím příkladu.

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

V tomto příkladu klient určuje formát schránky CF_TEXT jako upřednostňovaný formát požadované datové položky.

Příjemce (server) zprávy WM_DDE_REQUEST obvykle musí odstranit atom položky, ale pokud volání PostMessage selže, klient musí odstranit atom.

Pokud má server přístup k požadované položce a může ji vykreslit v požadovaném formátu, server zkopíruje hodnotu položky jako objekt sdílené paměti a odešle klientovi WM_DDE_DATA zprávu, jak je znázorněno v následujícím příkladu.

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

V tomto příkladu serverová aplikace přidělí objekt paměti, který bude obsahovat datovou položku. Datový objekt je inicializován jako struktura DDEDATA .

Serverová aplikace poté nastaví člen cfFormat struktury na CF_TEXT, aby informovala klientskou aplikaci, že data jsou v textovém formátu. Klient odpoví zkopírováním hodnoty požadovaných dat do člena hodnoty struktury DDEDATA . Po vyplnění datového objektu server odemkne data a vytvoří globální atom obsahující název datové položky.

Server nakonec vydá zprávu WM_DDE_DATA voláním PostMessage. Popisovač datového objektu a atom obsahující název položky jsou zabaleny funkcí PackDDElParam do parametru zprávy lParam.

Pokud PostMessage selže, server musí použít funkci FreeDDElParam k uvolnění zabaleného lParam parametru. Server musí také uvolnit zabalený parametr lParam pro WM_DDE_REQUEST zprávu, kterou přijala.

Pokud server nemůže požadavek splnit, odešle klientovi zápornou zprávu WM_DDE_ACK , jak je znázorněno v následujícím příkladu.

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

Po přijetí WM_DDE_DATA zprávy klient podle potřeby zpracuje hodnotu datové položky. Pokud je člen fAckReq odkazující na zprávu WM_DDE_DATA nastaven na 1, klient musí serveru odeslat kladnou zprávu WM_DDE_ACK, jak je znázorněno v následujícím příkladu.

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

V tomto příkladu klient prozkoumá formát dat. Pokud formát není CF_TEXT (nebo pokud klient nemůže uzamknout paměť dat), klient odešle zápornou WM_DDE_ACK zprávu, která indikuje, že nemůže zpracovat data. Pokud klient nemůže uzamknout popisovač dat, protože popisovač obsahuje člena fAckReq, neměl by odesílat negativní zprávu WM_DDE_ACK. Místo toho by měl klient ukončit konverzaci.

Pokud klient odešle negativní potvrzení v reakci na zprávu WM_DDE_DATA , je server zodpovědný za uvolnění paměti (ale ne parametru lParam ), na který odkazuje zpráva WM_DDE_DATA přidružená k zápornému potvrzení.

Pokud může zpracovat data, klient zkontroluje člena fAckReq struktury DDEDATA a určí, jestli server požádal, aby byl informován, že klient přijal a úspěšně zpracoval data. Pokud server požádal o tyto informace, klient odešle server pozitivní WM_DDE_ACK zprávu.

Protože odemknutí dat zneplatní ukazatel na data, klient před odemknutím datového objektu uloží hodnotu členu fRelease . Po uložení hodnoty klient zkontroluje, zda serverová aplikace požádala klienta o uvolnění paměti obsahující data; klient funguje odpovídajícím způsobem.

Pokud klient obdrží neúspěšnou zprávu WM_DDE_ACK, může znovu vyžádat stejnou hodnotu položky, přičemž určí jiný formát schránky. Typicky klient nejprve požádá o nejsložitější formát, který může podporovat, a pak v případě potřeby přejde dolů k postupně jednodušším formátům, dokud nenajde formát, který může server poskytnout.

Pokud server podporuje položku Formáty systémového tématu, může klient jednou určit, jaké formáty schránky server podporuje, místo toho, aby je určoval pokaždé, když klient požádá o položku.

Odeslání položky na server

Klient může odeslat hodnotu položky na server pomocí WM_DDE_POKE zprávy. Klient vykreslí položku, která se má odeslat, a odešle WM_DDE_POKE zprávu, jak je znázorněno v následujícím příkladu.

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

Poznámka:

Odesílání dat pomocí WM_DDE_POKE zprávy je v podstatě stejné jako odeslání pomocí WM_DDE_DATA, s tím rozdílem, že WM_DDE_POKE se odesílá z klienta na server.

 

Pokud server dokáže přijmout hodnotu datové položky ve formátu vykresleného klientem, server zpracuje hodnotu položky podle potřeby a odešle klientovi pozitivní WM_DDE_ACK zprávu. Pokud nelze zpracovat hodnotu položky z důvodu jejího formátu nebo z jiných důvodů, server odešle klientovi zápornou WM_DDE_ACK zprávu.

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.

V tomto příkladu server volá GlobalGetAtomName k načtení názvu položky, kterou klient odeslal. Server pak určí, zda podporuje položku a zda je položka vykreslena ve správném formátu (to znamená CF_TEXT). Pokud položka není podporována a nevykresluje se ve správném formátu nebo pokud server nemůže uzamknout paměť dat, server odešle negativní potvrzení zpět klientské aplikaci. Všimněte si, že v tomto případě je odeslání negativního potvrzení správné, protože zprávy WM_DDE_POKE jsou vždy považovány za mající nastavený člen fAckReq. Server by měl člena ignorovat.

Pokud server odešle negativní potvrzení v reakci na zprávu WM_DDE_POKE , je klient zodpovědný za uvolnění paměti (ale ne parametru lParam ), na který odkazuje WM_DDE_POKE zpráva přidružená k zápornému potvrzení.

Klientská aplikace může pomocí DDE vytvořit odkaz na položku v serverové aplikaci. Po vytvoření takového propojení server odesílá klientovi pravidelné aktualizace propojené položky, obvykle vždy, když se změní hodnota položky. Proto je mezi těmito dvěma aplikacemi vytvořen trvalý datový proud; tento datový proud zůstane na místě, dokud se explicitně neodpojí.

Klient zahájí datový odkaz odesláním WM_DDE_ADVISE zprávy, jak je znázorněno v následujícím příkladu.

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 
 
}

V tomto příkladu klientská aplikace nastaví příznak fDeferUpdzprávy WM_DDE_ADVISEna FALSE. Tím nasměruje serverová aplikace, aby data odesílala klientovi vždy, když se data změní.

Pokud server nemůže obsluhovat požadavek WM_DDE_ADVISE , odešle klientovi zápornou zprávu WM_DDE_ACK . Pokud má ale server přístup k položce a může ji vykreslit v požadovaném formátu, server zaznamená nový odkaz (odvolává příznaky zadané v parametru hOptions ) a odešle klientovi pozitivní WM_DDE_ACK zprávu. Od té doby, dokud klient nesdílí odpovídající WM_DDE_UNADVISE zprávu, odešle server klientovi nová data pokaždé, když se hodnota položky změní na serveru.

Zpráva WM_DDE_ADVISE vytvoří formát dat, která se mají během propojení vyměňovat. Pokud se klient pokusí vytvořit další propojení se stejnou položkou, ale používá jiný datový formát, server může odmítnout druhý formát dat nebo se ho pokusit podporovat. Pokud byl pro libovolnou datovou položku vytvořen teplý odkaz, server může současně podporovat pouze jeden datový formát. Důvodem je to, že WM_DDE_DATA zpráva pro teplé propojení obsahuje popisovač dat NULL , který jinak obsahuje informace o formátu. Server proto musí odmítnout všechny teplé odkazy pro položku, která je již propojena, a musí odmítnout všechny odkazy pro položku, která obsahuje teplé odkazy. Další interpretací může být, že server změní formát a horký nebo teplý stav odkazu, když se pro stejnou datovou položku požaduje druhý odkaz.

Klientské aplikace by se obecně neměly pokoušet o vytvoření více než jednoho propojení najednou pro datovou položku.

Aplikace, které podporují rychlé nebo často používané datové odkazy, obvykle podporují registrovaný formát clipboardu s názvem Link. Pokud je tento formát schránky přidružen k příkazům Kopírovat a Vložit propojení v aplikaci, umožňuje uživateli snadno navázat konverzace DDE mezi aplikacemi tím, že zkopíruje datovou položku v serverové aplikaci a vloží ji do klientské aplikace.

Serverová aplikace podporuje formát schránky typu Odkaz tak, že umístí do schránky řetězec obsahující názvy aplikace, tématu a položky, když uživatel zvolí příkaz Kopírovat z nabídky Upravit. Následuje standardní formát odkazu:

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

Jeden znak null odděluje názvy a dva znaky null ukončují celý řetězec.

Klientské i serverové aplikace musí zaregistrovat formát schránky Link, jak je znázorněno na následujícím obrázku:

cfLink = RegisterClipboardFormat("Link");

Klientská aplikace podporuje formát schránky typu Odkaz prostřednictvím příkazu Vložit odkaz v nabídce Úpravy. Když uživatel zvolí tento příkaz, klientská aplikace analyzuje názvy aplikací, témat a položek ze schránky ve formátu Link. Pomocí těchto názvů klientská aplikace zahájí konverzaci pro aplikaci a téma, pokud taková konverzace ještě neexistuje. Klientská aplikace pak odešle do serverové aplikace zprávu WM_DDE_ADVISE , která určuje název položky obsažené ve schránce ve formátu Link.

Následuje příklad odpovědi klientské aplikace, když uživatel zvolí příkaz Vložit odkaz.

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

V tomto příkladu klientská aplikace otevře schránku a určí, jestli obsahuje data ve formátu Link (tj. cfLink), který předtím zaregistroval. Pokud ne, nebo pokud nemůže uzamknout data ve schránce, se klient vrátí.

Jakmile klientská aplikace načte ukazatel na data schránky, parsuje data za účelem extrakce aplikace, tématu a názvů položek.

Klientská aplikace určuje, jestli mezi ním a serverovou aplikací už existuje konverzace v tématu. Pokud konverzace existuje, klient zkontroluje, jestli už odkaz pro datovou položku existuje. Pokud takový odkaz existuje, klient zobrazí uživateli pole se zprávou; jinak volá vlastní funkci SendAdvise , která odešle zprávu WM_DDE_ADVISE na server položky.

Pokud mezi klientem a serverem ještě neexistuje konverzace v tématu, klient nejprve zavolá vlastní funkci SendInitiate , která odešle zprávu WM_DDE_INITIATE k vyžádání konverzace a za druhé zavolá vlastní funkci FindServerGivenAppTopic , která vytvoří konverzaci s oknem, které odpoví jménem serverové aplikace. Po zahájení konverzace klientská aplikace zavolá sendAdvise a požádá o odkaz.

Upozornění klienta, že se změnila data

Když klient vytvoří propojení pomocí zprávy WM_DDE_ADVISE , přičemž člen fDeferUpd není nastaven (tj. rovno nule) ve struktuře DDEDATA , klient požádal server o odeslání datové položky pokaždé, když se hodnota položky změní. V takových případech server vykreslí novou hodnotu datové položky v dříve zadaném formátu a odešle klientovi WM_DDE_DATA zprávu, jak je znázorněno v následujícím příkladu.

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

V tomto příkladu klient zpracuje hodnotu položky podle potřeby. Pokud je nastaven příznak fAckReq pro položku, klient odešle server pozitivní WM_DDE_ACK zprávu.

Když klient vytvoří propojení se sadou členů fDeferUpd (tj. rovnou 1), klient požádal, aby se při každé změně dat odeslalo pouze oznámení, nikoli samotná data. V takových případech, když se hodnota položky změní, server nevykreslí hodnotu, ale jednoduše odešle klientovi WM_DDE_DATA zprávu s popisovačem dat null, jak je znázorněno v následujícím příkladu.

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

V případě potřeby může klient požádat o nejnovější hodnotu datové položky tak, že vydá normální WM_DDE_REQUEST zprávu, nebo může jednoduše ignorovat oznámení ze serveru, že se data změnila. V obou případech, pokud fAckReq je rovna 1, očekává se, že klient odešle pozitivní WM_DDE_ACK zprávu na server.

Pokud klient požádá o ukončení konkrétního datového propojení, klient odešle serveru WM_DDE_UNADVISE zprávu, jak je znázorněno v následujícím příkladu.

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 zkontroluje, jestli má klient aktuálně odkaz na konkrétní položku v této konverzaci. Pokud existuje odkaz, server odešle klientovi pozitivní WM_DDE_ACK zprávu; server už není nutný k odesílání aktualizací o položce. Pokud neexistuje žádný odkaz, server odešle klientovi negativní WM_DDE_ACK zprávu.

Zpráva WM_DDE_UNADVISE určuje formát dat. Formát nuly informuje server, aby zastavil všechny odkazy pro zadanou položku, i když je vytvořeno několik horkých odkazů a každý používá jiný formát.

Pokud chcete ukončit všechny odkazy na konverzaci, klientská aplikace odešle serveru zprávu WM_DDE_UNADVISE s atomem položky s hodnotou null. Server určuje, jestli má konverzace aktuálně vytvořený alespoň jeden odkaz. Pokud existuje odkaz, server odešle klientovi pozitivní WM_DDE_ACK zprávu; server pak už nemusí posílat žádné aktualizace v konverzaci. Pokud neexistuje žádný odkaz, server odešle klientovi negativní WM_DDE_ACK zprávu.

Provádění příkazů v serverové aplikaci

Aplikace můžou pomocí WM_DDE_EXECUTE zprávy způsobit provedení určitého příkazu nebo řady příkazů v jiné aplikaci. Klient odešle serveru zprávu WM_DDE_EXECUTE obsahující popisovač příkazového řetězce, jak je znázorněno v následujícím příkladu.

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

V tomto příkladu se server pokusí provést zadaný příkazový řetězec. Pokud bude úspěšné, server odešle klientovi pozitivní WM_DDE_ACK zprávu; v opačném případě odešle zápornou zprávu WM_DDE_ACK . Tato zpráva WM_DDE_ACK znovu použije popisovač hCommand, který byl předán v původní zprávě WM_DDE_EXECUTE.

Pokud řetězec spuštění příkazu klienta požádá o ukončení serveru, měl by server odpovědět odesláním pozitivní WM_DDE_ACK zprávy a odesláním WM_DDE_TERMINATE zprávy před ukončením. Všechny ostatní příkazy odeslané se zprávou WM_DDE_EXECUTE by se měly spouštět synchronně; to znamená, že server by měl odeslat WM_DDE_ACK zprávu až po úspěšném dokončení příkazu.

Ukončení konverzace

Klient nebo server může kdykoli ukončit konverzaci WM_DDE_TERMINATE zprávou. Podobně by měly být klientské i serverové aplikace připravené k přijetí této zprávy kdykoli. Aplikace musí před vypnutím ukončit všechny své konverzace.

V následujícím příkladu aplikace ukončující konverzaci publikuje WM_DDE_TERMINATE zprávu.

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

To informuje druhou aplikaci, že odesílající aplikace neodešle žádné další zprávy a příjemce může zavřít okno. Příjemce se ve všech případech očekává, že odpoví okamžitě odesláním WM_DDE_TERMINATE zprávy. Příjemce nesmí odeslat negativní, zaneprázdněný ani pozitivní WM_DDE_ACK zprávu.

Jakmile aplikace odešle partnerovi zprávu WM_DDE_TERMINATE v konverzaci DDE, nesmí odpovídat na zprávy od daného partnera, protože partner mohl zničit okno, do kterého by byla odpověď odeslána.

Pokud aplikace obdrží jinou zprávu DDE než WM_DDE_TERMINATE po publikování WM_DDE_TERMINATE, měla by uvolnit všechny objekty přidružené k přijatým zprávám s výjimkou popisovačů dat pro WM_DDE_DATA nebo WM_DDE_POKE zprávy, které nemají nastavený člen fRelease .

Když se aplikace chystá ukončit, měla by ukončit všechny aktivní konverzace DDE před dokončením zpracování WM_DESTROY zprávy. Pokud ale aplikace neukončí aktivní konverzace DDE, systém ukončí všechny konverzace DDE přidružené k oknem při zničení okna. Následující příklad ukazuje, jak serverová aplikace ukončí všechny konverzace 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; 
}