Usando o intercâmbio dinâmico de dados
Esta seção tem exemplos de código sobre as seguintes tarefas:
- Iniciando uma conversa
- Transferindo um único item
- Estabelecendo um link de dados permanente
- Executando comandos em um aplicativo de servidor
- Encerrando uma conversa
Iniciando uma conversa
Para iniciar uma conversa DDE (Dynamic Data Exchange), o cliente envia uma mensagem WM_DDE_INITIATE. Normalmente, o cliente transmite essa mensagem chamando SendMessage, com –1 como o primeiro parâmetro. Se o aplicativo já tiver o identificador de janela para o aplicativo de servidor, ele poderá enviar a mensagem diretamente para essa janela. O cliente prepara átomos para o nome do aplicativo e o nome do tópico chamando GlobalAddAtom. O cliente pode solicitar conversas com qualquer aplicativo de servidor em potencial e para qualquer tópico potencial fornecendo átomos NULL (curinga) para o aplicativo e o tópico.
O exemplo a seguir ilustra como o cliente inicia uma conversa, onde o aplicativo e o tópico são especificados.
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);
Observação
Se seu aplicativo usa átomos NULL, você não precisa usar as funções GlobalAddAtom e GlobalDeleteAtom. Neste exemplo, o aplicativo cliente cria dois átomos globais contendo o nome do servidor e o nome do tópico, respectivamente.
O aplicativo cliente envia uma mensagem WM_DDE_INITIATE com esses dois átomos no parâmetro lParam da mensagem. Na chamada para a função SendMessage , o identificador de janela especial –1 direciona o sistema para enviar essa mensagem para todos os outros aplicativos ativos. SendMessage não retorna ao aplicativo cliente até que todos os aplicativos que recebem a mensagem tenham, por sua vez, retornado o controle para o sistema. Isso significa que todas as mensagens WM_DDE_ACK enviadas em resposta pelos aplicativos do servidor têm a garantia de terem sido processadas pelo cliente no momento em que a chamada SendMessage for retornada.
Depois que SendMessage retorna, o aplicativo cliente exclui os átomos globais.
Os aplicativos de servidor respondem de acordo com a lógica ilustrada no diagrama a seguir.
Para reconhecer um ou mais tópicos, o servidor deve criar átomos para cada conversa (exigindo átomos de nome de aplicativo duplicados se houver vários tópicos) e enviar uma mensagem WM_DDE_ACK para cada conversa, conforme ilustrado no exemplo a seguir.
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.
}
Quando um servidor responde com uma mensagem WM_DDE_ACK , o aplicativo cliente deve salvar um identificador na janela do servidor. O cliente que recebe o identificador como o parâmetro wParam da mensagem WM_DDE_ACK envia todas as mensagens DDE subsequentes para a janela do servidor que esse identificador identifica.
Se o aplicativo cliente usa um átomo NULL para o nome do aplicativo ou nome do tópico, espere que o aplicativo receba confirmações de mais de um aplicativo de servidor. Várias confirmações também podem vir de várias instâncias de um servidor DDE, mesmo se seu aplicativo cliente não usar átomos NULL . Um servidor deve sempre usar uma janela exclusiva para cada conversa. O procedimento de janela no aplicativo cliente pode usar um identificador para a janela do servidor (fornecido como o parâmetro lParam de WM_DDE_INITIATE) para rastrear várias conversas. Isso permite que uma única janela de cliente processe várias conversas sem precisar encerrar e se reconectar com uma nova janela de cliente para cada conversa.
Transferindo um único item
Depois que uma conversa DDE for estabelecida, o cliente poderá recuperar o valor de um item de dados do servidor emitindo a mensagem WM_DDE_REQUEST ou enviar um valor de item de dados para o servidor emitindo WM_DDE_POKE.
Recuperando um item do servidor
Para recuperar um item do servidor, o cliente envia ao servidor uma mensagem de WM_DDE_REQUEST especificando o item e o formato a serem recuperados, conforme mostrado no exemplo a seguir.
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.
}
Neste exemplo, o cliente especifica o formato da área de transferência CF_TEXT como o formato preferencial para o item de dados solicitado.
O receptor (servidor) da mensagem WM_DDE_REQUEST normalmente deve excluir o item atom, mas se a chamada PostMessage falhar, o cliente deve excluir o atom.
Se o servidor tiver acesso ao item solicitado e puder renderizá-lo no formato solicitado, o servidor copiará o valor do item como um objeto de memória compartilhada e enviará ao cliente uma mensagem WM_DDE_DATA , conforme ilustrado no exemplo a seguir.
// 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.
}
Neste exemplo, o aplicativo de servidor aloca um objeto de memória para conter o item de dados. O objeto de dados é inicializado como uma estrutura DDEDATA.
Em seguida, o aplicativo de servidor define o membro cfFormat da estrutura para CF_TEXT para informar ao aplicativo cliente que os dados estão no formato de texto. O cliente responde copiando o valor dos dados solicitados para o membro Value da estrutura DDEDATA . Depois que o servidor tiver preenchido o objeto de dados, o servidor desbloqueia os dados e cria um átomo global contendo o nome do item de dados.
Finalmente, o servidor emite a mensagem WM_DDE_DATA chamando PostMessage. O identificador para o objeto de dados e o átomo que contém o nome do item são empacotados no parâmetro lParam da mensagem pela função PackDDElParam.
Se PostMessage falhar, o servidor deve usar a função FreeDDElParam para liberar o parâmetro lParam compactado. O servidor também deve liberar o parâmetro lParam compactado para a mensagem WM_DDE_REQUEST recebida.
Se o servidor não puder atender à solicitação, ele enviará uma mensagem de WM_DDE_ACK negativa para o cliente, conforme mostrado no exemplo a seguir.
// Negative acknowledgment.
PostMessage(hwndClientDDE,
WM_DDE_ACK,
(WPARAM) hwndServerDDE,
PackDDElParam(WM_DDE_ACK, 0, atomItem));
Ao receber uma mensagem de WM_DDE_DATA , o cliente processa o valor do item de dados conforme apropriado. Em seguida, se o membro fAckReq apontado na mensagem WM_DDE_DATA for 1, o cliente deverá enviar ao servidor uma mensagem de WM_DDE_ACK positiva, conforme mostrado no exemplo a seguir.
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);
Neste exemplo, o cliente examina o formato dos dados. Se o formato não estiver CF_TEXT (ou se o cliente não puder bloquear a memória para os dados), o cliente enviará uma mensagem de WM_DDE_ACK negativa para indicar que não pode processar os dados. Se o cliente não pode bloquear um identificador de dados porque o identificador contém o membro fAckReq, o cliente não deve enviar uma mensagem de WM_DDE_ACK negativa. Em vez disso, o cliente deve encerrar a conversa.
Se um cliente enviar uma confirmação negativa em resposta a uma mensagem WM_DDE_DATA, o servidor será responsável por liberar a memória (mas não o parâmetro lParam) referenciada pela mensagem WM_DDE_DATA associada à confirmação negativa.
Se ele pode processar os dados, o cliente examina o membro fAckReq da estrutura DDEDATA para determinar se o servidor solicitou que ele fosse informado de que o cliente recebeu e processou os dados com êxito. Se o servidor solicitou essas informações, o cliente enviará ao servidor uma mensagem de WM_DDE_ACK positiva.
Como o desbloqueio de dados invalida o ponteiro para os dados, o cliente salva o valor do membro fRelease antes de desbloquear o objeto de dados. Depois de salvar o valor, o cliente o examina para determinar se o aplicativo do servidor solicitou que o cliente liberasse a memória que contém os dados; O cliente age em conformidade.
Ao receber uma mensagem de WM_DDE_ACK negativa , o cliente pode solicitar o mesmo valor de item novamente, especificando um formato de área de transferência diferente. Normalmente, um cliente pedirá primeiro o formato mais complexo que pode suportar e, em seguida, renunciará, se necessário, por meio de formatos progressivamente mais simples até encontrar um que o servidor possa fornecer.
Se o servidor oferecer suporte ao item Formatos do tópico do sistema, o cliente poderá determinar uma vez quais formatos da área de transferência o servidor suporta, em vez de determiná-los sempre que o cliente solicitar um item.
Enviando um item para o servidor
O cliente pode enviar um valor de item para o servidor usando a mensagem WM_DDE_POKE. O cliente renderiza o item a ser enviado e envia a mensagem WM_DDE_POKE, conforme ilustrado no exemplo a seguir.
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.
}
Observação
Enviar dados usando uma mensagem de WM_DDE_POKE é essencialmente o mesmo que enviá-la usando WM_DDE_DATA, exceto que WM_DDE_POKE é enviada do cliente para o servidor.
Se o servidor puder aceitar o valor do item de dados no formato renderizado pelo cliente, o servidor processará o valor do item conforme apropriado e enviará ao cliente uma mensagem de WM_DDE_ACK positiva. Se não for possível processar o valor do item, devido ao seu formato ou por outros motivos, o servidor enviará ao cliente uma mensagem de WM_DDE_ACK negativa.
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.
Neste exemplo, o servidor chama GlobalGetAtomName para recuperar o nome do item que o cliente enviou. Em seguida, o servidor determina se ele oferece suporte ao item e se o item é renderizado no formato correto (ou seja, CF_TEXT). Se o item não for suportado e não renderizado no formato correto, ou se o servidor não puder bloquear a memória para os dados, o servidor enviará uma confirmação negativa de volta para o aplicativo cliente. Observe que, nesse caso, o envio de uma confirmação negativa está correto porque WM_DDE_POKE mensagens são sempre assumidas como tendo o membro fAckReq definido. O servidor deve ignorar o membro.
Se um servidor enviar uma confirmação negativa em resposta a uma mensagem WM_DDE_POKE, o cliente será responsável por liberar a memória (mas não o parâmetro lParam) referenciada pela mensagem WM_DDE_POKE associada à confirmação negativa.
Estabelecendo um link de dados permanente
Um aplicativo cliente pode usar o DDE para estabelecer um link para um item em um aplicativo de servidor. Depois que esse link é estabelecido, o servidor envia atualizações periódicas do item vinculado para o cliente, normalmente, sempre que o valor do item é alterado. Assim, um fluxo de dados permanente é estabelecido entre os dois aplicativos; Esse fluxo de dados permanece no local até que seja explicitamente desconectado.
Iniciando um link de dados
O cliente inicia um link de dados postando uma mensagem WM_DDE_ADVISE , conforme mostrado no exemplo a seguir.
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
}
Neste exemplo, o aplicativo cliente define o sinalizador fDeferUpd da mensagem WM_DDE_ADVISE como FALSE. Isso direciona o aplicativo de servidor para enviar os dados para o cliente sempre que os dados são alterados.
Se o servidor não puder atender à solicitação de WM_DDE_ADVISE, ele enviará ao cliente uma mensagem de WM_DDE_ACK negativa. Mas se o servidor tiver acesso ao item e puder renderizá-lo no formato solicitado, o servidor anotará o novo link (lembrando os sinalizadores especificados no parâmetro hOptions) e enviará ao cliente uma mensagem de WM_DDE_ACK positiva. A partir de então, até que o cliente emita uma mensagem de WM_DDE_UNADVISE correspondente, o servidor envia os novos dados para o cliente sempre que o valor do item é alterado no servidor.
A mensagem WM_DDE_ADVISE estabelece o formato dos dados a serem trocados durante o link. Se o cliente tentar estabelecer outro vínculo com o mesmo item, mas estiver usando um formato de dados diferente, o servidor poderá optar por rejeitar o segundo formato de dados ou tentar suportá-lo. Se um link quente tiver sido estabelecido para qualquer item de dados, o servidor poderá oferecer suporte a apenas um formato de dados por vez. Isso ocorre porque a mensagem WM_DDE_DATA para um link quente tem um identificador de dados NULL, que de outra forma contém as informações de formato. Assim, um servidor deve rejeitar todos os links quentes para um item já vinculado e deve rejeitar todos os links para um item que tenha links quentes. Outra interpretação pode ser que o servidor altera o formato e o estado quente ou quente de um link quando um segundo link é solicitado para o mesmo item de dados.
Em geral, os aplicativos cliente não devem tentar estabelecer mais de um link por vez para um item de dados.
Iniciando um link de dados com o comando Colar link
Os aplicativos que oferecem suporte a links de dados quentes ou quentes geralmente oferecem suporte a um formato de área de transferência registrado chamado Link. Quando associado aos comandos Copiar e Colar Link do aplicativo, esse formato da área de transferência permite que o usuário estabeleça conversas DDE entre aplicativos simplesmente copiando um item de dados no aplicativo servidor e colando-o no aplicativo cliente.
Um aplicativo de servidor oferece suporte ao formato da área de transferência Link colocando na área de transferência uma cadeia de caracteres contendo os nomes do aplicativo, tópico e item quando o usuário escolhe o comando Copiar no menu Editar. A seguir está o formato de link padrão:
aplicativo**\0 tópico\0 item\0\0**
Um único caractere nulo separa os nomes e dois caracteres nulos encerram a cadeia de caracteres inteira.
Os aplicativos cliente e servidor devem registrar o formato da área de transferência Link, conforme mostrado:
cfLink = RegisterClipboardFormat("Link");
Um aplicativo cliente oferece suporte ao formato da área de transferência Link por meio de um comando Colar link em seu menu Editar. Quando o usuário escolhe esse comando, o aplicativo cliente analisa os nomes de aplicativo, tópico e item dos dados da área de transferência em formato de link. Usando esses nomes, o aplicativo cliente inicia uma conversa para o aplicativo e o tópico, se essa conversa ainda não existir. Em seguida, o aplicativo cliente envia uma mensagem de WM_DDE_ADVISE para o aplicativo de servidor, especificando o nome do item contido nos dados da área de transferência em formato de link.
A seguir está um exemplo da resposta de um aplicativo cliente quando o usuário escolhe o comando Colar link.
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;
}
Neste exemplo, o aplicativo cliente abre a área de transferência e determina se ela contém dados no formato Link (ou seja, cfLink) que havia registrado anteriormente. Caso contrário, ou se não for possível bloquear os dados na área de transferência, o cliente retornará.
Depois que o aplicativo cliente recupera um ponteiro para os dados da área de transferência, ele analisa os dados para extrair os nomes do aplicativo, tópico e item.
O aplicativo cliente determina se já existe uma conversa sobre o tópico entre ele e o aplicativo de servidor. Se uma conversa existir, o cliente verificará se já existe um link para o item de dados. Se esse link existir, o cliente exibirá uma caixa de mensagem para o usuário; caso contrário, ele chama sua própria função SendAdvise para enviar uma mensagem de WM_DDE_ADVISE ao servidor para o item.
Se uma conversa sobre o tópico ainda não existir entre o cliente e o servidor, o cliente primeiro chama sua própria função SendInitiate para transmitir a mensagem WM_DDE_INITIATE para solicitar uma conversa e, em segundo lugar, chama sua própria função FindServerGivenAppTopic para estabelecer a conversa com a janela que responde em nome do aplicativo do servidor. Depois que a conversa começar, o aplicativo cliente chama SendAdvise para solicitar o link.
Notificando o cliente de que os dados foram alterados
Quando o cliente estabelece um link usando a mensagem WM_DDE_ADVISE, com o membro fDeferUpd não definido (ou seja, igual a zero) na estrutura DDEDATA, o cliente solicitou que o servidor enviasse o item de dados cada vez que o valor do item fosse alterado. Nesses casos, o servidor renderiza o novo valor do item de dados no formato especificado anteriormente e envia ao cliente uma mensagem WM_DDE_DATA , conforme mostrado no exemplo a seguir.
// 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.
}
Neste exemplo, o cliente processa o valor do item conforme apropriado. Se o sinalizador fAckReq para o item estiver definido, o cliente enviará ao servidor uma mensagem de WM_DDE_ACK positiva.
Quando o cliente estabelece o vínculo, com o conjunto de membros fDeferUpd (ou seja, igual a 1), o cliente solicitou que apenas uma notificação, e não os dados em si, fosse enviada cada vez que os dados fossem alterados. Nesses casos, quando o valor do item é alterado, o servidor não renderiza o valor, mas simplesmente envia ao cliente uma mensagem de WM_DDE_DATA com um identificador de dados nulo, conforme ilustrado no exemplo a seguir.
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.
}
Conforme necessário, o cliente pode solicitar o valor mais recente do item de dados emitindo uma mensagem de WM_DDE_REQUEST normal ou pode simplesmente ignorar o aviso do servidor de que os dados foram alterados. Em ambos os casos, se fAckReq for igual a 1, espera-se que o cliente envie uma mensagem de WM_DDE_ACK positiva para o servidor.
Encerrando um link de dados
Se o cliente solicitar que um link de dados específico seja encerrado, ele enviará ao servidor uma mensagem WM_DDE_UNADVISE , conforme mostrado no exemplo a seguir.
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.
}
O servidor verifica se o cliente tem atualmente um link para o item específico nesta conversa. Se existir um link, o servidor envia ao cliente uma mensagem de WM_DDE_ACK positiva, o servidor não é mais obrigado a enviar atualizações sobre o item. Se nenhum link existir, o servidor enviará ao cliente uma mensagem de WM_DDE_ACK negativa.
A mensagem WM_DDE_UNADVISE especifica um formato de dados. Um formato de zero informa o servidor para parar todos os links para o item especificado, mesmo se vários hot links forem estabelecidos e cada um usar um formato diferente.
Para encerrar todos os links de uma conversa, o aplicativo cliente envia ao servidor uma mensagem de WM_DDE_UNADVISE com um átomo de item nulo. O servidor determina se a conversa tem pelo menos um link estabelecido no momento. Se existir um link, o servidor envia ao cliente uma mensagem de WM_DDE_ACK positiva, o servidor não precisa mais enviar atualizações na conversa. Se nenhum link existir, o servidor enviará ao cliente uma mensagem de WM_DDE_ACK negativa.
Executando comandos em um aplicativo de servidor
Os aplicativos podem usar a mensagem WM_DDE_EXECUTE para fazer com que um determinado comando ou série de comandos seja executado em outro aplicativo. Para fazer isso, o cliente envia ao servidor uma mensagem de WM_DDE_EXECUTE contendo um identificador para uma cadeia de caracteres de comando, conforme mostrado no exemplo a seguir.
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);
}
Neste exemplo, o servidor tenta executar a cadeia de caracteres de comando especificada. Se for bem-sucedido, o servidor envia ao cliente uma mensagem de WM_DDE_ACK positiva, caso contrário, envia uma mensagem de WM_DDE_ACK negativa. Essa mensagem WM_DDE_ACK reutiliza o identificador hCommand passado na mensagem WM_DDE_EXECUTE original.
Se a cadeia de execução de comando do cliente solicitar que o servidor seja encerrado, o servidor deverá responder enviando uma mensagem de WM_DDE_ACK positiva e, em seguida, postar uma mensagem de WM_DDE_TERMINATE antes de encerrar. Todos os outros comandos enviados com uma mensagem WM_DDE_EXECUTE devem ser executados de forma síncrona, ou seja, o servidor deve enviar uma mensagem de WM_DDE_ACK somente após concluir o comando com êxito.
Encerrando uma conversa
O cliente ou o servidor podem emitir uma mensagem de WM_DDE_TERMINATE para encerrar uma conversa a qualquer momento. Da mesma forma, os aplicativos cliente e servidor devem estar preparados para receber essa mensagem a qualquer momento. Um aplicativo deve encerrar todas as suas conversas antes de encerrá-las.
No exemplo a seguir, o aplicativo que encerra a conversa posta uma mensagem WM_DDE_TERMINATE.
PostMessage(hwndServerDDE, WM_DDE_TERMINATE,
(WPARAM) hwndClientDDE, 0);
Isso informa ao outro aplicativo que o aplicativo de envio não enviará mais mensagens e o destinatário pode fechar sua janela. Espera-se, em todos os casos, que o destinatário responda prontamente enviando uma mensagem WM_DDE_TERMINATE. O destinatário não deve enviar uma mensagem de WM_DDE_ACK negativa, ocupada ou positiva.
Depois que um aplicativo envia a mensagem WM_DDE_TERMINATE para o parceiro em uma conversa DDE, ele não deve responder às mensagens desse parceiro, pois o parceiro pode ter destruído a janela para a qual a resposta seria enviada.
Se um aplicativo receber uma mensagem DDE diferente de WM_DDE_TERMINATE depois de ter postado WM_DDE_TERMINATE, ele deverá liberar todos os objetos associados às mensagens recebidas, exceto os identificadores de dados para mensagens WM_DDE_DATA ou WM_DDE_POKE que não tenham o membro fRelease definido.
Quando um aplicativo está prestes a terminar, ele deve encerrar todas as conversas DDE ativas antes de concluir o processamento da mensagem WM_DESTROY . No entanto, se um aplicativo não encerrar suas conversas DDE ativas, o sistema encerrará todas as conversas DDE associadas a uma janela quando a janela for destruída. O exemplo a seguir mostra como um aplicativo de servidor encerra todas as conversas 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;
}