Uso di Dynamic Data Exchange

Questa sezione include esempi di codice per le attività seguenti:

Avvio di una conversazione

Per avviare una conversazione DDE (Dynamic Data Exchange), il client invia un messaggio di WM_DDE_INITIATE . In genere, il client trasmette questo messaggio chiamando SendMessage, con –1 come primo parametro. Se l'applicazione dispone già dell'handle di finestra per l'applicazione server, può inviare il messaggio direttamente a tale finestra. Il client prepara gli atom per il nome dell'applicazione e il nome dell'argomento chiamando GlobalAddAtom. Il client può richiedere conversazioni con qualsiasi potenziale applicazione server e per qualsiasi argomento potenziale fornendo atom NULL (jolly) per l'applicazione e l'argomento.

Nell'esempio seguente viene illustrato come il client avvia una conversazione, in cui vengono specificati sia l'applicazione che l'argomento.

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

Nota

Se l'applicazione usa atomi NULL, non è necessario usare le funzioni GlobalAddAtom e GlobalDeleteAtom. In questo esempio, l'applicazione client crea due atom globali contenenti rispettivamente il nome del server e il nome dell'argomento.

 

L'applicazione client invia un messaggio di WM_DDE_INITIATE con questi due atomi nel parametro lParam del messaggio. Nella chiamata alla funzione SendMessage , l'handle di finestra speciale –1 indica al sistema di inviare questo messaggio a tutte le altre applicazioni attive. SendMessage non torna all'applicazione client finché tutte le applicazioni che ricevono il messaggio non hanno a sua volta il controllo restituito al sistema. Ciò significa che tutti i messaggi WM_DDE_ACK inviati in risposta dalle applicazioni server sono garantiti che il client sia stato elaborato dal momento in cui la chiamata SendMessage è stata restituita.

Al termine della restituzione di SendMessage , l'applicazione client elimina gli atomi globali.

Le applicazioni server rispondono in base alla logica illustrata nel diagramma seguente.

server application response logic

Per confermare uno o più argomenti, il server deve creare atomi per ogni conversazione (che richiedono atomi di nome applicazione duplicati se sono presenti più argomenti) e inviare un messaggio WM_DDE_ACK per ogni conversazione, come illustrato nell'esempio seguente.

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

Quando un server risponde con un messaggio di WM_DDE_ACK , l'applicazione client deve salvare un handle nella finestra del server. Il client che riceve l'handle come parametro wParam del messaggio WM_DDE_ACK quindi invia tutti i messaggi DDE successivi alla finestra del server identificata da questo handle.

Se l'applicazione client usa un atom NULL per il nome dell'applicazione o il nome dell'argomento, aspettarsi che l'applicazione riceva riconoscimenti da più applicazioni server. Più riconoscimenti possono provenire anche da più istanze di un server DDE, anche se l'applicazione client non usa atom. Un server deve sempre usare una finestra univoca per ogni conversazione. La procedura della finestra nell'applicazione client può usare un handle per la finestra del server (fornita come parametro lParam di WM_DDE_INITIATE) per tenere traccia di più conversazioni. In questo modo, una singola finestra client può elaborare diverse conversazioni senza dover terminare e riconnettersi con una nuova finestra client per ogni conversazione.

Trasferimento di un singolo elemento

Dopo aver stabilito una conversazione DDE, il client può recuperare il valore di un elemento di dati dal server eseguendo il messaggio di WM_DDE_REQUEST oppure inviando un valore dell'elemento di dati al server eseguendo WM_DDE_POKE.

Recupero di un elemento dal server

Per recuperare un elemento dal server, il client invia al server un messaggio di WM_DDE_REQUEST che specifica l'elemento e il formato da recuperare, come illustrato nell'esempio seguente.

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

In questo esempio, il client specifica il formato degli Appunti CF_TEXT come formato preferito per l'elemento di dati richiesto.

Il ricevitore (server) del messaggio WM_DDE_REQUEST deve in genere eliminare l'atomo di elemento, ma se la chiamata PostMessage ha esito negativo, il client deve eliminare l'atom.

Se il server ha accesso all'elemento richiesto e può eseguirne il rendering nel formato richiesto, il server copia il valore dell'elemento come oggetto memoria condivisa e invia al client un messaggio di WM_DDE_DATA , come illustrato nell'esempio seguente.

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

In questo esempio, l'applicazione server alloca un oggetto memoria per contenere l'elemento di dati. L'oggetto dati viene inizializzato come struttura DDEDATA.

L'applicazione server imposta quindi il membro cfFormat della struttura su CF_TEXT per informare l'applicazione client che i dati sono in formato testo. Il client risponde copiando il valore dei dati richiesti nel membro Value della struttura DDEDATA . Dopo che il server ha compilato l'oggetto dati, il server sblocca i dati e crea un atomo globale contenente il nome dell'elemento di dati.

Infine, il server rilascia il messaggio WM_DDE_DATA chiamando PostMessage. L'handle per l'oggetto dati e l'atom contenente il nome dell'elemento vengono compressi nel parametro lParam del messaggio dalla funzione PackDDElParam.

Se PostMessage ha esito negativo, il server deve usare la funzione FreeDDElParam per liberare il parametro lParam compresso. Il server deve anche liberare il parametro lParam compresso per il messaggio WM_DDE_REQUEST ricevuto.

Se il server non riesce a soddisfare la richiesta, invia un messaggio negativo WM_DDE_ACK al client, come illustrato nell'esempio seguente.

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

Dopo aver ricevuto un messaggio di WM_DDE_DATA , il client elabora il valore dell'elemento di dati in base alle esigenze. Se quindi il membro fAckReq a cui punta il messaggio WM_DDE_DATA è 1, il client deve inviare al server un messaggio di WM_DDE_ACK positivo, come illustrato nell'esempio seguente.

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

In questo esempio, il client esamina il formato dei dati. Se il formato non è CF_TEXT (o se il client non è in grado di bloccare la memoria per i dati), il client invia un messaggio negativo WM_DDE_ACK per indicare che non può elaborare i dati. Se il client non riesce a bloccare un handle di dati perché l'handle contiene il membro fAckReq , il client non deve inviare un messaggio negativo WM_DDE_ACK . Al contrario, il client deve terminare la conversazione.

Se un client invia un riconoscimento negativo in risposta a un messaggio di WM_DDE_DATA , il server è responsabile della liberazione della memoria (ma non del parametro lParam ) a cui fa riferimento il messaggio WM_DDE_DATA associato al riconoscimento negativo.

Se è in grado di elaborare i dati, il client esamina il membro fAckReq della struttura DDEDATA per determinare se il server ha richiesto che sia stato informato che il client ha ricevuto ed elaborato correttamente i dati. Se il server ha richiesto queste informazioni, il client invia al server un messaggio di WM_DDE_ACK positivo.

Poiché lo sblocco dei dati invalida il puntatore ai dati, il client salva il valore del membro fRelease prima di sbloccare l'oggetto dati. Dopo aver salvato il valore, il client lo esamina per determinare se l'applicazione server ha richiesto al client di liberare la memoria contenente i dati; il cliente agisce di conseguenza.

Dopo aver ricevuto un messaggio negativo WM_DDE_ACK , il client può chiedere di nuovo lo stesso valore dell'elemento, specificando un formato diverso per gli Appunti. In genere, un client chiede prima di tutto il formato più complesso che può supportare, quindi, se necessario, eseguire il passaggio indietro attraverso formati progressivamente più semplici fino a quando non trova un server in grado di fornire.

Se il server supporta l'elemento Formats dell'argomento di sistema, il client può determinare una volta che gli Appunti formatta il server supportano, anziché determinarli ogni volta che il client richiede un elemento.

Invio di un elemento al server

Il client può inviare un valore di elemento al server usando il messaggio WM_DDE_POKE. Il client esegue il rendering dell'elemento da inviare e invia il messaggio di WM_DDE_POKE , come illustrato nell'esempio seguente.

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

Nota

L'invio di dati tramite un messaggio di WM_DDE_POKE equivale essenzialmente all'invio tramite WM_DDE_DATA, ad eccezione del fatto che WM_DDE_POKE viene inviato dal client al server.

 

Se il server è in grado di accettare il valore dell'elemento dati nel formato sottoposto a rendering dal client, il server elabora il valore dell'elemento in base alle esigenze e invia al client un messaggio positivo WM_DDE_ACK . Se non è in grado di elaborare il valore dell'elemento, a causa del relativo formato o per altri motivi, il server invia al client un messaggio negativo WM_DDE_ACK .

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.

In questo esempio il server chiama GlobalGetAtomName per recuperare il nome dell'elemento inviato dal client. Il server determina quindi se supporta l'elemento e se viene eseguito il rendering dell'elemento nel formato corretto, ovvero CF_TEXT. Se l'elemento non è supportato e non viene eseguito il rendering nel formato corretto o se il server non è in grado di bloccare la memoria per i dati, il server invia un riconoscimento negativo all'applicazione client. Si noti che in questo caso, l'invio di un riconoscimento negativo è corretto perché si presuppone che i messaggi WM_DDE_POKE abbiano sempre impostato il membro fAckReq. Il server deve ignorare il membro.

Se un server invia un riconoscimento negativo in risposta a un messaggio di WM_DDE_POKE , il client è responsabile della liberazione della memoria (ma non del parametro lParam ) a cui fa riferimento il messaggio WM_DDE_POKE associato al riconoscimento negativo.

Un'applicazione client può usare DDE per stabilire un collegamento a un elemento in un'applicazione server. Dopo aver stabilito tale collegamento, il server invia aggiornamenti periodici dell'elemento collegato al client, in genere, ogni volta che il valore dell'elemento viene modificato. Pertanto, viene stabilito un flusso di dati permanente tra le due applicazioni; questo flusso di dati rimane attivo fino a quando non viene disconnesso in modo esplicito.

Il client avvia un collegamento dati pubblicando un messaggio di WM_DDE_ADVIedizione Standard, come illustrato nell'esempio seguente.

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 
 
}

In questo esempio, l'applicazione client imposta il flag fDeferUpd del messaggio di WM_DDE_ADVIedizione Standard su FAL edizione Standard. In questo modo l'applicazione server invia i dati al client ogni volta che i dati vengono modificati.

Se il server non è in grado di gestire la richiesta di WM_DDE_ADVIedizione Standard, invia al client un messaggio negativo WM_DDE_ACK. Tuttavia, se il server ha accesso all'elemento e può eseguirne il rendering nel formato richiesto, il server annota il nuovo collegamento (richiamando i flag specificati nel parametro hOptions ) e invia al client un messaggio positivo WM_DDE_ACK . Da allora, fino a quando il client rilascia un messaggio di corrispondenza WM_DDE_UNADVIedizione Standard, il server invia i nuovi dati al client ogni volta che il valore dell'elemento cambia nel server.

Il messaggio WM_DDE_ADVIedizione Standard stabilisce il formato dei dati da scambiare durante il collegamento. Se il client tenta di stabilire un altro collegamento con lo stesso elemento ma usa un formato di dati diverso, il server può scegliere di rifiutare il secondo formato di dati o tentare di supportarlo. Se è stato stabilito un collegamento ad accesso frequente per qualsiasi elemento di dati, il server può supportare un solo formato dati alla volta. Ciò è dovuto al fatto che il messaggio di WM_DDE_DATA per un collegamento ad accesso frequente ha un handle dati NULL, che in caso contrario contiene le informazioni sul formato. Pertanto, un server deve rifiutare tutti i collegamenti ad accesso frequente per un elemento già collegato e deve rifiutare tutti i collegamenti per un elemento con collegamenti ad accesso frequente. Un'altra interpretazione può essere che il server modifica il formato e lo stato attivo o frequente di un collegamento quando viene richiesto un secondo collegamento per lo stesso elemento di dati.

In generale, le applicazioni client non devono tentare di stabilire più di un collegamento alla volta per un elemento di dati.

Le applicazioni che supportano collegamenti dati ad accesso frequente o ad accesso frequente supportano in genere un formato registrato degli Appunti denominato Link. Se associato ai comandi Copia e Incolla collegamento dell'applicazione, questo formato degli Appunti consente all'utente di stabilire conversazioni DDE tra applicazioni semplicemente copiando un elemento di dati nell'applicazione server e incollandolo nell'applicazione client.

Un'applicazione server supporta il formato Degli Appunti di collegamento inserendo negli Appunti una stringa contenente i nomi dell'applicazione, dell'argomento e degli elementi quando l'utente sceglie il comando Copia dal menu Modifica . Di seguito è riportato il formato standard link:

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

Un singolo carattere Null separa i nomi e due caratteri Null terminano l'intera stringa.

Sia le applicazioni client che server devono registrare il formato Degli Appunti di collegamento, come illustrato di seguito:

cfLink = RegisterClipboardFormat("Link");

Un'applicazione client supporta il formato Degli Appunti collegamento tramite un comando Incolla collegamento nel menu Modifica. Quando l'utente sceglie questo comando, l'applicazione client analizza i nomi dell'applicazione, dell'argomento e degli elementi dai dati degli Appunti in formato collegamento. Usando questi nomi, l'applicazione client avvia una conversazione per l'applicazione e l'argomento, se tale conversazione non esiste già. L'applicazione client invia quindi un messaggio WM_DDE_ADVIedizione Standard all'applicazione server, specificando il nome dell'elemento contenuto nei dati degli Appunti in formato collegamento.

Di seguito è riportato un esempio della risposta di un'applicazione client quando l'utente sceglie il comando Incolla collegamento.

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

In questo esempio, l'applicazione client apre gli Appunti e determina se contiene dati nel formato collegamento (ovvero cfLink) registrato in precedenza. In caso contrario, o se non è in grado di bloccare i dati negli Appunti, il client restituisce .

Dopo che l'applicazione client recupera un puntatore ai dati degli Appunti, analizza i dati per estrarre i nomi dell'applicazione, dell'argomento e degli elementi.

L'applicazione client determina se esiste già una conversazione sull'argomento tra di essa e l'applicazione server. Se esiste una conversazione, il client verifica se esiste già un collegamento per l'elemento di dati. Se tale collegamento esiste, il client visualizza una finestra di messaggio all'utente; in caso contrario, chiama la propria funzione SendAdvise per inviare un messaggio WM_DDE_ADVIedizione Standard al server per l'elemento.

Se non esiste già una conversazione sull'argomento tra il client e il server, il client chiama innanzitutto la propria funzione SendInitiate per trasmettere il messaggio WM_DDE_INITIATE per richiedere una conversazione e, in secondo luogo, chiama la propria funzione FindServerGivenAppTopic per stabilire la conversazione con la finestra che risponde per conto dell'applicazione server. Dopo l'avvio della conversazione, l'applicazione client chiama SendAdvise per richiedere il collegamento.

Notifica al client che i dati sono stati modificati

Quando il client stabilisce un collegamento utilizzando il messaggio di WM_DDE_ADVIedizione Standard, con il membro fDeferUpd non impostato (uguale a zero) nella struttura DDEDATA, il client ha richiesto al server di inviare l'elemento dati ogni volta che il valore dell'elemento cambia. In questi casi, il server esegue il rendering del nuovo valore dell'elemento di dati nel formato specificato in precedenza e invia al client un messaggio di WM_DDE_DATA , come illustrato nell'esempio seguente.

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

In questo esempio, il client elabora il valore dell'elemento in base alle esigenze. Se viene impostato il flag fAckReq per l'elemento, il client invia al server un messaggio di WM_DDE_ACK positivo.

Quando il client stabilisce il collegamento, con il set di membri fDeferUpd (ovvero uguale a 1), il client ha richiesto che solo una notifica, non i dati stessi, vengano inviati ogni volta che i dati cambiano. In questi casi, quando il valore dell'elemento cambia, il server non esegue il rendering del valore ma invia semplicemente al client un messaggio WM_DDE_DATA con un handle dati Null, come illustrato nell'esempio seguente.

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

Se necessario, il client può richiedere il valore più recente dell'elemento di dati eseguendo un normale messaggio di WM_DDE_REQUEST oppure può semplicemente ignorare l'avviso dal server che i dati sono stati modificati. In entrambi i casi, se fAckReq è uguale a 1, il client dovrebbe inviare un messaggio positivo WM_DDE_ACK al server.

Se il client richiede l'interruzione di un collegamento dati specifico, il client invia al server un messaggio WM_DDE_UNADVIedizione Standard, come illustrato nell'esempio seguente.

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

Il server controlla se il client dispone attualmente di un collegamento all'elemento specifico in questa conversazione. Se esiste un collegamento, il server invia al client un messaggio di WM_DDE_ACK positivo. Il server non è più necessario per inviare aggiornamenti sull'elemento. Se non esiste alcun collegamento, il server invia al client un messaggio negativo WM_DDE_ACK .

Il messaggio WM_DDE_UNADVIedizione Standard specifica un formato di dati. Un formato pari a zero indica al server di arrestare tutti i collegamenti per l'elemento specificato, anche se vengono stabiliti diversi collegamenti ad accesso frequente e ognuno usa un formato diverso.

Per terminare tutti i collegamenti per una conversazione, l'applicazione client invia al server un messaggio WM_DDE_UNADVIedizione Standard con un atom di elemento Null. Il server determina se la conversazione ha almeno un collegamento attualmente stabilito. Se esiste un collegamento, il server invia al client un messaggio di WM_DDE_ACK positivo; il server non deve più inviare aggiornamenti nella conversazione. Se non esiste alcun collegamento, il server invia al client un messaggio negativo WM_DDE_ACK .

Esecuzione di comandi in un'applicazione server

Le applicazioni possono usare il messaggio WM_DDE_EXECUTE per eseguire un determinato comando o una serie di comandi in un'altra applicazione. A tale scopo, il client invia al server un messaggio di WM_DDE_EXECUTE contenente un handle a una stringa di comando, come illustrato nell'esempio seguente.

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

In questo esempio il server tenta di eseguire la stringa di comando specificata. Se ha esito positivo, il server invia al client un messaggio di WM_DDE_ACK positivo; in caso contrario, invia un messaggio negativo WM_DDE_ACK. Questo WM_DDE_ACK messaggio riutilizza l'handle hCommand passato nel messaggio di WM_DDE_EXECUTE originale.

Se la stringa di esecuzione del comando del client richiede che il server termini, il server deve rispondere inviando un messaggio di WM_DDE_ACK positivo e quindi pubblicare un messaggio di WM_DDE_TERMINATE prima di terminare. Tutti gli altri comandi inviati con un messaggio di WM_DDE_EXECUTE devono essere eseguiti in modo sincrono, ovvero il server deve inviare un messaggio WM_DDE_ACK solo dopo aver completato correttamente il comando.

Terminazione di una conversazione

Il client o il server può emettere un messaggio WM_DDE_TERMINATE per terminare una conversazione in qualsiasi momento. Analogamente, sia le applicazioni client che le applicazioni server devono essere preparate a ricevere questo messaggio in qualsiasi momento. Un'applicazione deve terminare tutte le conversazioni prima dell'arresto.

Nell'esempio seguente l'applicazione che termina la conversazione pubblica un messaggio di WM_DDE_TERMINATE.

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

In questo modo l'altra applicazione informa che l'applicazione di invio non invierà altri messaggi e il destinatario può chiudere la finestra. Il destinatario è previsto in tutti i casi per rispondere tempestivamente inviando un messaggio di WM_DDE_TERMINATE. Il destinatario non deve inviare un messaggio negativo, occupato o positivo WM_DDE_ACK .

Dopo che un'applicazione ha inviato il messaggio di WM_DDE_TERMINATE al partner in una conversazione DDE, non deve rispondere ai messaggi del partner, perché il partner potrebbe aver distrutto la finestra a cui verrebbe inviata la risposta.

Se un'applicazione riceve un messaggio DDE diverso da WM_DDE_TERMINATE dopo che è stato pubblicato WM_DDE_TERMINATE, deve liberare tutti gli oggetti associati ai messaggi ricevuti, ad eccezione degli handle di dati per WM_DDE_DATA o WM_DDE_POKE messaggi che non dispongono del membro fRelease impostato.

Quando un'applicazione sta per terminare, deve terminare tutte le conversazioni DDE attive prima di completare l'elaborazione del messaggio WM_DESTROY. Tuttavia, se un'applicazione non termina le conversazioni DDE attive, il sistema terminerà tutte le conversazioni DDE associate a una finestra quando la finestra viene eliminata definitivamente. Nell'esempio seguente viene illustrato come un'applicazione server termina tutte le conversazioni 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; 
}