Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Deze sectie bevat codevoorbeelden voor de volgende taken:
- Een gesprek starten
- Eén item overdragen
- Een permanente gegevenskoppeling tot stand brengen
- Opdrachten uitvoeren in een servertoepassing
- Een gesprek beëindigen
Een gesprek starten
Om een DDE-gesprek (Dynamic Data Exchange) te starten, verzendt de client een WM_DDE_INITIATE bericht. Normaal gesproken verzendt de client dit bericht door SendMessage aan te roepen, met –1 als de eerste parameter. Als de toepassing al de window handle heeft naar de serverapplicatie, kan het bericht rechtstreeks naar dat venster worden verzonden. De cliënt bereidt atomen voor op de toepassingsnaam en onderwerpnaam door GlobalAddAtom aan te roepen. De client kan gesprekken aanvragen met elke mogelijke servertoepassing en voor elk mogelijk onderwerp door NULL-atomen (jokertekens) op te leveren voor de toepassing en het onderwerp.
In het volgende voorbeeld ziet u hoe de client een gesprek start, waarbij zowel de toepassing als het onderwerp zijn opgegeven.
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);
Opmerking
Als uw toepassing NULL-atomen gebruikt, moet u de functies GlobalAddAtom en GlobalDeleteAtom niet gebruiken. In dit voorbeeld maakt de clienttoepassing twee globale atomen die respectievelijk de naam van de server en de naam van het onderwerp bevatten.
De clienttoepassing verzendt een WM_DDE_INITIATE bericht met deze twee atomen in de parameter lParam van het bericht. In de aanroep van de functie SendMessage stuurt de speciale venstergreep –1 het systeem om dit bericht te verzenden naar alle andere actieve toepassingen. SendMessage keert pas terug naar de clienttoepassing wanneer alle toepassingen die het bericht ontvangen, op hun beurt de controle aan het systeem hebben teruggegeven. Dit betekent dat alle WM_DDE_ACK berichten die worden verzonden in antwoord door de servertoepassingen gegarandeerd door de client worden verwerkt wanneer de SendMessage-aanroep is geretourneerd.
Nadat SendMessage terugkeert, verwijdert de clienttoepassing de globale gegevens.
Servertoepassingen reageren op basis van de logica die wordt geïllustreerd in het volgende diagram.
Als u een of meer onderwerpen wilt bevestigen, moet de server atomen maken voor elk gesprek (waarbij dubbele toepassingsnaamatomen zijn vereist als er meerdere onderwerpen zijn) en een WM_DDE_ACK bericht verzenden voor elk gesprek, zoals wordt geïllustreerd in het volgende voorbeeld.
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.
}
Wanneer een server reageert met een WM_DDE_ACK bericht, moet de clienttoepassing een ingang opslaan in het servervenster. De client die de ingang ontvangt als de parameter wParam van het WM_DDE_ACK-bericht , verzendt vervolgens alle volgende DDE-berichten naar het servervenster dat deze ingang identificeert.
Als uw clienttoepassing een NULL-atom gebruikt voor de naam van de toepassing of onderwerpnaam, verwacht u dat de toepassing bevestigingen ontvangt van meer dan één servertoepassing. Meerdere bevestigingen kunnen ook afkomstig zijn van meerdere exemplaren van een DDE-server, zelfs als uw clienttoepassing geen NULL-atomen gebruikt. Een server moet altijd een uniek venster gebruiken voor elk gesprek. De vensterprocedure in de clienttoepassing kan een ingang gebruiken voor het servervenster (opgegeven als de parameter lParam van WM_DDE_INITIATE) om meerdere gesprekken bij te houden. Hierdoor kan één clientvenster verschillende gesprekken verwerken zonder dat er voor elk gesprek een nieuw clientvenster hoeft te worden beëindigd en opnieuw verbinding hoeft te worden gemaakt.
Eén item overdragen
Zodra een DDE-gesprek tot stand is gebracht, kan de client de waarde van een gegevensitem van de server ophalen door het WM_DDE_REQUEST bericht uit te geven of een gegevensitemwaarde naar de server te verzenden door WM_DDE_POKE uit te geven.
Een item ophalen van de server
Als u een item van de server wilt ophalen, verzendt de client de server een WM_DDE_REQUEST bericht waarin het item en de indeling worden opgegeven die moet worden opgehaald, zoals wordt weergegeven in het volgende voorbeeld.
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 dit voorbeeld specificeert de client de klembordindeling CF_TEXT als voorkeursindeling voor het aangevraagde gegevensitem.
De ontvanger (server) van het WM_DDE_REQUEST bericht moet meestal het item atom verwijderen, maar als de PostMessage-aanroep mislukt, moet de client het atoom verwijderen.
Als de server toegang heeft tot het aangevraagde item en het kan weergeven in de aangevraagde indeling, kopieert de server de itemwaarde als een gedeeld geheugenobject en verzendt de client een WM_DDE_DATA bericht, zoals wordt geïllustreerd in het volgende voorbeeld.
// 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 dit voorbeeld wijst de servertoepassing een geheugenobject toe dat het gegevensitem bevat. Het gegevensobject wordt geïnitialiseerd als een DDEDATA-structuur .
De servertoepassing stelt vervolgens het cfFormat-lid van de structuur in op CF_TEXT om de clienttoepassing te informeren dat de gegevens een tekstindeling hebben. De client reageert door de waarde van de aangevraagde gegevens te kopiëren naar het waardelid van de DDEDATA-structuur . Nadat de server het gegevensobject heeft ingevuld, ontgrendelt de server de gegevens en maakt een globaal atoom met de naam van het gegevensitem.
Ten slotte geeft de server het WM_DDE_DATA bericht door PostMessage aan te roepen. De verwijzing naar het gegevensobject en het atoom met de itemnaam worden door de functie PackDDElParam ingepakt in de lParam-parameter van het bericht.
Als PostMessage mislukt, moet de server de functie FreeDDElParam gebruiken om de ingepakte lParam-parameter vrij te maken. De server moet ook de verpakte lParam-parameter vrij maken voor het WM_DDE_REQUEST bericht dat deze heeft ontvangen.
Als de server niet aan de aanvraag kan voldoen, wordt er een negatief WM_DDE_ACK bericht naar de client verzonden, zoals wordt weergegeven in het volgende voorbeeld.
// Negative acknowledgment.
PostMessage(hwndClientDDE,
WM_DDE_ACK,
(WPARAM) hwndServerDDE,
PackDDElParam(WM_DDE_ACK, 0, atomItem));
Na ontvangst van een WM_DDE_DATA bericht verwerkt de client de waarde van het gegevensitem, indien van toepassing. Dan, als de fAckReq onderdeel geadresseerd in het WM_DDE_DATA bericht 1 is, moet de client de server een positief WM_DDE_ACK bericht moet verzenden, zoals in het volgende voorbeeld wordt getoond.
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 dit voorbeeld onderzoekt de client de indeling van de gegevens. Als de indeling niet is CF_TEXT (of als de client het geheugen voor de gegevens niet kan vergrendelen), verzendt de client een negatief WM_DDE_ACK bericht om aan te geven dat de gegevens niet kunnen worden verwerkt. Als de client een datahandle niet kan vergrendelen omdat de handle het fAckReq-lid bevat, mag de client geen negatief WM_DDE_ACK-bericht verzenden. In plaats daarvan moet de client het gesprek beëindigen.
Als een client een negatieve bevestiging verzendt in reactie op een WM_DDE_DATA bericht, is de server verantwoordelijk voor het vrijmaken van het geheugen (maar niet de parameter lParam ) waarnaar wordt verwezen door het WM_DDE_DATA bericht dat is gekoppeld aan de negatieve bevestiging.
Als de gegevens kunnen worden verwerkt, onderzoekt de client het lid fAckReq van de DDEDATA-structuur om te bepalen of de server heeft aangevraagd dat deze wordt geïnformeerd dat de client de gegevens heeft ontvangen en verwerkt. Als de server deze informatie heeft aangevraagd, stuurt de client de server een positief WM_DDE_ACK bericht.
Omdat het ontgrendelen van gegevens de aanwijzer naar de gegevens ongeldig maakt, slaat de client de waarde van het lid fRelease op voordat het gegevensobject wordt ontgrendeld. Nadat de waarde is opgeslagen, onderzoekt de client deze om te bepalen of de servertoepassing de client heeft gevraagd om het geheugen met de gegevens vrij te maken; de opdrachtgever handelt dienovereenkomstig.
Bij ontvangst van een negatief WM_DDE_ACK bericht kan de client opnieuw om dezelfde itemwaarde vragen, waarbij een andere klembordindeling wordt opgegeven. Normaal gesproken vraagt een client eerst om de meest complexe indeling die deze kan ondersteunen, en stapt deze indien nodig af met behulp van geleidelijk eenvoudigere indelingen totdat er een wordt gevonden die de server kan bieden.
Als de server het item Indelingen van het systeemonderwerp ondersteunt, kan de client bepalen welke klembordindelingen de server ondersteunt, in plaats van ze telkens te bepalen wanneer de client een item aanvraagt.
Een item verzenden naar de server
De client kan een itemwaarde naar de server verzenden met behulp van het WM_DDE_POKE bericht. De client geeft het item weer dat moet worden verzonden en verzendt het WM_DDE_POKE bericht, zoals wordt geïllustreerd in het volgende voorbeeld.
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.
}
Opmerking
Het verzenden van gegevens met behulp van een WM_DDE_POKE bericht is in wezen hetzelfde als het verzenden ervan met behulp van WM_DDE_DATA, behalve dat WM_DDE_POKE van de client naar de server wordt verzonden.
Als de server de waarde van het gegevensitem kan accepteren in de indeling die door de client wordt weergegeven, verwerkt de server de itemwaarde naar wens en verzendt de client een positief WM_DDE_ACK bericht. Als de itemwaarde vanwege de indeling of om andere redenen niet kan worden verwerkt, verzendt de server de client een negatief WM_DDE_ACK bericht.
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 dit voorbeeld roept de server GlobalGetAtomName aan om de naam op te halen van het item dat de client heeft verzonden. De server bepaalt vervolgens of het item het ondersteunt en of het item wordt weergegeven in de juiste indeling (dat wil gezegd CF_TEXT). Als het item niet wordt ondersteund en niet in de juiste indeling wordt weergegeven, of als de server het geheugen voor de gegevens niet kan vergrendelen, stuurt de server een negatieve bevestiging terug naar de clienttoepassing. In dit geval is het verzenden van een negatieve kennisgeving juist omdat WM_DDE_POKE-berichten altijd worden aangenomen fAckReq ingesteld te hebben. De server moet het lid negeren.
Als een server een negatieve bevestiging verzendt in reactie op een WM_DDE_POKE bericht, is de client verantwoordelijk voor het vrijmaken van het geheugen (maar niet de parameter lParam ) waarnaar wordt verwezen door het WM_DDE_POKE bericht dat is gekoppeld aan de negatieve bevestiging.
Een permanente gegevenskoppeling tot stand brengen
Een clienttoepassing kan DDE gebruiken om een koppeling naar een item in een servertoepassing tot stand te brengen. Nadat een dergelijke koppeling tot stand is gebracht, verzendt de server periodieke updates van het gekoppelde item naar de client, meestal wanneer de waarde van het item wordt gewijzigd. Er wordt dus een permanente gegevensstroom tot stand gebracht tussen de twee toepassingen; deze gegevensstroom blijft aanwezig totdat deze expliciet is losgekoppeld.
Een gegevenskoppeling initiëren
De client initieert een gegevenskoppeling door een WM_DDE_ADVISE bericht te plaatsen, zoals wordt weergegeven in het volgende voorbeeld.
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 dit voorbeeld stelt de clienttoepassing de vlag fDeferUpd van het WM_DDE_ADVISE bericht in op FALSE. Hiermee stuurt de servertoepassing de gegevens naar de client wanneer de gegevens worden gewijzigd.
Als de server de WM_DDE_ADVISE aanvraag niet kan verwerken, wordt de client een negatief WM_DDE_ACK bericht verzonden. Maar als de server toegang heeft tot het item en het in de aangevraagde indeling kan weergeven, noteert de server de nieuwe koppeling (de vlaggen die zijn opgegeven in de parameter hOptions ) en verzendt de client een positief WM_DDE_ACK bericht. Vanaf dat moment, totdat de client een overeenkomend WM_DDE_UNADVISE bericht uitgeeft, verzendt de server de nieuwe gegevens naar de client telkens wanneer de waarde van het item op de server verandert.
Het WM_DDE_ADVISE bericht bepaalt de indeling van de gegevens die tijdens de koppeling moeten worden uitgewisseld. Als de client probeert een andere koppeling met hetzelfde item tot stand te brengen, maar een andere gegevensindeling gebruikt, kan de server ervoor kiezen om de tweede gegevensindeling te weigeren of deze te ondersteunen. Als er een warme koppeling tot stand is gebracht voor een gegevensitem, kan de server slechts één gegevensindeling tegelijk ondersteunen. Dit komt doordat het WM_DDE_DATA bericht voor een warme koppeling een NULL-gegevensgreep heeft, die anders de indelingsgegevens bevat. Een server moet dus alle warme koppelingen voor een item dat al is gekoppeld, weigeren en alle koppelingen weigeren voor een item met warme koppelingen. Een andere interpretatie kan zijn dat de server de indeling en de dynamische of warme status van een koppeling wijzigt wanneer een tweede koppeling wordt aangevraagd voor hetzelfde gegevensitem.
In het algemeen mogen clienttoepassingen niet proberen meer dan één koppeling tegelijk tot stand te brengen voor een gegevensitem.
Een gegevenskoppeling starten met de opdracht Koppeling plakken
Toepassingen die actieve of semi-actieve gegevenskoppelingen ondersteunen, ondersteunen doorgaans een geregistreerde klembordformaat met de naam Koppeling. Wanneer deze klembordindeling is gekoppeld aan de opdrachten Kopiëren en Plakken Link van de toepassing, kan de gebruiker eenvoudigweg DDE-gesprekken tussen toepassingen tot stand brengen door een gegevensitem in de servertoepassing te kopiëren en in de clienttoepassing te plakken.
Een servertoepassing ondersteunt het Link klembordformaat door in het klembord een tekenreeks te plaatsen die de toepassing, het onderwerp en de itemnamen bevat wanneer de gebruiker de opdracht Kopiëren kiest in het menu Bewerken. Hieronder ziet u het standaard linkformaat:
application**\0topic\0item\0\0**
Eén null-teken scheidt de namen en twee null-tekens beëindigen de hele tekenreeks.
Zowel de client- als servertoepassingen moeten de koppelings klembordindeling registreren, zoals wordt weergegeven:
cfLink = RegisterClipboardFormat("Link");
Een clienttoepassing ondersteunt de koppelingsklembordindeling door middel van de opdracht 'Koppeling plakken' in het menu Bewerken. Wanneer de gebruiker deze opdracht kiest, leest de clienttoepassing de toepassing, het onderwerp en de itemnamen uit de klembordgegevens in het linkformaat. Met behulp van deze namen start de clienttoepassing een gesprek voor de toepassing en het onderwerp, als een dergelijk gesprek nog niet bestaat. De clienttoepassing verzendt vervolgens een WM_DDE_ADVISE bericht naar de servertoepassing en geeft de itemnaam op die is opgenomen in de klembordgegevens van de koppelingsindeling.
Hieronder volgt een voorbeeld van een cliëntapplicatie die reageert wanneer de gebruiker de opdracht Koppeling plakken kiest.
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 dit voorbeeld opent de clienttoepassing het klembord en bepaalt deze of het gegevens in de Koppeling-indeling (cfLink) bevat die het eerder heeft geregistreerd. Zo niet, of als de gegevens niet op het klembord kunnen worden vergrendeld, keert de cliënt terug.
Nadat de clienttoepassing een aanwijzer naar de klembordgegevens heeft opgehaald, parseert het de gegevens om de namen van de toepassing, het onderwerp en de namen van de items te extraheren.
De clientapplicatie bepaalt of er al een gesprek over het onderwerp bestaat tussen haar en de serverapplicatie. Als er wel een gesprek bestaat, controleert de client of er al een koppeling bestaat voor het gegevensitem. Als er een dergelijke koppeling bestaat, geeft de client een berichtvak weer aan de gebruiker; anders wordt een eigen SendAdvise-functie aangeroepen om een WM_DDE_ADVISE bericht naar de server voor het item te verzenden.
Als er nog geen gesprek over het onderwerp bestaat tussen de client en de server, roept de client eerst een eigen SendInitiate-functie aan om het WM_DDE_INITIATE bericht uit te zenden om een gesprek aan te vragen en roept ten tweede zijn eigen Functie FindServerGivenAppTopic aan om het gesprek tot stand te brengen met het venster dat namens de servertoepassing reageert. Nadat het gesprek is gestart, roept de clienttoepassing SendAdvise aan om de koppeling aan te vragen.
De client informeren dat de gegevens zijn gewijzigd
Wanneer de client een koppeling tot stand brengt met behulp van het WM_DDE_ADVISE bericht, waarbij het lid fDeferUpd niet is ingesteld (gelijk aan nul) in de DDEDATA-structuur , heeft de client de server gevraagd het gegevensitem te verzenden telkens wanneer de waarde van het item wordt gewijzigd. In dergelijke gevallen geeft de server de nieuwe waarde van het gegevensitem weer in de eerder opgegeven indeling en verzendt de client een WM_DDE_DATA bericht, zoals wordt weergegeven in het volgende voorbeeld.
// 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 dit voorbeeld verwerkt de client de itemwaarde naar wens. Als de vlag fAckReq voor het item is ingesteld, verzendt de client de server een positief WM_DDE_ACK bericht.
Wanneer de client de koppeling tot stand brengt, met de ledenset fDeferUpd (dat wil gezegd, gelijk aan 1), heeft de client gevraagd om alleen een melding, niet de gegevens zelf, te verzenden telkens wanneer de gegevens worden gewijzigd. Wanneer de itemwaarde wordt gewijzigd, geeft de server in dergelijke gevallen niet de waarde weer, maar verzendt de client gewoon een WM_DDE_DATA bericht met een null-gegevensgreep, zoals wordt geïllustreerd in het volgende voorbeeld.
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.
}
Indien nodig kan de client de meest recente waarde van het gegevensitem aanvragen door een normaal WM_DDE_REQUEST bericht uit te geven of kan deze de kennisgeving van de server negeren dat de gegevens zijn gewijzigd. In beide gevallen, als fAckReq gelijk is aan 1, wordt verwacht dat de client een positief WM_DDE_ACK bericht naar de server verzendt.
Een gegevenskoppeling beëindigen
Als de client aanvraagt dat een specifieke gegevenskoppeling wordt beëindigd, verzendt de client de server een WM_DDE_UNADVISE bericht, zoals wordt weergegeven in het volgende voorbeeld.
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.
}
De server controleert of de client momenteel een koppeling heeft naar het specifieke item in dit gesprek. Als er een koppeling bestaat, stuurt de server de client een positief WM_DDE_ACK bericht; de server is dan niet meer vereist om updates over het item te verzenden. Als er geen koppeling bestaat, verzendt de server de client een negatief WM_DDE_ACK bericht.
Het WM_DDE_UNADVISE bericht geeft een gegevensindeling op. Een formaat van nul informeert de server om alle koppelingen voor het gespecificeerde item te beëindigen, zelfs als er verschillende dynamische koppelingen tot stand zijn gebracht en elk een ander formaat gebruikt.
Als u alle koppelingen voor een gesprek wilt beëindigen, verzendt de clienttoepassing de server een WM_DDE_UNADVISE bericht met een null-item atom. De server bepaalt of het gesprek momenteel ten minste één koppeling tot stand heeft gebracht. Als er een koppeling bestaat, stuurt de server de client een positief WM_DDE_ACK bericht; de server hoeft dan geen updates meer te verzenden in het gesprek. Als er geen koppeling bestaat, verzendt de server de client een negatief WM_DDE_ACK bericht.
Opdrachten uitvoeren in een servertoepassing
Toepassingen kunnen het WM_DDE_EXECUTE bericht gebruiken om ervoor te zorgen dat een bepaalde opdracht of reeks opdrachten in een andere toepassing wordt uitgevoerd. Hiervoor verzendt de client de server een WM_DDE_EXECUTE bericht met een ingang naar een opdrachtreeks, zoals wordt weergegeven in het volgende voorbeeld.
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 dit voorbeeld probeert de server de opgegeven opdrachtreeks uit te voeren. Als dit lukt, stuurt de server de client een positief WM_DDE_ACK bericht; anders wordt er een negatief WM_DDE_ACK bericht verzonden. Met dit WM_DDE_ACK bericht wordt de hCommand-ingang opnieuw gebruikt die is doorgegeven in het oorspronkelijke WM_DDE_EXECUTE bericht.
Als de opdrachtuitvoeringsreeks van de client vraagt dat de server wordt beëindigd, moet de server reageren door een positief WM_DDE_ACK bericht te verzenden en vervolgens een WM_DDE_TERMINATE bericht te posten voordat de server wordt beëindigd. Alle andere opdrachten die met een WM_DDE_EXECUTE bericht worden verzonden, moeten synchroon worden uitgevoerd; Dat wil gezegd, de server moet pas een WM_DDE_ACK bericht verzenden nadat de opdracht is voltooid.
Een gesprek beëindigen
De client of de server kan op elk gewenst moment een WM_DDE_TERMINATE bericht uitgeven om een gesprek te beëindigen. Op dezelfde manier moeten zowel de client- als servertoepassingen op elk gewenst moment worden voorbereid om dit bericht te ontvangen. Een toepassing moet alle gesprekken beëindigen voordat deze worden afgesloten.
In het volgende voorbeeld plaatst de toepassing die het gesprek beëindigt een WM_DDE_TERMINATE bericht.
PostMessage(hwndServerDDE, WM_DDE_TERMINATE,
(WPARAM) hwndClientDDE, 0);
Hiermee wordt de andere toepassing geïnformeerd dat de verzendende toepassing geen verdere berichten verzendt en de ontvanger het venster kan sluiten. De geadresseerde wordt in alle gevallen verwacht om onmiddellijk te reageren door een WM_DDE_TERMINATE bericht te verzenden. De ontvanger mag geen negatief, bezet of positief WM_DDE_ACK bericht verzenden.
Nadat een toepassing het WM_DDE_TERMINATE bericht heeft verzonden naar de partner in een DDE-gesprek, mag deze niet reageren op berichten van die partner, omdat de partner mogelijk het venster heeft vernietigd waarnaar het antwoord zou worden verzonden.
Als een toepassing een ander DDE-bericht dan WM_DDE_TERMINATE ontvangt nadat deze WM_DDE_TERMINATE heeft gepost, moeten alle objecten die aan de ontvangen berichten zijn gekoppeld, vrij zijn, met uitzondering van de gegevensgrepen voor WM_DDE_DATA of WM_DDE_POKE berichten waarvoor de fRelease-lidsetniet is ingesteld.
Wanneer een toepassing op het punt staat te worden beëindigd, moet deze alle actieve DDE-gesprekken beëindigen voordat de verwerking van het WM_DESTROY bericht wordt voltooid. Als een toepassing echter de actieve DDE-gesprekken niet beëindigt, beëindigt het systeem alle DDE-gesprekken die zijn gekoppeld aan een venster wanneer het venster wordt vernietigd. In het volgende voorbeeld ziet u hoe een servertoepassing alle DDE-gesprekken beëindigt.
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;
}