Share via


Verwenden von dynamischem Datenaustausch

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

Initiieren einer Unterhaltung

Um eine DDE-Konversation (Dynamic Data Exchange) zu initiieren, sendet der Client eine WM_DDE_INITIATE Nachricht. In der Regel überträgt 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 vor, indem Er GlobalAddAtom aufruft. Der Client kann Unterhaltungen mit jeder potenziellen Serveranwendung und für jedes potenzielle Thema anfordern, indem er NULL-Atome (Wildcard) für die Anwendung und das Thema angibt.

Im folgenden Beispiel wird veranschaulicht, wie der Client eine Konversation initiiert, in der sowohl die Anwendung als auch das Thema angegeben werden.

    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. Beim Aufruf der SendMessage-Funktion weist 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 Nachricht empfangen, wiederum die Steuerung an das System zurückgegeben haben. Dies bedeutet, dass alle WM_DDE_ACK Nachrichten, die von den Serveranwendungen als Antwort gesendet werden, garantiert vom Client verarbeitet wurden, wenn der SendMessage-Aufruf zurückgegeben wurde.

Nach der Rückgabe von SendMessage löscht die Clientanwendung die globalen Atome.

Serveranwendungen reagieren gemäß der im folgenden Diagramm dargestellten Logik.

Antwortlogik für Serveranwendungen

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

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 mehreren Serveranwendungen empfängt. Mehrere Bestätigungen können auch von mehreren Instanzen eines DDE-Servers stammen, auch wenn Ihre Clientanwendung keine Atome verwendet. Ein Server sollte für jede Unterhaltung immer ein eindeutiges Fenster 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 dass für jede Unterhaltung ein neues Clientfenster beendet und wieder hergestellt werden muss.

Übertragen eines einzelnen Elements

Nachdem eine DDE-Konversation 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 das 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 Elementatom 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 Meldung, wie im folgenden Beispiel veranschaulicht.

// 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 diesem Beispiel weist die Serveranwendung ein Speicherobjekt zu, das das Datenelement enthält. Das Datenobjekt wird als DDEDATA-Struktur initialisiert.

Die Serveranwendung legt dann den cfFormat-Member der Struktur auf CF_TEXT fest, um die Clientanwendung darüber zu informieren, dass die Daten im Textformat vorliegen. Der Client antwortet, indem er den Wert der angeforderten Daten in den Value-Member der DDEDATA-Struktur kopiert. Nachdem der Server das Datenobjekt gefüllt hat, entsperrt der Server die Daten und erstellt ein globales Atom, das den Namen des Datenelements enthält.

Schließlich gibt der Server die WM_DDE_DATA-Nachricht aus, indem Er PostMessage aufruft. Das Handle für das Datenobjekt und das Atom, das den Elementnamen enthält, werden von der PackDDElParam-Funktion in den lParam-Parameter der Nachricht gepackt.

Wenn PostMessage fehlschlägt, muss der Server die FreeDDElParam-Funktion verwenden, um den gepackten lParam-Parameter frei zu geben. 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 nach Bedarf. Wenn dann das fAckReq-Element , auf das in der WM_DDE_DATA Nachricht verweist, 1 ist, muss der Client dem Server eine positive WM_DDE_ACK Nachricht senden, wie im folgenden Beispiel gezeigt.

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 diesem Beispiel untersucht der Client das Format der Daten. Wenn das Format nicht CF_TEXT ist (oder wenn der Client den Arbeitsspeicher 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, weil das Handle den fAckReq-Member enthält, sollte der Client keine negative WM_DDE_ACK Nachricht senden. Stattdessen sollte der Client die Konversation beenden.

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

Wenn die Daten verarbeitet werden können, untersucht der Client den fAckReq-Member der DDEDATA-Struktur , um zu ermitteln, ob der Server angefordert hat, dass er darüber 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 untersucht der Client ihn, um zu ermitteln, ob die Serveranwendung den Client aufgefordert hat, den Arbeitsspeicher freizugeben, der die Daten enthält. Der Kunde handelt entsprechend.

Wenn eine negative WM_DDE_ACK Nachricht empfangen wird, kann der Client denselben Elementwert erneut anfordern und ein anderes Zwischenablageformat angeben. In der Regel fragt ein Client zuerst nach dem komplexesten Format, das er unterstützen kann, und tritt dann bei Bedarf durch schrittweise einfachere Formate zurück, bis er eines findet, das der Server bereitstellen kann.

Wenn der Server das Element Formats 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.

Übermitteln eines Elements an den Server

Der Client kann mithilfe der WM_DDE_POKE Nachricht einen Elementwert an den Server senden. Der Client rendert das zu sendende Element und sendet die WM_DDE_POKE-Nachricht , wie im folgenden Beispiel veranschaulicht.

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

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 in dem 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) &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 diesem Beispiel ruft der Server GlobalGetAtomName auf, um den Namen des vom Client gesendeten Elements abzurufen. Der Server bestimmt dann, ob er 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 Arbeitsspeicher für die Daten nicht sperren kann, sendet der Server eine negative Bestätigung zurück 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-Element festgelegt ist. Der Server sollte den Member ignorieren.

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

Eine Clientanwendung kann DDE verwenden, um einen Link zu 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 permanenter Datenstrom zwischen den beiden Anwendungen eingerichtet; Dieser Datenstrom bleibt an Ort und Stelle, bis er explizit getrennt wird.

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) 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 anweisen, die Daten an den Client zu senden, wenn sich die Daten ändern.

Wenn der Server die WM_DDE_ADVISE-Anforderung nicht verarbeiten 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 sich der Server den neuen Link (die im hOptions-Parameter angegebenen Flags ) und sendet dem Client eine positive WM_DDE_ACK Nachricht. Bis der Client von nun an eine übereinstimmende WM_DDE_UNADVISE-Nachricht ausgibt, sendet der Server die neuen Daten jedes Mal an den Client, wenn sich der Wert des Elements auf dem Server ändert.

Die WM_DDE_ADVISE Nachricht legt das Format der Daten fest, 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, es zu unterstützen. Wenn für ein Beliebiges Datenelement ein warmer Link eingerichtet wurde, kann der Server jeweils nur ein Datenformat unterstützen. Dies liegt daran, dass die WM_DDE_DATA Nachricht für einen warmen Link über ein NULL-Datenhandle verfügt, das andernfalls 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, mehr als einen Link 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 es den Befehlen "Link kopieren" und "Link einfügen" der Anwendung zugeordnet ist, ermöglicht dieses Zwischenablageformat es dem Benutzer, DDE-Unterhaltungen zwischen Anwendungen einzurichten, indem einfach ein Datenelement in der Serveranwendung kopiert und in die Clientanwendung eingefügt wird.

Eine Serveranwendung unterstützt das Link-Zwischenablageformat, indem sie eine Zeichenfolge mit den Anwendungs-, Themen- und Elementnamen in die Zwischenablage legt, wenn der Benutzer den Befehl Kopieren im Menü Bearbeiten auswäht. Es folgt das Standardformat Link:

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 registrieren, wie gezeigt:

cfLink = RegisterClipboardFormat("Link");

Eine Clientanwendung unterstützt das Link-Zwischenablageformat mithilfe eines Befehls Link einfügen im Menü Bearbeiten. Wenn der Benutzer diesen Befehl wählt, analysiert die Clientanwendung die Anwendungs-, Themen- und Elementnamen aus den Zwischenablagedaten im Linkformat. Mithilfe dieser Namen initiiert die Clientanwendung eine Konversation für die Anwendung und das Thema, sofern eine solche Unterhaltung noch nicht vorhanden ist. Die Clientanwendung sendet dann eine WM_DDE_ADVISE Nachricht an die Serveranwendung und gibt den Elementnamen an, der in den Linkformat-Zwischenablagedaten enthalten ist.

Im Folgenden finden Sie 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 Zuvor registrierten Link-Format (d. h. cfLink) enthält. Andernfalls oder wenn die Daten nicht in der Zwischenablage gesperrt werden können, gibt der Client zurück.

Nachdem die Clientanwendung einen Zeiger auf die Zwischenablagedaten abgerufen hat, analysiert sie die Daten, um die Anwendungs-, Themen- und Elementnamen zu extrahieren.

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

Wenn zwischen dem Client und dem Server noch keine Unterhaltung zum Thema vorhanden ist, ruft der Client zunächst seine eigene SendInitiate-Funktion auf, um die WM_DDE_INITIATE Nachricht zum Anfordern einer Unterhaltung zu senden, und ruft zweitens seine eigene FindServerGivenAppTopic-Funktion auf, um die Konversation mit dem Fenster einzurichten, das im Namen der Serveranwendung antwortet. Nachdem die Unterhaltung begonnen hat, ruft die Clientanwendung SendAdvise auf, um den Link anzufordern.

Benachrichtigung des Clients, dass sich Daten geändert haben

Wenn der Client mithilfe der WM_DDE_ADVISE-Nachricht einen Link herstellt und der fDeferUpd-Member in der DDEDATA-Struktur nicht festgelegt ist (d. h. gleich Null), hat der Client den Server aufgefordert, 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) hData, atomItem))) 
    { 
        GlobalFree(hData); 
        GlobalDeleteAtom(atomItem); 
        FreeDDElParam(WM_DDE_DATA, lParam); 
    } 
} 
 
if (atomItem == 0) 
{ 
    // Handle errors. 
 
}

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

Wenn der Client den Link mit dem fDeferUpd-Membersatz (d. h. gleich 1) herstellt, hat der Client angefordert, dass bei jeder Datenänderung 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 veranschaulicht.

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 die Benachrichtigung vom Server ignorieren, dass sich die Daten geändert haben. Wenn fAckReq gleich 1 ist, wird erwartet, dass der Client eine positive WM_DDE_ACK Nachricht an den Server sendet.

Wenn der Client anfordert, eine bestimmte Datenverbindung zu beenden, 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 ein Link vorhanden ist, sendet der Server dem Client eine positive WM_DDE_ACK Nachricht. Der Server ist dann nicht mehr erforderlich, um Updates zum Element zu senden. Wenn kein Link 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 Links für das angegebene Element zu beenden, auch wenn mehrere Hotlinks eingerichtet sind und jeder ein anderes Format verwendet.

Um alle Links für eine Unterhaltung zu beenden, sendet die Clientanwendung dem Server eine WM_DDE_UNADVISE Nachricht mit einem NULL-Elementatom. Der Server bestimmt, ob für die Unterhaltung derzeit mindestens ein Link eingerichtet ist. Wenn ein Link vorhanden ist, sendet der Server dem Client eine positive WM_DDE_ACK Nachricht. Der Server muss dann keine Updates mehr in der Unterhaltung senden. Wenn kein Link 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 einen bestimmten Befehl oder eine Reihe von Befehlen in einer anderen Anwendung auszuführen. Hierzu sendet der Client dem Server eine WM_DDE_EXECUTE Nachricht mit einem Handle 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) hCommand))) 
{ 
    GlobalFree(hCommand); 
    FreeDDElParam(WM_DDE_EXECUTE, lParam); 
}

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

Wenn die Befehlsausführungszeichenfolge des Clients anfordert, den Server zu beenden, sollte der Server antworten, indem er eine positive WM_DDE_ACK Nachricht sendet und dann eine WM_DDE_TERMINATE Nachricht vor dem Beenden postet. Alle anderen Befehle, die mit einer WM_DDE_EXECUTE Nachricht gesendet werden, sollten synchron ausgeführt werden. Das heißt, der Server sollte erst nach erfolgreichem Abschluss des Befehls eine WM_DDE_ACK Nachricht senden.

Beenden einer Unterhaltung

Der Client oder der Server kann jederzeit eine WM_DDE_TERMINATE-Nachricht ausgeben, um eine Unterhaltung zu beenden. Ebenso sollten sowohl die Client- als auch die Serveranwendungen darauf vorbereitet sein, diese Nachricht jederzeit zu empfangen. Eine Anwendung muss alle Unterhaltungen 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. Es wird erwartet, dass der Empfänger in allen Fällen umgehend antwortet, indem er eine WM_DDE_TERMINATE Nachricht sendet. Der Empfänger darf keine negative, gebuchte oder positive WM_DDE_ACK Nachricht senden.

Nachdem eine Anwendung die WM_DDE_TERMINATE Nachricht an den Partner in einer DDE-Unterhaltung gesendet hat, darf sie nicht auf Nachrichten von diesem Partner reagieren, da der Partner möglicherweise das Fenster zerstört hat, an das die Antwort gesendet wird.

Wenn eine Anwendung eine andere DDE-Nachricht als WM_DDE_TERMINATE empfängt, nachdem sie WM_DDE_TERMINATE veröffentlicht 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, für die das fRelease-Elementnicht festgelegt ist.

Wenn eine Anwendung beendet wird, sollten alle aktiven DDE-Unterhaltungen beendet werden, bevor die Verarbeitung der WM_DESTROY Nachricht abgeschlossen wird. Wenn eine Anwendung ihre 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-Unterhaltungen 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; 
}