Freigeben über


Verwenden von dynamischem Datenaustausch

Dieser Abschnitt enthält Codebeispiele für die folgenden Aufgaben:

Initiieren einer Unterhaltung

Um eine Dynamic Data Exchange (DDE)-Kommunikation einzuleiten, sendet der Client eine WM_DDE_INITIATE-Nachricht. In der Regel sendet der Client diese Nachricht durch Aufrufen von SendMessage mit -1 als ersten Parameter. Wenn die Anwendung bereits über das Fensterhandle für die Serveranwendung verfügt, kann sie die Nachricht direkt an dieses Fenster senden. Der Client bereitet Atome für den Anwendungsnamen und den Themennamen durch Aufrufen von GlobalAddAtom vor. Der Client kann Gespräche mit jeder potenziellen Serveranwendung und zu jedem möglichen Thema anfordern, indem er NULL-Atome (Wildcard) für die Anwendung und das Thema angibt.

Das folgende Beispiel veranschaulicht, wie der Client eine Unterhaltung initiiert, in der sowohl die Anwendung als auch das Thema angegeben sind.

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

Hinweis

Wenn Ihre Anwendung NULL-Atome verwendet, müssen Sie die Funktionen GlobalAddAtom und GlobalDeleteAtom nicht verwenden. In diesem Beispiel erstellt die Clientanwendung zwei globale Atome, die den Namen des Servers bzw. den Namen des Themas enthalten.

 

Die Clientanwendung sendet eine WM_DDE_INITIATE Nachricht mit diesen beiden Atomen im lParam-Parameter der Nachricht. Im Aufruf der SendMessage-Funktion leitet das spezielle Fensterhandle –1 das System an, diese Nachricht an alle anderen aktiven Anwendungen zu senden. SendMessage kehrt erst dann zur Clientanwendung zurück, wenn alle Anwendungen, die die Meldung empfangen haben, die Steuerung an das System zurückgegeben haben. Das bedeutet, dass alle WM_DDE_ACK-Nachrichten, die als Antwort von den Server-Anwendungen gesendet werden, garantiert vom Client verarbeitet worden sind, wenn der SendMessage-Aufruf erfolgt ist.

Nachdem SendMessage zurückgegeben hat, löscht die Clientanwendung die globalen Atome.

Serveranwendungen reagieren entsprechend der Logik, die im folgenden Diagramm veranschaulicht wird.

Serveranwendungsantwortlogik

Um ein oder mehrere Themen zu bestätigen, muss der Server Atome für jede Unterhaltung erstellen (die doppelte Anwendungsnamen-Atome erfordern, wenn mehrere Themen vorhanden sind) und eine WM_DDE_ACK Nachricht für jede Unterhaltung senden, wie im folgenden Beispiel dargestellt.

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

Wenn ein Server mit einer WM_DDE_ACK Nachricht antwortet, sollte die Clientanwendung ein Handle im Serverfenster speichern. Der Client, der das Handle als wParam-Parameter der WM_DDE_ACK Nachricht empfängt, sendet dann alle nachfolgenden DDE-Nachrichten an das Serverfenster, das dieses Handle identifiziert.

Wenn Ihre Clientanwendung ein NULL-Atom für den Anwendungsnamen oder den Themennamen verwendet, erwarten Sie, dass die Anwendung Bestätigungen von mehr als einer Serveranwendung empfängt. Mehrere Bestätigungen können auch von mehreren Instanzen eines DDE-Servers stammen, auch wenn Ihre Clientanwendung keine NULL-Atome verwendet. Ein Server sollte immer ein eindeutiges Fenster für jede Unterhaltung verwenden. Die Fensterprozedur in der Clientanwendung kann ein Handle für das Serverfenster (bereitgestellt als lParam-Parameter von WM_DDE_INITIATE) verwenden, um mehrere Unterhaltungen nachzuverfolgen. Dadurch kann ein einzelnes Clientfenster mehrere Unterhaltungen verarbeiten, ohne eine Verbindung mit einem neuen Clientfenster für jede Unterhaltung beenden und erneut herstellen zu müssen.

Übertragen eines einzelnen Elements

Nachdem eine DDE-Unterhaltung eingerichtet wurde, kann der Client entweder den Wert eines Datenelements vom Server abrufen, indem er die WM_DDE_REQUEST Nachricht ausgibt, oder einen Datenelementwert an den Server senden, indem er WM_DDE_POKE ausgibt.

Abrufen eines Elements vom Server

Um ein Element vom Server abzurufen, sendet der Client dem Server eine WM_DDE_REQUEST Nachricht, die das abzurufende Element und format angibt, wie im folgenden Beispiel gezeigt.

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 diesem Beispiel gibt der Client das Zwischenablageformat CF_TEXT als bevorzugtes Format für das angeforderte Datenelement an.

Der Empfänger (Server) der WM_DDE_REQUEST Nachricht muss in der Regel das Element atom löschen, aber wenn der PostMessage-Aufruf fehlschlägt, muss der Client das Atom löschen.

Wenn der Server Zugriff auf das angeforderte Element hat und es im angeforderten Format rendern kann, kopiert der Server den Elementwert als freigegebenes Speicherobjekt und sendet dem Client eine WM_DDE_DATA Nachricht, wie im folgenden Beispiel dargestellt.

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

In diesem Beispiel weist die Serveranwendung ein Speicherobjekt zu, das das Datenelement enthält. Das Datenobjekt wird als DDEDATA-Struktur initialisiert.

Anschließend legt die Serveranwendung das cfFormat-Element der Struktur auf CF_TEXT fest, um die Clientanwendung darüber zu informieren, dass sich die Daten im Textformat befinden. Der Client antwortet, indem der Wert der angeforderten Daten in das Value-Element der DDEDATA-Struktur kopiert wird. Nachdem der Server das Datenobjekt ausgefüllt hat, entsperrt der Server die Daten und erstellt ein globales Atom mit dem Namen des Datenelements.

Schließlich gibt der Server die WM_DDE_DATA Nachricht durch Aufrufen von PostMessage aus. Der Handle zum Datenobjekt und das Atom mit dem Elementnamen werden von der PackDDElParam-Funktion in den Parameter lParam der Nachricht gepackt.

Wenn PostMessage fehlschlägt, muss der Server die FreeDDElParam-Funktion verwenden, um den gepackten lParam-Parameter freizulegen. Der Server muss auch den gepackten lParam-Parameter für die empfangene WM_DDE_REQUEST Nachricht freigeben.

Wenn der Server die Anforderung nicht erfüllen kann, sendet er eine negative WM_DDE_ACK Nachricht an den Client, wie im folgenden Beispiel gezeigt.

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

Beim Empfang einer WM_DDE_DATA Nachricht verarbeitet der Client den Datenelementwert entsprechend. Wenn dann das fAckReq-Element , auf das in der WM_DDE_DATA Nachricht verweist, 1 lautet, muss der Client dem Server eine positive WM_DDE_ACK Nachricht senden, wie im folgenden Beispiel gezeigt.

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

In diesem Beispiel untersucht der Client das Format der Daten. Wenn das Format nicht CF_TEXT ist (oder wenn der Client den Speicher für die Daten nicht sperren kann), sendet der Client eine negative WM_DDE_ACK Meldung, um anzugeben, dass die Daten nicht verarbeitet werden können. Wenn der Client ein Datenhandle nicht sperren kann, da das Handle das fAckReq-Element enthält, sollte der Client keine negative WM_DDE_ACK Nachricht senden. Stattdessen sollte der Client die Unterhaltung beenden.

Wenn ein Client als Reaktion auf eine WM_DDE_DATA Nachricht eine negative Bestätigung sendet, ist der Server dafür verantwortlich, den Speicher freizugeben (aber nicht den lParam-Parameter ), auf den durch die WM_DDE_DATA Nachricht verwiesen wird, die der negativen Bestätigung zugeordnet ist.

Wenn die Daten verarbeitet werden können, überprüft der Client das fAckReq-Mitglied der DDEDATA-Struktur , um festzustellen, ob der Server angefordert hat, dass er informiert wird, dass der Client die Daten erfolgreich empfangen und verarbeitet hat. Wenn der Server diese Informationen angefordert hat, sendet der Client dem Server eine positive WM_DDE_ACK Nachricht.

Da das Entsperren von Daten den Zeiger auf die Daten ungültig macht, speichert der Client den Wert des fRelease-Elements , bevor das Datenobjekt entsperrt wird. Nach dem Speichern des Werts überprüft der Client ihn dann, um festzustellen, ob die Serveranwendung den Client aufgefordert hat, den Speicher mit den Daten freizugeben; der Kunde handelt entsprechend.

Beim Empfang einer negativen WM_DDE_ACK Nachricht kann der Client denselben Elementwert erneut anfordern und dabei ein anderes Zwischenablageformat angeben. In der Regel wird ein Client zuerst das komplexeste Format anfragen, das es unterstützen kann, und dann bei Bedarf zu schrittweise einfacheren Formaten übergehen, bis es eines findet, das der Server bereitstellen kann.

Wenn der Server das Element „Formate“ des Systemthemas unterstützt, kann der Client einmal bestimmen, welche Zwischenablageformate der Server unterstützt, anstatt sie jedes Mal zu bestimmen, wenn der Client ein Element anfordert.

Senden eines Elements an den Server

Der Client kann einen Elementwert an den Server senden, indem er die Meldung WM_DDE_POKE verwendet. Der Client rendert das zu sendende Element und sendet die WM_DDE_POKE Nachricht, wie im folgenden Beispiel dargestellt.

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

Hinweis

Das Senden von Daten mithilfe einer WM_DDE_POKE Nachricht entspricht im Wesentlichen dem Senden mithilfe von WM_DDE_DATA, mit der Ausnahme, dass WM_DDE_POKE vom Client an den Server gesendet wird.

 

Wenn der Server den Datenelementwert im vom Client gerenderten Format akzeptieren kann, verarbeitet der Server den Elementwert entsprechend und sendet dem Client eine positive WM_DDE_ACK Nachricht. Wenn der Elementwert aufgrund seines Formats oder aus anderen Gründen nicht verarbeitet werden kann, sendet der Server dem Client eine negative WM_DDE_ACK Nachricht.

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.

In diesem Beispiel ruft der Server GlobalGetAtomName auf, um den Namen des Elements abzurufen, das der Client gesendet hat. Der Server bestimmt dann, ob es das Element unterstützt und ob das Element im richtigen Format gerendert wird (d. b. CF_TEXT). Wenn das Element nicht unterstützt und nicht im richtigen Format gerendert wird oder der Server den Speicher für die Daten nicht sperren kann, sendet der Server eine negative Bestätigung an die Clientanwendung. Beachten Sie, dass in diesem Fall das Senden einer negativen Bestätigung korrekt ist, da bei WM_DDE_POKE-Nachrichten immer davon ausgegangen wird, dass das fAckReq-Mitglied festgelegt ist. Der Server sollte das Mitglied ignorieren.

Wenn ein Server als Reaktion auf eine WM_DDE_POKE Nachricht eine negative Bestätigung sendet, ist der Client dafür verantwortlich, den Speicher (aber nicht den lParam-Parameter ) freizugeben, auf den durch die WM_DDE_POKE Nachricht verwiesen wird, die der negativen Bestätigung zugeordnet ist.

Eine Clientanwendung kann DDE verwenden, um eine Verknüpfung mit einem Element in einer Serveranwendung herzustellen. Nachdem eine solche Verknüpfung hergestellt wurde, sendet der Server regelmäßige Aktualisierungen des verknüpften Elements an den Client, in der Regel, wenn sich der Wert des Elements ändert. So wird ein dauerhafter Datenstrom zwischen den beiden Anwendungen hergestellt; Dieser Datenstrom bleibt so lange vorhanden, bis er explizit getrennt ist.

Der Client initiiert einen Datenlink, indem er eine WM_DDE_ADVISE Nachricht veröffentlicht, wie im folgenden Beispiel gezeigt.

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 
 
}

In diesem Beispiel legt die Clientanwendung das fDeferUpd-Flag der WM_DDE_ADVISE Nachricht auf FALSE fest. Dadurch wird die Serveranwendung aufgefordert, die Daten immer dann an den Client zu senden, wenn sich die Daten ändern.

Wenn der Server die WM_DDE_ADVISE Anforderung nicht bedienen kann, sendet er dem Client eine negative WM_DDE_ACK Nachricht. Wenn der Server jedoch Zugriff auf das Element hat und es im angeforderten Format rendern kann, notiert der Server den neuen Link (rückruft die im hOptions-Parameter angegebenen Flags ) und sendet dem Client eine positive WM_DDE_ACK Nachricht. Ab diesem Zeitpunkt sendet der Server bei jeder Änderung des Werts des Elements auf dem Server die neuen Daten an den Client, bis der Client eine übereinstimmende WM_DDE_UNADVISE Nachricht ausgibt.

Die WM_DDE_ADVISE Nachricht richtet das Format der Daten ein, die während des Links ausgetauscht werden sollen. Wenn der Client versucht, eine andere Verknüpfung mit demselben Element herzustellen, aber ein anderes Datenformat verwendet, kann der Server das zweite Datenformat ablehnen oder versuchen, ihn zu unterstützen. Wenn für ein Datenelement ein warmer Link eingerichtet wurde, kann der Server jeweils nur ein Datenformat unterstützen. Der Grund dafür ist, dass die WM_DDE_DATA-Nachricht für einen Warm Link ein NULL-Datenhandle hat, das ansonsten die Formatinformationen enthält. Daher muss ein Server alle warmen Links für ein bereits verknüpftes Element ablehnen und alle Links für ein Element mit warmen Links ablehnen. Eine andere Interpretation kann sein, dass der Server das Format und den heißen oder warmen Zustand eines Links ändert, wenn ein zweiter Link für dasselbe Datenelement angefordert wird.

Im Allgemeinen sollten Clientanwendungen nicht versuchen, mehrere Verknüpfungen gleichzeitig für ein Datenelement einzurichten.

Anwendungen, die heiße oder warme Datenlinks unterstützen, unterstützen in der Regel ein registriertes Zwischenablageformat namens Link. Wenn dieses Zwischenablage-Format den Befehlen „Link kopieren“ und „Link einfügen“ der Anwendung zugeordnet ist, können Benutzende DDE-Kommunikationen zwischen Anwendungen herstellen, indem sie einfach ein Datenelement in der Serveranwendung kopieren und in die Clientanwendung einfügen.

Eine Serveranwendung unterstützt das Format der Verknüpfungsablage, indem sie in der Zwischenablage eine Zeichenfolge mit den Anwendungs-, Themen- und Elementnamen platziert, wenn der Benutzer im Menü "Bearbeiten" den Befehl "Kopieren" auswählt. Es folgt das Standard-Linkformat:

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

Ein einzelnes NULL-Zeichen trennt die Namen, und zwei NULL-Zeichen beenden die gesamte Zeichenfolge.

Sowohl die Client- als auch die Serveranwendungen müssen das Link-Zwischenablageformat wie folgt registrieren:

cfLink = RegisterClipboardFormat("Link");

Eine Clientanwendung unterstützt das Link-Zwischenablageformat mithilfe eines Befehls "Link einfügen" im Menü "Bearbeiten". Wenn der Benutzer diesen Befehl auswählt, analysiert die Clientanwendung die Namen der Anwendung, des Themas und des Elements aus den Link-Format-Zwischenablagedaten. Mit diesen Namen initiiert die Clientanwendung eine Konversation bezüglich der Anwendung und des Themas, falls eine solche Konversation noch nicht existiert. Die Clientanwendung sendet dann eine WM_DDE_ADVISE Nachricht an die Serveranwendung und gibt den Elementnamen in den Linkformat-Zwischenablagedaten an.

Es folgt ein Beispiel für die Antwort einer Clientanwendung, wenn der Benutzer den Befehl "Link einfügen" auswäht.

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 diesem Beispiel öffnet die Clientanwendung die Zwischenablage und bestimmt, ob sie Daten im Linkformat (d. h. cfLink) enthält, die sie zuvor registriert hatte. Ist dies nicht der Fall oder kann sie die Daten in der Zwischenablage nicht sperren, kehrt der Client zurück.

Nachdem die Client-Anwendung einen Zeiger auf die Daten in der Zwischenablage abgerufen hat, analysiert sie die Daten, um die Anwendungs-, Themen- und Elementnamen zu extrahieren.

Die Clientanwendung bestimmt, ob eine Unterhaltung zu dem Thema bereits zwischen ihr und der Serveranwendung vorhanden ist. Wenn eine Unterhaltung vorhanden ist, überprüft der Client, ob für das Datenelement bereits ein Link vorhanden ist. Wenn eine solche Verknüpfung vorhanden ist, zeigt der Client dem Benutzer ein Meldungsfeld an. andernfalls ruft sie eine eigene SendAdvise-Funktion auf, um eine WM_DDE_ADVISE Nachricht an den Server für das Element zu senden.

Wenn eine Unterhaltung im Thema nicht bereits zwischen dem Client und dem Server vorhanden ist, ruft der Client zunächst seine eigene SendInitiate-Funktion auf, um die WM_DDE_INITIATE Nachricht zu übertragen, um eine Unterhaltung anzufordern, und ruft zweitens seine eigene FindServerGivenAppTopic-Funktion auf, um die Unterhaltung mit dem Fenster herzustellen, das im Auftrag der Serveranwendung antwortet. Nachdem die Unterhaltung begonnen hat, ruft die Clientanwendung SendAdvise auf, um den Link anzufordern.

Benachrichtigen des Clients, dass Daten geändert wurden

Wenn der Client eine Verknüpfung mithilfe der WM_DDE_ADVISE Nachricht herstellt, wobei das fDeferUpd-Element nicht (d. h. gleich Null) in der DDEDATA-Struktur festgelegt ist, hat der Client den Server angefordert, das Datenelement jedes Mal zu senden, wenn sich der Wert des Elements ändert. In solchen Fällen rendert der Server den neuen Wert des Datenelements im zuvor angegebenen Format und sendet dem Client eine WM_DDE_DATA Nachricht, wie im folgenden Beispiel gezeigt.

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

In diesem Beispiel verarbeitet der Client den Elementwert entsprechend. Wenn das fAckReq-Flag für das Element festgelegt ist, sendet der Client dem Server eine positive WM_DDE_ACK Nachricht.

Wenn der Client die Verknüpfung mit dem fDeferUpd-Membersatz (d. h. gleich 1) herstellt, hat der Client angefordert, dass bei jeder Änderung der Daten nur eine Benachrichtigung und nicht die Daten selbst gesendet werden. In solchen Fällen, wenn sich der Elementwert ändert, rendert der Server den Wert nicht, sondern sendet dem Client einfach eine WM_DDE_DATA Nachricht mit einem NULL-Datenhandle, wie im folgenden Beispiel dargestellt.

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

Bei Bedarf kann der Client den neuesten Wert des Datenelements anfordern, indem er eine normale WM_DDE_REQUEST Nachricht ausgibt, oder er kann einfach den Hinweis vom Server ignorieren, dass sich die Daten geändert haben. Wenn fAckReq in beiden Fällen gleich 1 ist, wird erwartet, dass der Client eine positive WM_DDE_ACK Nachricht an den Server sendet.

Wenn der Client anfordert, dass eine bestimmte Datenverbindung beendet wird, sendet der Client dem Server eine WM_DDE_UNADVISE Nachricht, wie im folgenden Beispiel gezeigt.

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

Der Server überprüft, ob der Client derzeit über einen Link zu dem bestimmten Element in dieser Unterhaltung verfügt. Wenn eine Verknüpfung vorhanden ist, sendet der Server dem Client eine positive WM_DDE_ACK Nachricht; Der Server ist dann nicht mehr erforderlich, um Aktualisierungen über das Element zu senden. Wenn keine Verknüpfung vorhanden ist, sendet der Server dem Client eine negative WM_DDE_ACK Nachricht.

Die WM_DDE_UNADVISE Nachricht gibt ein Datenformat an. Ein Format von Null informiert den Server, alle Verknüpfungen für das angegebene Element zu beenden, auch wenn mehrere Hotlinks eingerichtet sind und jedes ein anderes Format verwendet.

Um alle Links für eine Kommunikation zu beenden, sendet die Client-Anwendung dem Server eine WM_DDE_UNADVISE-Nachricht mit einem null item Atom. Der Server stellt fest, ob für die Kommunikation mindestens ein Link aktuell eingerichtet ist. Wenn eine Verknüpfung vorhanden ist, sendet der Server dem Client eine positive WM_DDE_ACK Nachricht; der Server muss dann keine Aktualisierungen mehr in der Unterhaltung senden. Wenn keine Verknüpfung vorhanden ist, sendet der Server dem Client eine negative WM_DDE_ACK Nachricht.

Ausführen von Befehlen in einer Serveranwendung

Anwendungen können die WM_DDE_EXECUTE Nachricht verwenden, um zu bewirken, dass ein bestimmter Befehl oder eine Reihe von Befehlen in einer anderen Anwendung ausgeführt wird. Dazu sendet der Client dem Server eine Meldung WM_DDE_EXECUTE, die ein Handle enthält, an eine Befehlszeichenfolge, wie im folgenden Beispiel gezeigt.

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

In diesem Beispiel versucht der Server, die angegebene Befehlszeichenfolge auszuführen. Wenn er erfolgreich ist, sendet der Server dem Client eine positive WM_DDE_ACK Nachricht; andernfalls sendet sie eine negative WM_DDE_ACK Nachricht. Diese WM_DDE_ACK Nachricht verwendet das hCommand-Handle , das in der ursprünglichen WM_DDE_EXECUTE Nachricht übergeben wird.

Wenn die Befehlsausführungszeichenfolge des Clients anfordert, dass der Server beendet wird, sollte der Server reagieren, indem er eine positive WM_DDE_ACK Nachricht sendet und dann eine WM_DDE_TERMINATE Nachricht vor dem Beenden sendet. Alle anderen Befehle, die mit einer WM_DDE_EXECUTE Nachricht gesendet werden, sollten synchron ausgeführt werden; d. h., der Server sollte eine WM_DDE_ACK Nachricht erst senden, nachdem der Befehl erfolgreich abgeschlossen wurde.

Beenden einer Unterhaltung

Entweder der Client oder der Server kann eine WM_DDE_TERMINATE Nachricht ausgeben, um eine Unterhaltung jederzeit zu beenden. Ebenso sollten sowohl die Client- als auch die Serveranwendungen darauf vorbereitet sein, diese Nachricht jederzeit zu empfangen. Eine Anwendung muss alle ihre Kommunikationen beenden, bevor sie heruntergefahren wird.

Im folgenden Beispiel sendet die Anwendung, die die Unterhaltung beendet, eine WM_DDE_TERMINATE Nachricht.

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

Dadurch wird die andere Anwendung darüber informiert, dass die sendende Anwendung keine weiteren Nachrichten sendet und der Empfänger sein Fenster schließen kann. Der Empfänger wird in allen Fällen erwartet, dass er umgehend antwortet, indem er eine WM_DDE_TERMINATE Nachricht sendet. Der Empfänger darf keine "negativ", "beschäftigt" oder "positiv" WM_DDE_ACK-Nachricht senden.

Nachdem eine Anwendung die WM_DDE_TERMINATE-Nachricht an den Partner in einer DDE-Kommunikation übermittelt hat, darf sie nicht auf Nachrichten dieses Partners antworten, da der Partner möglicherweise das Fenster geschlossen hat, an das die Antwort gesendet würde.

Wenn eine Anwendung eine andere DDE-Nachricht als WM_DDE_TERMINATE empfängt, nachdem sie WM_DDE_TERMINATE gepostet hat, sollte sie alle Objekte freigeben, die den empfangenen Nachrichten zugeordnet sind, mit Ausnahme der Datenhandles für WM_DDE_DATA oder WM_DDE_POKE Nachrichten, die nicht über den FRelease-Membersatz verfügen.

Wenn eine Anwendung kurz vor dem Beenden steht, sollte sie alle aktiven DDE-Konversationen beenden, bevor die Verarbeitung der Meldung WM_DESTROY abgeschlossen wird. Wenn eine Anwendung die aktiven DDE-Unterhaltungen jedoch nicht beendet, beendet das System alle DDE-Unterhaltungen, die einem Fenster zugeordnet sind, wenn das Fenster zerstört wird. Das folgende Beispiel zeigt, wie eine Serveranwendung alle DDE-Gespräche beendet.

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