Notes
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Cette section contient des exemples de code sur les tâches suivantes :
- Lancement d’une conversation
- Transfert d’un élément unique
- Établissement d’un lien de données permanent
- Exécution de commandes dans une application serveur
- Fin d’une conversation
Lancement d’une conversation
Pour lancer une conversation DDE (Dynamic Data Exchange), le client envoie un message WM_DDE_INITIATE . En règle générale, le client diffuse ce message en appelant SendMessage, avec –1 comme premier paramètre. Si l’application dispose déjà du handle de fenêtre à l’application serveur, elle peut envoyer le message directement à cette fenêtre. Le client prépare des atomes pour le nom de l’application et le nom de la rubrique en appelant GlobalAddAtom. Le client peut demander des conversations avec n’importe quelle application serveur potentielle et pour toute rubrique potentielle en fournissant des atomes NULL (caractères génériques) pour l’application et la rubrique.
L’exemple suivant montre comment le client lance une conversation, où l’application et la rubrique sont spécifiées.
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);
Remarque
Si votre application utilise des atomes NULL , vous n’avez pas besoin d’utiliser les fonctions GlobalAddAtom et GlobalDeleteAtom . Dans cet exemple, l’application cliente crée deux atomes globaux contenant le nom du serveur et le nom de la rubrique, respectivement.
L’application cliente envoie un message WM_DDE_INITIATE avec ces deux atomes dans le paramètre lParam du message. Dans l’appel à la fonction SendMessage , le handle de fenêtre spécial –1 dirige le système pour envoyer ce message à toutes les autres applications actives. SendMessage ne retourne pas à l’application cliente tant que toutes les applications qui reçoivent le message n’ont pas retourné le contrôle au système. Cela signifie que tous les messages WM_DDE_ACK envoyés en réponse par les applications serveur sont assurés d’avoir été traités par le client au moment où l’appel SendMessage a retourné.
Une fois SendMessage retourné, l’application cliente supprime les atomes globaux.
Les applications serveur répondent en fonction de la logique illustrée dans le diagramme suivant.
Pour reconnaître une ou plusieurs rubriques, le serveur doit créer des atomes pour chaque conversation (nécessitant des atomes de nom d’application en double s’il existe plusieurs rubriques) et envoyer un message WM_DDE_ACK pour chaque conversation, comme illustré dans l’exemple suivant.
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.
}
Lorsqu’un serveur répond avec un message WM_DDE_ACK , l’application cliente doit enregistrer un handle dans la fenêtre du serveur. Le client recevant le handle en tant que paramètre wParam du message WM_DDE_ACK , puis envoie tous les messages DDE suivants à la fenêtre du serveur que ce handle identifie.
Si votre application cliente utilise un atome NULL pour le nom de l’application ou le nom de la rubrique, attendez-vous que l’application reçoive des accusés de réception de plusieurs applications serveur. Plusieurs accusés de réception peuvent également provenir de plusieurs instances d’un serveur DDE, même si votre application cliente n’utilise pas d'atomes NULL. Un serveur doit toujours utiliser une fenêtre unique pour chaque conversation. La procédure de fenêtre dans l’application cliente peut utiliser un handle pour la fenêtre du serveur (fournie en tant que paramètre lParam de WM_DDE_INITIATE) pour suivre plusieurs conversations. Cela permet à une seule fenêtre cliente de traiter plusieurs conversations sans avoir à se terminer et à se reconnecter avec une nouvelle fenêtre cliente pour chaque conversation.
Transfert d’un élément unique
Une fois qu’une conversation DDE a été établie, le client peut récupérer la valeur d’un élément de données à partir du serveur en émettant le message WM_DDE_REQUEST , ou envoyer une valeur d’élément de données au serveur en émettant WM_DDE_POKE.
Récupération d’un élément à partir du serveur
Pour récupérer un élément à partir du serveur, le client envoie un message WM_DDE_REQUEST spécifiant l’élément et le format à récupérer, comme illustré dans l’exemple suivant.
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.
}
Dans cet exemple, le client spécifie le format du Presse-papiers CF_TEXT comme format préféré pour l’élément de données demandé.
Le récepteur (serveur) du message WM_DDE_REQUEST doit généralement supprimer l’atome d’élément, mais si l’appel PostMessage échoue, le client doit supprimer l’atome.
Si le serveur a accès à l’élément demandé et peut le restituer au format demandé, le serveur copie la valeur de l’élément en tant qu’objet de mémoire partagée et envoie au client un message WM_DDE_DATA , comme illustré dans l’exemple suivant.
// 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.
}
Dans cet exemple, l’application serveur alloue un objet mémoire pour contenir l’élément de données. L’objet de données est initialisé en tant que structure DDEDATA .
L’application serveur définit ensuite le membre cfFormat de la structure sur CF_TEXT pour informer l’application cliente que les données sont au format texte. Le client répond en copiant la valeur des données demandées dans le membre Value de la structure DDEDATA . Une fois que le serveur a rempli l’objet de données, le serveur déverrouille les données et crée un atome global contenant le nom de l’élément de données.
Enfin, le serveur émet le message WM_DDE_DATA en appelant PostMessage. Le handle de l’objet de données et l’atome contenant le nom de l’élément sont encapsulés dans le paramètre lParam du message par la fonction PackDDElParam.
Si PostMessage échoue, le serveur doit utiliser la fonction FreeDDElParam pour libérer le paramètre lParam packed. Le serveur doit également libérer le paramètre lParam packed pour le message WM_DDE_REQUEST qu’il a reçu.
Si le serveur ne peut pas satisfaire la demande, il envoie un message WM_DDE_ACK négatif au client, comme illustré dans l’exemple suivant.
// Negative acknowledgment.
PostMessage(hwndClientDDE,
WM_DDE_ACK,
(WPARAM) hwndServerDDE,
PackDDElParam(WM_DDE_ACK, 0, atomItem));
Lors de la réception d’un message WM_DDE_DATA , le client traite la valeur de l’élément de données selon les besoins. Ensuite, si le membre fAckReq pointé dans le message WM_DDE_DATA est 1, le client doit envoyer au serveur un message positif WM_DDE_ACK , comme illustré dans l’exemple suivant.
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);
Dans cet exemple, le client examine le format des données. Si le format n’est pas CF_TEXT (ou si le client ne peut pas verrouiller la mémoire des données), le client envoie un message WM_DDE_ACK négatif pour indiquer qu’il ne peut pas traiter les données. Si le client ne peut pas verrouiller un handle de données parce que le handle contient le membre fAckReq ; il ne doit pas envoyer un message WM_DDE_ACK négatif. Au lieu de cela, le client doit mettre fin à la conversation.
Si un client envoie un accusé de réception négatif en réponse à un message WM_DDE_DATA , le serveur est chargé de libérer la mémoire (mais pas le paramètre lParam ) référencé par le message WM_DDE_DATA associé à l’accusé de réception négatif.
S’il peut traiter les données, le client examine le membre fAckReq de la structure DDEDATA pour déterminer si le serveur a demandé qu’il soit informé que le client a reçu et traité les données correctement. Si le serveur a demandé ces informations, le client envoie au serveur un message positif WM_DDE_ACK .
Étant donné que le déverrouillage des données invalide le pointeur vers les données, le client enregistre la valeur du membre fRelease avant de déverrouiller l’objet de données. Après avoir enregistré la valeur, le client l’examine pour déterminer si l’application serveur a demandé au client de libérer la mémoire contenant les données ; le client agit en conséquence.
Lors de la réception d’un message négatif WM_DDE_ACK, le client peut demander à nouveau la même valeur de l'élément, en spécifiant un autre format de presse-papiers. En règle générale, un client demande d'abord le format le plus complexe qu'il peut prendre en charge, puis passe à des formats progressivement plus simples si nécessaire, jusqu'à ce qu'il trouve un que le serveur peut fournir.
Si le serveur prend en charge l'élément Formats de la rubrique système, le client peut déterminer une fois quels formats de presse-papiers le serveur prend en charge, au lieu de les déterminer chaque fois que le client demande un élément.
Envoi d’un élément au serveur
Le client peut envoyer une valeur d’élément au serveur à l’aide du message WM_DDE_POKE . Le client affiche l’élément à envoyer et envoie le message WM_DDE_POKE , comme illustré dans l’exemple suivant.
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.
}
Remarque
L’envoi de données à l’aide d’un message WM_DDE_POKE est essentiellement identique à l’envoi de données à l’aide de WM_DDE_DATA, sauf que WM_DDE_POKE est envoyé du client au serveur.
Si le serveur est en mesure d’accepter la valeur de l’élément de données dans le format rendu par le client, le serveur traite la valeur de l’élément selon les besoins et envoie au client un message positif WM_DDE_ACK . S’il ne parvient pas à traiter la valeur de l’élément, en raison de son format ou pour d’autres raisons, le serveur envoie au client un message WM_DDE_ACK négatif.
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.
Dans cet exemple, le serveur appelle GlobalGetAtomName pour récupérer le nom de l’élément envoyé par le client. Le serveur détermine ensuite s’il prend en charge l’élément et si l’élément est rendu au format correct (autrement dit, CF_TEXT). Si l’élément n’est pas pris en charge et n’est pas rendu dans le format correct ou si le serveur ne peut pas verrouiller la mémoire des données, le serveur envoie un accusé de réception négatif à l’application cliente. Notez que dans ce cas, l’envoi d’un accusé de réception négatif est correct, car WM_DDE_POKE messages sont toujours supposés avoir le membre fAckReq défini. Le serveur doit ignorer le membre.
Si un serveur envoie un accusé de réception négatif en réponse à un message WM_DDE_POKE , le client est chargé de libérer la mémoire (mais pas le paramètre lParam ) référencé par le message WM_DDE_POKE associé à l’accusé de réception négatif.
Établissement d’un lien de données permanent
Une application cliente peut utiliser DDE pour établir un lien vers un élément dans une application serveur. Une fois ce lien établi, le serveur envoie des mises à jour périodiques de l’élément lié au client, généralement chaque fois que la valeur de l’élément change. Ainsi, un flux de données permanent est établi entre les deux applications ; ce flux de données reste en place jusqu’à ce qu’il soit explicitement déconnecté.
Lancement d’un lien de données
Le client lance un lien de données en publiant un message WM_DDE_ADVISE , comme illustré dans l’exemple suivant.
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
}
Dans cet exemple, l’application cliente définit l’indicateur fDeferUpd du message WM_DDE_ADVISEsur FALSE. Cela permet à l’application serveur d’envoyer les données au client chaque fois que les données changent.
Si le serveur ne parvient pas à traiter la demande de WM_DDE_ADVISE , il envoie au client un message WM_DDE_ACK négatif. Toutefois, si le serveur a accès à l’élément et peut le rendre au format demandé, le serveur note le nouveau lien (rappelant les indicateurs spécifiés dans le paramètre hOptions ) et envoie au client un message positif WM_DDE_ACK . À partir de là, jusqu’à ce que le client émet un message WM_DDE_UNADVISE correspondant, le serveur envoie les nouvelles données au client chaque fois que la valeur de l’élément change dans le serveur.
Le message WM_DDE_ADVISE établit le format des données à échanger pendant le lien. Si le client tente d’établir un autre lien avec le même élément, mais utilise un autre format de données, le serveur peut choisir de rejeter le deuxième format de données ou de tenter de le prendre en charge. Si un lien chaud a été établi pour un élément de données, le serveur ne peut prendre en charge qu’un seul format de données à la fois. Cela est dû au fait que le message WM_DDE_DATA d’un lien actif a un handle de données NULL, qui contient sinon les informations de format. Par conséquent, un serveur doit rejeter tous les liens chauds pour un élément déjà lié, et doit rejeter tous les liens d’un élément qui a des liens chauds. Une autre interprétation peut être que le serveur modifie le format et l’état chaud ou chaud d’un lien lorsqu’un deuxième lien est demandé pour le même élément de données.
En général, les applications clientes ne doivent pas tenter d’établir plusieurs liens à la fois pour un élément de données.
Lancement d’un lien de données avec la commande Coller le lien
Les applications qui prennent en charge les liens de données chauds ou tièdes prennent généralement en charge un format de presse-papiers enregistré nommé Lien. Lorsqu’il est associé aux commandes Copier et Coller le lien de l’application, ce format de presse-papiers permet à l’utilisateur d’établir des conversations DDE entre les applications, en copiant simplement un élément de données dans l’application serveur et en le collant dans l’application cliente.
Une application serveur prend en charge le format du presse-papiers Link en y plaçant une chaîne contenant le nom de l’application, du sujet et de l’élément lorsque l’utilisateur choisit la commande Copier dans le menu Modifier . Voici le format de lien standard :
application**\0topic\0item\0\0**
Un caractère null unique sépare les noms, et deux caractères Null terminent la chaîne entière.
Les applications client et serveur doivent enregistrer le format du presse-papiers Link, comme indiqué :
cfLink = RegisterClipboardFormat("Link");
Une application cliente prend en charge le format du presse-papiers Link au moyen d’une commande Coller le lien dans son menu Édition. Lorsque l'utilisateur choisit cette commande, l'application cliente analyse les noms d'application, de sujet et d'élément à partir des données du Presse-papiers au format de lien. À l’aide de ces noms, l’application cliente lance une conversation pour l’application et la rubrique, si une telle conversation n’existe pas déjà. L’application cliente envoie ensuite un message WM_DDE_ADVISE à l’application serveur, en spécifiant le nom de l’élément contenu dans les données du Presse-papiers au format lien.
Voici un exemple de réponse d’une application cliente lorsque l’utilisateur choisit la commande Coller le lien.
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;
}
Dans cet exemple, l'application cliente ouvre le Presse-papiers et détermine s'il contient des données au format Lien (c'est-à-dire, cfLink) qu'elle avait précédemment enregistrées. Si ce n’est pas le cas, ou si elle ne peut pas verrouiller les données dans le presse-papiers, le client est renvoyé.
Une fois que l'application cliente récupère un pointeur vers les données du presse-papiers, elle analyse les données pour en extraire l'application, le sujet et les noms des éléments.
L’application cliente détermine si une conversation sur la rubrique existe déjà entre elle et l’application serveur. Si une conversation existe, le client vérifie si un lien existe déjà pour l’élément de données. S’il existe un tel lien, le client affiche une boîte de message à l’utilisateur ; sinon, il appelle sa propre fonction SendAdvise pour envoyer un message WM_DDE_ADVISE au serveur pour l’élément.
Si une conversation sur la rubrique n’existe pas déjà entre le client et le serveur, le client appelle d’abord sa propre fonction SendInitiate pour diffuser le message WM_DDE_INITIATE pour demander une conversation et, deuxièmement, appelle sa propre fonction FindServerGivenAppTopic pour établir la conversation avec la fenêtre qui répond au nom de l’application serveur. Une fois la conversation commencée, l’application cliente appelle SendAdvise pour demander le lien.
Notification du client que les données ont changé
Lorsque le client établit un lien à l’aide du message WM_DDE_ADVISE , avec le membre fDeferUpd non défini (c’est-à-dire égal à zéro) dans la structure DDEDATA , le client a demandé au serveur d’envoyer l’élément de données chaque fois que la valeur de l’élément change. Dans ce cas, le serveur affiche la nouvelle valeur de l’élément de données au format spécifié précédemment et envoie au client un message WM_DDE_DATA , comme illustré dans l’exemple suivant.
// 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.
}
Dans cet exemple, le client traite la valeur de l’élément selon les besoins. Si l’indicateur fAckReq de l’élément est défini, le client envoie au serveur un message positif WM_DDE_ACK .
Lorsque le client établit le lien, avec le jeu de membres fDeferUpd (c’est-à-dire égal à 1), il demande à ce que seule une notification, et non les données elles-mêmes, soit envoyée chaque fois que les données changent. Dans ce cas, lorsque la valeur de l’élément change, le serveur n’affiche pas la valeur, mais envoie simplement au client un message WM_DDE_DATA avec un handle de données Null, comme illustré dans l’exemple suivant.
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.
}
Si nécessaire, le client peut demander la dernière valeur de l’élément de données en émettant un message de WM_DDE_REQUEST normal, ou il peut simplement ignorer l’avis du serveur que les données ont changé. Dans les deux cas, si fAckReq est égal à 1, le client est censé envoyer un message positif WM_DDE_ACK au serveur.
Fin d’un lien de données
Si le client demande qu’un lien de données spécifique soit arrêté, le client envoie au serveur un message WM_DDE_UNADVISE , comme illustré dans l’exemple suivant.
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.
}
Le serveur vérifie si le client dispose actuellement d’un lien vers l’élément spécifique de cette conversation. S’il existe un lien, le serveur envoie au client un message positif WM_DDE_ACK ; le serveur n’est plus nécessaire pour envoyer des mises à jour sur l’élément. S’il n’existe aucun lien, le serveur envoie au client un message WM_DDE_ACK négatif.
Le message WM_DDE_UNADVISE spécifie un format de données. Un format de zéro informe le serveur d’arrêter tous les liens pour l’élément spécifié, même si plusieurs liens chauds sont établis et chacun utilise un format différent.
Pour terminer tous les liens d’une conversation, l’application cliente envoie au serveur un message WM_DDE_UNADVISE avec un atome d’élément null. Le serveur détermine si la conversation a au moins un lien actuellement établi. S’il existe un lien, le serveur envoie au client un message positif WM_DDE_ACK ; le serveur n’a plus besoin d’envoyer de mises à jour dans la conversation. S’il n’existe aucun lien, le serveur envoie au client un message WM_DDE_ACK négatif.
Exécution de commandes dans une application serveur
Les applications peuvent utiliser le message WM_DDE_EXECUTE pour provoquer l’exécution d’une certaine commande ou d’une série de commandes dans une autre application. Pour ce faire, le client envoie au serveur un message WM_DDE_EXECUTE contenant un handle à une chaîne de commande, comme illustré dans l’exemple suivant.
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);
}
Dans cet exemple, le serveur tente d’exécuter la chaîne de commande spécifiée. S’il réussit, le serveur envoie au client un message positif WM_DDE_ACK ; sinon, il envoie un message WM_DDE_ACK négatif. Ce message WM_DDE_ACK réutilise le handle hCommand passé dans le message de WM_DDE_EXECUTE d’origine.
Si la chaîne d’exécution de commande du client demande que le serveur se termine, le serveur doit répondre en envoyant un message positif WM_DDE_ACK , puis publier un message WM_DDE_TERMINATE avant de terminer. Toutes les autres commandes envoyées avec un message WM_DDE_EXECUTE doivent être exécutées de manière synchrone ; autrement dit, le serveur doit envoyer un message WM_DDE_ACK uniquement une fois la commande terminée.
Fin d’une conversation
Le client ou le serveur peut émettre un message WM_DDE_TERMINATE pour arrêter une conversation à tout moment. De même, les applications clientes et serveurs doivent être prêtes à recevoir ce message à tout moment. Une application doit mettre fin à toutes ses conversations avant de s’arrêter.
Dans l’exemple suivant, l’application termine la conversation publie un message WM_DDE_TERMINATE .
PostMessage(hwndServerDDE, WM_DDE_TERMINATE,
(WPARAM) hwndClientDDE, 0);
Cela informe l’autre application que l’application d’envoi n’envoie aucun autre message et que le destinataire peut fermer sa fenêtre. Il est attendu que le destinataire réponde rapidement dans tous les cas en envoyant un message WM_DDE_TERMINATE. Le destinataire ne doit pas envoyer de message WM_DDE_ACK négatif, occupé ou positif.
Une fois qu’une application a envoyé le message WM_DDE_TERMINATE au partenaire dans une conversation DDE, elle ne doit pas répondre aux messages de ce partenaire, car le partenaire a peut-être détruit la fenêtre à laquelle la réponse serait envoyée.
Si une application reçoit un message DDE autre que WM_DDE_TERMINATE une fois qu’elle a publié WM_DDE_TERMINATE, elle doit libérer tous les objets associés aux messages reçus, à l’exception des handles de données pour WM_DDE_DATA ou WM_DDE_POKE messages qui n’ont pas le membre fRelease défini.
Lorsqu’une application est sur le point de se terminer, elle doit mettre fin à toutes les conversations DDE actives avant de terminer le traitement du message WM_DESTROY . Toutefois, si une application ne termine pas ses conversations DDE actives, le système met fin à toutes les conversations DDE associées à une fenêtre lorsque la fenêtre est détruite. L’exemple suivant montre comment une application serveur met fin à toutes les conversations DDE.
void TerminateConversations(hwndServerDDE)
HWND hwndServerDDE;
{
HWND hwndClientDDE;
// Terminate each active conversation.
while (hwndClientDDE = GetNextLink(hwndClientDDE))
{
SendTerminate(hwndServerDDE, hwndClientDDE);
}
return;
}
BOOL AtLeastOneLinkActive(VOID)
{
return TRUE;
}
HWND GetNextLink(hwndDummy)
HWND hwndDummy;
{
return (HWND) 1;
}
VOID SendTerminate(HWND hwndServerDDE, HWND hwndClientDDE)
{
return;
}