Usando dados dinâmicos Exchange

Esta seção tem exemplos de código nas seguintes tarefas:

Iniciando uma conversa

Para iniciar uma conversa do DDE (Dynamic Data Exchange), o cliente envia uma mensagem de 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 do 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 em potencial fornecendo átomos NULL (curinga) para o aplicativo e o tópico.

O exemplo a seguir ilustra como o cliente inicia uma conversa, em que 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 o aplicativo usar átomos NULL , você não precisará usar as funções GlobalAddAtom e GlobalDeleteAtom . Neste exemplo, o aplicativo cliente cria dois átomos globais que contêm 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 a todos os outros aplicativos ativos. O 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 de servidor têm a garantia de ter sido processadas pelo cliente até o momento em que a chamada SendMessage tiver retornado.

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.

server application response logic

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(atomApplication); 
    } 
 
    GlobalDeleteAtom(atomTopic); 
} 
 
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 usar um atom 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 que seu aplicativo cliente não use atoms. Um servidor sempre deve 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 acompanhar várias conversas. Isso permite que uma única janela do cliente processe várias conversas sem a necessidade de 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 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 atom do item, mas se a chamada PostMessage falhar, o cliente deverá excluir o átomo.

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 de 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 como 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 desbloqueará os dados e criará um atom global que contém o nome do item de dados.

Por fim, 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 o PostMessage falhar, o servidor deverá usar a função FreeDDElParam para liberar o parâmetro lParam empacotado. O servidor também deve liberar o parâmetro lParam empacotado para o WM_DDE_REQUEST mensagem 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 de 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 for CF_TEXT (ou se o cliente não puder bloquear a memória dos 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 puder bloquear um identificador de dados porque o identificador contém o membro fAckReq , o cliente não deverá 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 puder processar os dados, o cliente examinará 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 desbloquear 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 ao cliente para liberar a memória que contém os dados; o cliente atua adequadamente.

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 primeiro solicitará o formato mais complexo que ele pode dar suporte 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 der suporte ao item Formatos do tópico do sistema, o cliente poderá determinar uma vez que formatos de área de transferência o servidor dá suporte, 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

O envio de dados usando uma mensagem de WM_DDE_POKE é essencialmente o mesmo que enviá-los usando WM_DDE_DATA, exceto que WM_DDE_POKE é enviado do cliente para o servidor.

 

Se o servidor for capaz de 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 dá suporte ao item e se o item é renderizado no formato correto (ou seja, CF_TEXT). Se o item não tiver suporte e não for renderizado no formato correto ou se o servidor não puder bloquear a memória dos 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 sempre são consideradas com o membro fAckReq definido. O servidor deve ignorar o membro.

Se um servidor enviar uma confirmação negativa em resposta a uma mensagem de 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.

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 em vigor até que seja explicitamente desconectado.

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 orienta o aplicativo de servidor a enviar os dados para o cliente sempre que os dados forem alterados.

Se o servidor não puder atender à solicitação WM_DDE_ADVISE , ele enviará ao cliente uma mensagem de WM_DDE_ACK negativa. No entanto, se o servidor tiver acesso ao item e puder renderizá-lo no formato solicitado, o servidor anotará o novo link (recuperando os sinalizadores especificados no parâmetro hOptions ) e enviará ao cliente uma mensagem de WM_DDE_ACK positiva. A partir daí, 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 link 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 dar suporte a ele. Se um link quente tiver sido estabelecido para qualquer item de dados, o servidor poderá dar suporte a apenas um formato de dados por vez. Isso ocorre porque a mensagem de WM_DDE_DATA para um link quente tem um identificador de dados NULL , que, caso contrário, contém as informações de formato. Portanto, 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.

Aplicativos que dão suporte a links de dados quentes ou quentes normalmente dão suporte a um formato de área de transferência registrado chamado Link. Quando associado aos comandos Copiar e Colar Link do aplicativo, esse formato de área de transferência permite que o usuário estabeleça conversas de DDE entre aplicativos simplesmente copiando um item de dados no aplicativo do servidor e colando-o no aplicativo cliente.

Um aplicativo de servidor dá suporte ao formato de área de transferência link colocando na área de transferência uma cadeia de caracteres que contém o aplicativo, o tópico e os nomes de item quando o usuário escolhe o comando Copiar no menu Editar . A seguir está o formato de link padrão:

application**\0topic\0item\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 dá suporte ao formato de á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 o aplicativo, o tópico e os nomes de itens dos dados da área de transferência de formato de link. Usando esses nomes, o aplicativo cliente iniciará uma conversa para o aplicativo e o tópico, se essa conversa ainda não existir. Em seguida, o aplicativo cliente envia uma mensagem WM_DDE_ADVISE para o aplicativo de servidor, especificando o nome do item contido nos dados da área de transferência de 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 ele contém dados no formato Link (ou seja, cfLink) que ele havia registrado anteriormente. Caso contrário, ou se não puder 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 o aplicativo, o tópico e os nomes dos itens.

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 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 chamará 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 de servidor. Depois que a conversa for iniciada, o aplicativo cliente chamará 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 sempre que o valor do item for 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 do item estiver definido, o cliente enviará ao servidor uma mensagem de WM_DDE_ACK positiva.

Quando o cliente estabelece o link, com o conjunto de membros fDeferUpd (ou seja, igual a 1), o cliente solicitou que apenas uma notificação, não os dados em si, fosse enviada sempre 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 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 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.

Se o cliente solicitar que um link de dados específico seja encerrado, o cliente 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 atualmente tem um link para o item específico nesta conversa. Se existir um link, o servidor enviará ao cliente uma mensagem de WM_DDE_ACK positiva; o servidor não é mais necessário para 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 interromper todos os links para o item especificado, mesmo que vários links quentes sejam estabelecidos e cada um use um formato diferente.

Para encerrar todos os links para uma conversa, o aplicativo cliente envia ao servidor uma mensagem WM_DDE_UNADVISE com um átomo de item nulo. O servidor determina se a conversa tem pelo menos um link atualmente estabelecido. Se existir um link, o servidor enviará ao cliente uma mensagem de WM_DDE_ACK positiva; o servidor, então, 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 realizado em outro aplicativo. Para fazer isso, o cliente envia ao servidor uma mensagem 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 ele for bem-sucedido, o servidor enviará ao cliente uma mensagem de WM_DDE_ACK positiva; caso contrário, ele envia uma mensagem de WM_DDE_ACK negativa. Esta mensagem WM_DDE_ACK reutiliza o identificador hCommand passado na mensagem de WM_DDE_EXECUTE original.

Se a cadeia de caracteres 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 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 WM_DDE_ACK somente depois de concluir o comando com êxito.

Encerrando uma conversa

O cliente ou o servidor podem emitir uma mensagem 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 conversas antes de desligar.

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 que o destinatário pode fechar sua janela. Espera-se 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 enviar a mensagem WM_DDE_TERMINATE para o parceiro em uma conversa de 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 postar 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 têm o membro fRelease definido.

Quando um aplicativo está prestes a ser encerrado, 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á 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 de 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; 
}