Använda "Dynamic Data Exchange"
Det här avsnittet innehåller kodexempel på följande uppgifter:
- Påbörja en konversation
- överföra ett enskilt objekt
- upprätta en permanent datalänk
- Genomförande av kommandon i en serverapplikation
- Avslut av en konversation
Starta en konversation
Om du vill initiera en DDE-konversation (Dynamic Data Exchange) skickar klienten ett WM_DDE_INITIATE meddelande. Vanligtvis sänder klienten det här meddelandet genom att anropa SendMessage, med -1 som den första parametern. Om programmet redan har fönsterhandtaget till serverprogrammet kan det skicka meddelandet direkt till det fönstret. Klienten förbereder atomer för programnamnet och ämnesnamnet genom att anropa GlobalAddAtom. Klienten kan begära konversationer med alla potentiella serverprogram och för alla potentiella ämnen genom att ange NULL- atomer (jokertecken) för programmet och ämnet.
I följande exempel visas hur klienten initierar en konversation, där både programmet och ämnet anges.
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);
Notera
Om ditt program använder NULL- atomer behöver du inte använda funktionerna GlobalAddAtom och GlobalDeleteAtom. I det här exemplet skapar klientprogrammet två globala atomer som innehåller namnet på servern respektive namnet på ämnet.
Klientprogrammet skickar ett WM_DDE_INITIATE meddelande med dessa två atomer i parametern lParam i meddelandet. I anropet till funktionen SendMessage dirigerar det särskilda fönsterhandtaget –1 systemet att skicka det här meddelandet till alla andra aktiva program. SendMessage- inte återgår till klientprogrammet förrän alla program som tar emot meddelandet i sin tur har returnerat kontrollen till systemet. Det innebär att alla WM_DDE_ACK meddelanden som skickas som svar av serverprogrammen garanterat har bearbetats av klienten när SendMessage--anropet har returnerats.
När SendMessage returnerar tar klientprogrammet bort de globala atomerna.
Serverprogram svarar enligt den logik som visas i följande diagram.
För att bekräfta ett eller flera ämnen måste servern skapa atomer för varje konversation (kräver duplicerade programnamnsatomer om det finns flera ämnen) och skicka ett WM_DDE_ACK meddelande för varje konversation, vilket visas i följande exempel.
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.
}
När en server svarar med ett WM_DDE_ACK meddelande bör klientprogrammet spara ett handtag till serverfönstret. Klienten som får handtaget som wParam-parametern för WM_DDE_ACK-meddelandet skickar sedan alla efterföljande DDE-meddelanden till serverfönstret som identifieras av handtaget.
Om klientprogrammet använder en NULL- atom för programnamnet eller ämnesnamnet förväntar du dig att programmet tar emot bekräftelser från mer än ett serverprogram. Flera bekräftelser kan också komma från flera instanser av en DDE-server, även om ditt klientprogram inte NULL använder atomer. En server bör alltid använda ett unikt fönster för varje konversation. Fönsterproceduren i klientprogrammet kan använda ett handtag till serverfönstret (tillhandahålls som lParam parametern WM_DDE_INITIATE) för att spåra flera konversationer. Detta gör att ett enda klientfönster kan bearbeta flera konversationer utan att behöva avsluta och återansluta med ett nytt klientfönster för varje konversation.
Överföra ett enskilt objekt
När en DDE-konversation har upprättats kan klienten antingen hämta värdet för ett dataobjekt från servern genom att utfärda WM_DDE_REQUEST-meddelandet eller skicka ett dataobjektvärde till servern genom att utfärda WM_DDE_POKE.
Hämtar ett objekt från servern
Om du vill hämta ett objekt från servern skickar klienten ett WM_DDE_REQUEST meddelande som anger objektet och formatet som ska hämtas, enligt följande exempel.
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.
}
I det här exemplet anger klienten Formatet för Urklipp CF_TEXT som önskat format för det begärda dataobjektet.
Mottagaren (servern) för WM_DDE_REQUEST-meddelandet måste vanligtvis ta bort objektatomen, men om PostMessage--anropet misslyckas måste klienten ta bort atomen.
Om servern har åtkomst till det begärda objektet och kan återge det i det begärda formatet kopierar servern objektvärdet som ett delat minnesobjekt och skickar klienten ett WM_DDE_DATA meddelande, enligt följande exempel.
// 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.
}
I det här exemplet allokerar serverprogrammet ett minnesobjekt som ska innehålla dataobjektet. Dataobjektet initieras som en DDEDATA- struktur.
Serverprogrammet anger sedan cfFormat medlem i strukturen till CF_TEXT för att informera klientprogrammet om att data är i textformat. Klienten svarar genom att kopiera värdet för de begärda data till Value medlem i DDEDATA--strukturen. När servern har fyllt dataobjektet låser servern upp data och skapar en global atom som innehåller namnet på dataobjektet.
Slutligen utfärdar servern WM_DDE_DATA-meddelandet genom att anropa PostMessage. Referensen till dataobjektet och atomen som innehåller objektnamnet packas i parametern lParam i meddelandet av funktionen PackDDElParam.
Om PostMessage misslyckas måste servern använda funktionen FreeDDElParam för att frigöra den packade parametern lParam. Servern måste också frigöra den packade parametern lParam för det WM_DDE_REQUEST meddelande som den tog emot.
Om servern inte kan uppfylla begäran skickar den ett negativt WM_DDE_ACK meddelande till klienten, enligt följande exempel.
// Negative acknowledgment.
PostMessage(hwndClientDDE,
WM_DDE_ACK,
(WPARAM) hwndServerDDE,
PackDDElParam(WM_DDE_ACK, 0, atomItem));
När ett WM_DDE_DATA meddelande tas emot bearbetar klienten dataobjektvärdet efter behov. Om fAckReq- medlem som pekas på i WM_DDE_DATA-meddelandet är 1, måste klienten sedan skicka ett positivt WM_DDE_ACK meddelande till servern, enligt följande exempel.
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);
I det här exemplet undersöker klienten dataformatet. Om formatet inte är CF_TEXT (eller om klienten inte kan låsa minnet för data) skickar klienten ett negativt WM_DDE_ACK meddelande som anger att det inte kan bearbeta data. Om klienten inte kan låsa ett datahandtag på grund av att handtaget innehåller medlemmen fAckReq, bör klienten inte skicka ett negativt WM_DDE_ACK meddelande. Klienten bör i stället avsluta konversationen.
Om en klient skickar en negativ bekräftelse som svar på ett WM_DDE_DATA meddelande ansvarar servern för att frigöra minnet (men inte parametern lParam) som refereras av det WM_DDE_DATA meddelande som är associerat med den negativa bekräftelsen.
Om den kan bearbeta data undersöker klienten fAckReq- medlem av DDEDATA--strukturen för att avgöra om servern begärde att bli informerad om att klienten framgångsrikt har tagit emot och bearbetat data. Om servern begärde den här informationen skickar klienten ett positivt WM_DDE_ACK meddelande till servern.
Eftersom upplåsning av data ogiltigförklarar pekaren till data sparar klienten värdet för fRelease medlem innan dataobjektet låss upp. När värdet har sparats undersöker klienten det för att avgöra om serverprogrammet begärde att klienten skulle frigöra det minne som innehåller data. klienten agerar därefter.
När klienten får ett negativt WM_DDE_ACK meddelande kan klienten fråga efter samma objektvärde igen och ange ett annat urklippsformat. Vanligtvis ber en klient först om det mest komplexa format som den kan stödja och går ner, om det behövs, genom successivt enklare format tills den hittar ett som servern kan tillhandahålla.
Om servern stöder formatobjektet i systemämnet kan klienten fastställa vilka urklippsformat som servern stöder, i stället för att behöva bestämma dem varje gång klienten begär ett objekt.
Skicka ett objekt till servern
Klienten kan skicka ett objektvärde till servern med hjälp av meddelandet WM_DDE_POKE. Klienten renderar objektet som ska skickas och skickar WM_DDE_POKE meddelandet, enligt följande exempel.
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.
}
Anteckning
Att skicka data med hjälp av ett WM_DDE_POKE meddelande är i stort sett detsamma som att skicka dem med hjälp av WM_DDE_DATA, förutom att WM_DDE_POKE skickas från klienten till servern.
Om servern kan acceptera dataobjektvärdet i det format som återges av klienten bearbetar servern objektvärdet efter behov och skickar klienten ett positivt WM_DDE_ACK meddelande. Om det inte går att bearbeta objektvärdet, på grund av dess format eller av andra skäl, skickar servern klienten ett negativt WM_DDE_ACK meddelande.
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.
I det här exemplet anropar servern GlobalGetAtomName för att hämta namnet på objektet som klienten skickade. Servern avgör sedan om den stöder objektet och om objektet återges i rätt format (det vill CF_TEXT). Om objektet inte stöds och inte återges i rätt format, eller om servern inte kan låsa minnet för data, skickar servern en negativ bekräftelse tillbaka till klientprogrammet. Observera att i det här fallet är det korrekt att skicka en negativ bekräftelse eftersom meddelanden av typen WM_DDE_POKE alltid antas ha medlemmen fAckReq satt. Servern bör ignorera medlemmen.
Om en server skickar en negativ bekräftelse som svar på ett WM_DDE_POKE meddelande ansvarar klienten för att frigöra minnet (men inte parametern lParam) som refereras av det WM_DDE_POKE meddelande som är associerat med den negativa bekräftelsen.
Upprätta en permanent datalänk
Ett klientprogram kan använda DDE för att upprätta en länk till ett objekt i ett serverprogram. När en sådan länk har upprättats skickar servern regelbundna uppdateringar av det länkade objektet till klienten, vanligtvis när värdet för objektet ändras. Därför upprättas en permanent dataström mellan de två programmen. den här dataströmmen finns kvar tills den uttryckligen kopplas från.
Initiera en datalänk
Klienten initierar en datalänk genom att publicera ett WM_DDE_ADVISE meddelande, enligt följande exempel.
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
}
I det här exemplet anger klientprogrammet flaggan fDeferUpd för WM_DDE_ADVISE-meddelandet till FALSE-. Detta instruerar serverprogrammet att skicka data till klienten när data ändras.
Om servern inte kan hantera WM_DDE_ADVISE begäran skickar den ett negativt WM_DDE_ACK meddelande till klienten. Men om servern har åtkomst till objektet och kan återge det i det begärda formatet noterar servern den nya länken (återkallar flaggorna som anges i parametern hOptions) och skickar klienten ett positivt WM_DDE_ACK meddelande. Från och med då, tills klienten utfärdar ett matchande WM_DDE_UNADVISE meddelande, skickar servern nya data till klienten varje gång värdet för objektet ändras på servern.
Meddelandet WM_DDE_ADVISE anger formatet för de data som ska utbytas under länken. Om klienten försöker upprätta en annan länk med samma objekt men använder ett annat dataformat kan servern välja att avvisa det andra dataformatet eller försöka stödja det. Om en varm länk har upprättats för ett dataobjekt kan servern endast ha stöd för ett dataformat i taget. Det beror på att WM_DDE_DATA meddelande för en varm länk har en NULL- datareferens, som annars innehåller formatinformationen. Därför måste en server avvisa alla varma länkar för ett objekt som redan är länkat och måste avvisa alla länkar för ett objekt som har varma länkar. En annan tolkning kan vara att servern ändrar formatet och det varma eller varma tillståndet för en länk när en andra länk begärs för samma dataobjekt.
I allmänhet bör klientprogram inte försöka upprätta fler än en länk i taget för ett dataobjekt.
Initiera en datalänk med kommandot Klistra in länk
Program som stöder heta eller varma datalänkar stöder vanligtvis ett registrerat Urklippsformat med namnet Link. När det här urklippsformatet är associerat med programmets kommandon Kopiera och Klistra in länk kan användaren upprätta DDE-konversationer mellan program genom att kopiera ett dataobjekt i serverprogrammet och klistra in det i klientprogrammet.
Ett serverprogram stöder formatet Länka Urklipp genom att placera en sträng som innehåller program-, ämnes- och objektnamnen i Urklipp när användaren väljer kommandot Kopiera från menyn Redigera. Följande är Länks standardformat:
applikation**\0ämne\0objekt\0\0**
Ett enda null-tecken separerar namnen och två nulltecken avslutar hela strängen.
Både klient- och serverprogrammen måste registrera urklippsformatet för Länk enligt följande:
cfLink = RegisterClipboardFormat("Link");
Ett klientprogram stöder formatet Länk urklipp genom kommandot Klistra in länk på Redigera-menyn. När användaren väljer det här kommandot parsar klientprogrammet program-, ämnes- och objektnamnen från Urklippsdata i Länkformat. Med hjälp av dessa namn initierar klientprogrammet en konversation för programmet och ämnet, om en sådan konversation inte redan finns. Klientprogrammet skickar sedan ett WM_DDE_ADVISE-meddelande till serverprogrammet för att specificera objektnamnet i urklippsdata i länkformat.
Följande är ett exempel på ett klientprograms svar när användaren väljer kommandot Klistra in länk.
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;
}
I det här exemplet öppnar klientprogrammet Urklipp och avgör om det innehåller data i länkformatet (det vill säga cfLink) som det tidigare hade registrerat. Om inte, eller om det inte kan låsa data i Urklipp, returnerar klienten.
När klientprogrammet hämtar en pekare till urklippsdatan parsar det datan för att extrahera program-, ämnes- och objektnamnen.
Klientprogrammet avgör om det redan finns en konversation om ämnet mellan det och serverprogrammet. Om det finns en konversation kontrollerar klienten om det redan finns en länk för dataobjektet. Om en sådan länk finns visar klienten en meddelanderuta för användaren. Annars anropas en egen SendAdvise-funktion för att skicka ett WM_DDE_ADVISE meddelande till servern för objektet.
Om det inte redan finns en konversation i ämnet mellan klienten och servern anropar klienten först sin egen SendInitiate- funktion för att sända WM_DDE_INITIATE-meddelandet för att begära en konversation och anropar sedan sin egen FindServerGivenAppTopic- funktion för att upprätta konversationen med fönstret som svarar för serverprogrammets räkning. När konversationen har börjat anropar klientprogrammet SendAdvise för att begära länken.
Meddela klienten att data har ändrats
När klienten upprättar en länk med hjälp av WM_DDE_ADVISE-meddelandet, och medlemmen fDeferUpd inte är satt (dvs. lika med noll) i DDEDATA--strukturen, har klienten begärt att servern skickar dataobjektet varje gång objektets värde ändras. I sådana fall renderar servern det nya värdet för dataobjektet i det tidigare angivna formatet och skickar klienten ett WM_DDE_DATA meddelande, enligt följande exempel.
// 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.
}
I det här exemplet bearbetar klienten objektvärdet efter behov. Om flaggan fAckReq för objektet har angetts skickar klienten ett positivt WM_DDE_ACK meddelande till servern.
När klienten upprättar länken, med fDeferUpd medlemsuppsättning (dvs. lika med 1), har klienten begärt att endast ett meddelande, inte själva data, ska skickas varje gång data ändras. I sådana fall, när objektvärdet ändras, renderar servern inte värdet utan skickar bara ett WM_DDE_DATA meddelande med ett null-datahandtag, vilket visas i följande exempel.
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.
}
Vid behov kan klienten begära det senaste värdet för dataobjektet genom att utfärda ett normalt WM_DDE_REQUEST meddelande, eller ignorera meddelandet från servern om att data har ändrats. I båda fallen, om fAckReq är lika med 1, förväntas klienten skicka ett positivt WM_DDE_ACK meddelande till servern.
Avsluta en datalänk
Om klienten begär att en specifik datalänk avslutas skickar klienten ett WM_DDE_UNADVISE meddelande till servern, enligt följande exempel.
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.
}
Servern kontrollerar om klienten för närvarande har en länk till det specifika objektet i den här konversationen. Om det finns en länk skickar servern ett positivt WM_DDE_ACK meddelande till klienten. servern behöver sedan inte längre skicka uppdateringar om objektet. Om det inte finns någon länk skickar servern ett negativt WM_DDE_ACK meddelande till klienten.
Meddelandet WM_DDE_UNADVISE anger ett dataformat. Ett format med noll informerar servern om att stoppa alla länkar för det angivna objektet, även om flera heta länkar upprättas och var och en använder ett annat format.
Om du vill avsluta alla länkar för en konversation skickar klientprogrammet ett WM_DDE_UNADVISE meddelande till servern med en null-objektatom. Servern avgör om konversationen har minst en länk upprättad för närvarande. Om det finns en länk skickar servern ett positivt WM_DDE_ACK meddelande till klienten. servern behöver sedan inte längre skicka några uppdateringar i konversationen. Om det inte finns någon länk skickar servern ett negativt WM_DDE_ACK meddelande till klienten.
Utföra kommandon i ett serverprogram
Program kan använda WM_DDE_EXECUTE-meddelandet för att orsaka att ett visst kommando eller en serie kommandon utförs i ett annat program. För att göra detta skickar klienten ett WM_DDE_EXECUTE meddelande som innehåller ett handtag till en kommandosträng, enligt följande exempel.
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);
}
I det här exemplet försöker servern utföra den angivna kommandosträngen. Om det lyckas skickar servern ett positivt WM_DDE_ACK meddelande till klienten. annars skickar det ett negativt WM_DDE_ACK meddelande. Det här WM_DDE_ACK meddelandet återanvänder hCommand--handtaget som skickades i det ursprungliga WM_DDE_EXECUTE meddelandet.
Om klientens kommandokörningssträng begär att servern avslutas bör servern svara genom att skicka ett positivt WM_DDE_ACK meddelande och sedan publicera ett WM_DDE_TERMINATE meddelande innan det avslutas. Alla andra kommandon som skickas med ett WM_DDE_EXECUTE meddelande ska köras synkront. Servern bör alltså skicka ett WM_DDE_ACK meddelande först när kommandot har slutförts.
Avsluta en konversation
Antingen klienten eller servern kan utfärda ett WM_DDE_TERMINATE meddelande för att avsluta en konversation när som helst. På samma sätt bör både klient- och serverprogrammen vara beredda att ta emot det här meddelandet när som helst. Ett program måste avsluta alla sina konversationer innan det stängs av.
I följande exempel publicerar programmet som avslutar konversationen ett WM_DDE_TERMINATE meddelande.
PostMessage(hwndServerDDE, WM_DDE_TERMINATE,
(WPARAM) hwndClientDDE, 0);
Detta informerar det andra programmet om att det sändande programmet inte skickar några ytterligare meddelanden och att mottagaren kan stänga fönstret. Mottagaren förväntas i alla fall svara snabbt genom att skicka ett WM_DDE_TERMINATE meddelande. Mottagaren får inte skicka ett negativt, upptaget eller positivt WM_DDE_ACK meddelande.
När ett program har skickat WM_DDE_TERMINATE meddelande till partnern i en DDE-konversation får det inte svara på meddelanden från den partnern, eftersom partnern kan ha förstört fönstret som svaret skulle skickas till.
Om ett program tar emot ett annat DDE-meddelande än WM_DDE_TERMINATE efter att det har publicerat WM_DDE_TERMINATEbör alla objekt som är associerade med de mottagna meddelandena frigöras, förutom datahandtagen för WM_DDE_DATA eller WM_DDE_POKE meddelanden som inte har fRelease medlemsuppsättning.
När ett program är på väg att avslutas bör det avsluta alla aktiva DDE-konversationer innan bearbetningen av WM_DESTROY meddelandet slutförs. Men om ett program inte avslutar sina aktiva DDE-konversationer avslutar systemet alla DDE-konversationer som är associerade med ett fönster när fönstret förstörs. I följande exempel visas hur ett serverprogram avslutar alla DDE-konversationer.
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;
}