Partilhar via


Usando o intercâmbio dinâmico de dados

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

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 pode 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 aplicação de servidor potencial e sobre qualquer tópico potencial, fornecendo átomos de NULO (curinga) para a aplicação 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 NULL átomos, você não precisa usar o GlobalAddAtom e funções de 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 especial de janela –1 instrui o sistema a 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 de servidor têm a garantia de ter sido processadas pelo cliente no momento em que a chamada SendMessage retornou.

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.

lógica de resposta do aplicativo de servidor

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 seu cliente usa um átomo de NULL para o nome da aplicação ou o nome do tópico, deve esperar que o seu aplicativo receba confirmações de mais de um aplicativo servidor. Várias confirmações também podem vir de várias instâncias de um servidor DDE, mesmo que o seu aplicativo cliente não NULL use átomos. 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 a necessidade de encerrar e se reconectar com uma nova janela de cliente para cada conversa.

Transferir um único item

Depois que uma conversa DDE tiver sido 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 preferido para o item de dados solicitado.

O recetor (servidor) da mensagem WM_DDE_REQUEST normalmente deve excluir o átomo de item, mas se a chamada PostMessage falhar, o cliente deve excluir o átomo.

Se o servidor tiver acesso ao item solicitado e puder processá-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 o aplicativo cliente de que os dados estão em formato de texto. O cliente responde copiando o valor dos dados solicitados para o Value membro da estrutura DDEDATA. Depois que o servidor preenche 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 compactados no parâmetro lParam da mensagem pela funçãoPackDDElParam.

Se PostMessage falhar, o servidor deverá usar a função FreeDDElParam para liberar o parâmetro compactado lParam. O servidor também deve liberar o parâmetro lParam compactado para a mensagem WM_DDE_REQUEST que foi recebida.

Se o servidor não puder satisfazer a 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 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 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 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 negativa WM_DDE_ACK. Em vez disso, o cliente deve encerrar a conversa.

Se um cliente envia uma confirmação negativa em resposta a uma mensagem WM_DDE_DATA, o servidor é responsável por liberar a memória (mas não o parâmetro lParam) referenciado pela mensagem WM_DDE_DATA associada à confirmação negativa.

Se puder processar os dados, o cliente examinará o fAckReq membro da estruturaDDEDATApara determinar se o servidor solicitou que fosse informado 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 dos dados invalida o ponteiro para os mesmos, 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 de servidor solicitou que o cliente liberasse a memória que contém os dados; o cliente age em conformidade.

Ao receber uma mensagem negativa de WM_DDE_ACK, o cliente pode pedir 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, desce, se necessário, através 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 cada vez 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 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 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 envia 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 enviado pelo cliente. Em seguida, o servidor determina se ele suporta o item e se o item é processado no formato correto (ou seja, CF_TEXT). Se o item não for suportado e não for processado 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, neste caso, é correto enviar uma confirmação negativa porque se assume sempre que as mensagens WM_DDE_POKE têm o membro fAckReq definido. O servidor deve ignorar o membro.

Se um servidor envia uma confirmação negativa em resposta a uma mensagem WM_DDE_POKE, o cliente é responsável por liberar a memória (mas não o parâmetro lParam) referenciado pela mensagem WM_DDE_POKE associada à confirmação negativa.

Um aplicativo cliente pode usar 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 muda. Assim, um fluxo de dados permanente é estabelecido entre as duas aplicações; Esse fluxo de dados permanece em vigor até ser 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 fDeferUpd sinalizador da mensagem WM_DDE_ADVISE como FALSE. Isso direciona o aplicativo de servidor para enviar os dados para o cliente sempre que os dados forem alterados.

Se o servidor não conseguir atender à solicitação 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 muda 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 suportá-lo. Se um link quente tiver sido estabelecido para qualquer item de dados, o servidor poderá suportar apenas um formato de dados de cada vez. Isso ocorre porque a mensagem WM_DDE_DATA para um link ativo tem um identificador de dados NULL , que de outra forma conteria 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. Uma outra interpretação é que o servidor altera o formato e o estado quente ou morno 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 de cada vez para um item de dados.

As aplicações que suportam links de dados quentes ou mornos normalmente suportam um formato de clipboard registado 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 DDE entre aplicativos simplesmente copiando um item de dados no aplicativo de servidor e colando-o no aplicativo cliente.

Uma aplicação de servidor suporta o formato Link da área de transferência ao colocar na área de transferência uma cadeia de caracteres contendo os nomes da aplicação, do tópico e do item quando o utilizador escolhe o comando Copiar no menu Editar. Segue-se o formato de link padrão:

aplicativo**\0tópico\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 suporta o formato da área de transferência Link por meio de um comando Colar link no menu Editar. Quando o utilizador escolhe este comando, a aplicação cliente analisa os nomes da aplicação, do tópico e do item dos dados da área de transferência em formato de link. Usando esses nomes, a aplicação cliente inicia uma conversa para a aplicação e o tópico, caso essa conversa ainda não exista. Em seguida, a aplicação cliente envia uma mensagem WM_DDE_ADVISE para a aplicação de servidor, especificando o nome do item contido nos dados da área de transferência no formato 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 havia registrado anteriormente. Se não, ou se não conseguir bloquear os dados na área de transferência, o cliente retorna.

Depois que o aplicativo cliente obtém 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 existir uma conversa, o cliente verifica 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 seu próprio função SendAdvise para enviar uma mensagem WM_DDE_ADVISE para o servidor para o item.

Se ainda não existir uma conversa sobre o tópico entre o cliente e o servidor, o cliente primeiro chamará sua própria função de SendInitiate para transmitir a mensagem WM_DDE_INITIATE para solicitar uma conversa e, em segundo lugar, chamará sua própria função FindServerGivenAppTopic para estabelecer a conversa com a janela que responde em nome do aplicativo do servidor. Após o início da conversa, 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 estruturaDDEDATA, o cliente solicitou ao servidor enviar o item de dados cada vez que o valor do item for alterado. Nesses casos, o servidor processa 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 positiva de WM_DDE_ACK.

Quando o cliente estabelece o link, com o fDeferUpd conjunto de membros (ou seja, igual a 1), o cliente solicitou que apenas uma notificação, não os dados em si, seja enviada cada vez que os dados forem alterados. Nesses casos, quando o valor do item é alterado, o servidor não processa 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 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.

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 tem atualmente um link para o item específico nesta conversa. Se existir um link, o servidor envia ao cliente uma mensagem positiva de WM_DDE_ACK. 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 que vários hot links sejam estabelecidos e cada um use um formato diferente.

Para encerrar todos os links de 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 envia 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 sejam executados em outro aplicativo. Para fazer isso, o cliente envia ao servidor uma mensagem WM_DDE_EXECUTE contendo um identificador para uma cadeia 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. Esta mensagem WM_DDE_ACK reutiliza o identificador hCommand passado na mensagem WM_DDE_EXECUTE original.

Se a cadeia de execução de comandos do cliente solicitar que o servidor seja encerrado, o servidor deverá responder enviando uma mensagem positiva de WM_DDE_ACK e, em seguida, publicar 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 após concluir com êxito o comando.

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 suas conversas antes de ser encerrado.

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 o outro aplicativo que o aplicativo de envio não enviará mais mensagens e 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 negativa, ocupada ou positiva WM_DDE_ACK.

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 deve 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 fRelease conjunto de membros.

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; 
}