Conceptos básicos (DDE)

Estos conceptos son clave para comprender el intercambio dinámico de datos (DDE) y la biblioteca de administración de Intercambio de datos dinámicos (DDEML).

Interacción de cliente y servidor

DDE siempre se produce entre una aplicación cliente y una aplicación de servidor. La aplicación cliente DDE inicia el intercambio estableciendo una conversación con el servidor para enviar transacciones al servidor. Una transacción es una solicitud de datos o servicios. La aplicación de servidor DDE responde a las transacciones proporcionando datos o servicios al cliente. Por ejemplo, una aplicación gráfica puede contener un gráfico de barras que represente los beneficios trimestrales de una empresa, pero los datos para el gráfico de barras pueden estar contenidos en una aplicación de hoja de cálculo. Para obtener las cifras de beneficios más recientes, la aplicación de gráficos (el cliente) puede establecer una conversación con la aplicación de hoja de cálculo (el servidor). Después, la aplicación de gráficos puede enviar una transacción a la aplicación de hoja de cálculo, solicitando las cifras de beneficios más recientes.

Un servidor puede tener muchos clientes al mismo tiempo y un cliente puede solicitar datos de varios servidores. Una aplicación también puede ser tanto un cliente como un servidor. El cliente o el servidor pueden finalizar la conversación en cualquier momento.

Transacciones y la función de devolución de llamada DDE

DDEML notifica a una aplicación sobre la actividad DDE que afecta a la aplicación mediante el envío de transacciones a la función de devolución de llamada DDE de la aplicación. Una transacción DDE es similar a un mensaje que es una constante con nombre acompañada de otros parámetros que contienen información adicional sobre la transacción.

DDEML pasa una transacción a una función de devolución de llamada DDE definida por la aplicación que lleva a cabo una acción adecuada para el tipo de transacción. Por ejemplo, cuando una aplicación cliente intenta establecer una conversación con una aplicación de servidor, el cliente llama a la función DdeConnect. Esta función hace que DDEML envíe una transacción XTYP_CONNECT a la función de devolución de llamada DDE del servidor. La función de devolución de llamada puede permitir la conversación devolviendo TRUE a DDEML, o bien puede denegar la conversación devolviendo FALSE. Para obtener una explicación detallada de las transacciones, consulte Administración de transacciones.

Nombres de servicio, nombres de temas y nombres de elementos

Un servidor DDE usa un nombre de servicio de jerarquía de tres niveles (denominado "nombre de aplicación" en la documentación anterior de DDE), el nombre del tema y el nombre del elemento para identificar de forma única una unidad de datos que el servidor puede intercambiar durante una conversación.

Un nombre de servicio es una cadena a la que una aplicación de servidor responde cuando un cliente intenta establecer una conversación con el servidor. Un cliente debe especificar este nombre de servicio para establecer una conversación con el servidor. Aunque un servidor puede responder a muchos nombres de servicio, la mayoría de los servidores solo responden a un nombre.

Un nombre de tema es una cadena que identifica un contexto de datos lógico. En el caso de los servidores que operan en documentos basados en archivos, los nombres de tema suelen ser nombres de archivo; para otros servidores, son otras cadenas específicas de la aplicación. Un cliente debe especificar un nombre de tema junto con el nombre de servicio de un servidor cuando intenta establecer una conversación con un servidor.

Un nombre de elemento es una cadena que identifica una unidad de datos que un servidor puede pasar a un cliente durante una transacción. Por ejemplo, un nombre de elemento puede identificar un entero, una cadena, varios párrafos de texto o un mapa de bits.

Los nombres de servicio, tema y elemento permiten al cliente establecer una conversación con un servidor y recibir datos del servidor.

Tema del sistema

El tema Sistema proporciona un contexto para obtener información de interés general para cualquier cliente DDE. Se recomienda que las aplicaciones de servidor admitan el tema Sistema en todo momento. El tema Sistema se define en archivo de encabezado DDEML.H como SZDDESYS_TOPIC.

Para determinar qué servidores están presentes y los tipos de información que pueden proporcionar, una aplicación cliente puede solicitar una conversación en el tema Sistema al iniciarse, estableciendo el nombre del dispositivo en NULL. Estas conversaciones con comodines son costosas en términos de rendimiento del sistema, por lo que deben reducirse al mínimo. Para obtener más información sobre cómo iniciar conversaciones de DDE, consulte Administración de conversaciones.

Un servidor debe admitir los siguientes nombres de elemento dentro del tema Sistema y cualquier otro nombre de elemento que sea útil para un cliente.

Elemento Descripción
SZDDE_ITEM_ITEMLIST Lista de los elementos admitidos en un tema que no es del sistema. (Esta lista puede variar de un momento a otro y de un tema a otro).
SZDDESYS_ITEM_FORMATS Una lista delimitada por tabulaciones de cadenas que representan todos los formatos del Portapapeles potencialmente admitidos por la aplicación de servicio. Las cadenas que representan formatos predefinidos del Portapapeles son equivalentes a los valores de CF_ con el prefijo "CF_" quitado. Por ejemplo, el formato CF_TEXT se representa mediante la cadena "TEXT". Estas cadenas deben estar en mayúsculas para identificarlas aún más como formatos predefinidos. La lista de formatos debe aparecer en el orden de la mayoría de los contenidos enriquecidos a menos enriquecidos en el contenido. Para obtener más información acerca de los formatos del portapapeles y los datos de representación, consulte Portapapeles.
SZDDESYS_ITEM_HELP Información legible por el usuario de interés general. Este elemento debe contener, como mínimo, información sobre cómo usar las características DDE de la aplicación de servidor. Esta información puede incluir, pero no se limita a, cómo especificar elementos dentro de temas, qué cadenas de ejecución puede realizar el servidor, qué transacciones de escritura en memoria se permiten y cómo encontrar ayuda en otros elementos de tema del sistema.
SZDDESYS_ITEM_RTNMSG Detalles de compatibilidad con el mensaje de WM_DDE_ACK usado más recientemente. Este elemento es útil cuando se necesitan más de 8 bits de datos de devolución específicos de la aplicación.
SZDDESYS_ITEM_STATUS Indicación del estado actual del servidor. Normalmente, este elemento solo admite el formato CF_TEXT y contiene la cadena Ready o Busy.
SZDDESYS_ITEM_SYSITEMS Lista de los elementos admitidos en el tema Sistema por este servidor.
SZDDESYS_ITEM_TOPICS Lista de los temas admitidos por el servidor en el momento actual. (Esta lista puede variar de un momento a otro).

Estos nombres de elemento son valores definidos en el archivo de encabezado DDEML.H. Para obtener identificadores de cadena para estas cadenas, una aplicación debe usar las funciones de administración de cadenas DDEML, igual que para cualquier otra cadena de una aplicación DDEML. Para obtener más información acerca de la administración de cadenas, consulte Administración de cadenas.

Inicialización

Antes de llamar a cualquier otra función DDEML, una aplicación debe llamar a la función DdeInitialize. DdeInitialize obtiene un identificador de instancia para la aplicación, registra la función de devolución de llamada DDE de la aplicación con DDE y especifica las marcas de filtro de transacción para la función de devolución de llamada.

Cada instancia de una aplicación o un archivo DLL debe pasar su identificador de instancia como parámetro idInst a cualquier otra función DDEML que lo requiera. El propósito de varias instancias DDEML es admitir archivos DLL que deben usar DDEML al mismo tiempo que una aplicación lo está usando. Una aplicación no debe usar más de una instancia de DDEML.

Los filtros de transacción optimizan el rendimiento del sistema, ya que evitan que DDEML pase transacciones no deseadas a la función de devolución de llamada DDE de la aplicación. Una aplicación establece los filtros de transacción en el parámetro DdeInitialize ufCmd. Una aplicación debe especificar una marca de filtro de transacción para cada tipo de transacción que no procesa en su función de devolución de llamada. Una aplicación puede cambiar sus filtros de transacción con una llamada posterior a DdeInitialize. Para obtener más información acerca de las transacciones, consulte Administración de transacciones.

En el ejemplo siguiente se muestra cómo inicializar una aplicación para usar DDEML.

DWORD idInst = 0; 
HINSTANCE hinst; 
 
DdeInitialize(&idInst,         // receives instance identifier 
    (PFNCALLBACK) DdeCallback, // pointer to callback function 
    CBF_FAIL_EXECUTES |        // filter XTYPE_EXECUTE 
    CBF_SKIP_ALLNOTIFICATIONS, // filter notifications 
    0); 

Una aplicación debe llamar a la función DdeUninitialize cuando ya no va a usar DDEML. Esta función finaliza las conversaciones abiertas actualmente para la aplicación y libera los recursos DDEML asignados para la aplicación.

Callback (Función)

Una aplicación que usa DDEML debe proporcionar una función de devolución de llamada que procese los eventos DDE que afectan a la aplicación. DDEML notifica a una aplicación de tales eventos mediante el envío de transacciones a la función de devolución de llamada DDE de la aplicación. Las transacciones que recibe una función de devolución de llamada dependen de qué filtro de devolución de llamada marca la aplicación especificada en DdeInitialize y si la aplicación es un cliente, un servidor o ambos. Para obtener más información, consulte DdeCallback.

En el ejemplo siguiente se muestra la estructura general de una función de devolución de llamada para una aplicación cliente típica.

HDDEDATA CALLBACK DdeCallback(uType, uFmt, hconv, hsz1, 
    hsz2, hdata, dwData1, dwData2) 
UINT uType;       // transaction type 
UINT uFmt;        // clipboard data format 
HCONV hconv;      // handle to conversation 
HSZ hsz1;         // handle to string 
HSZ hsz2;         // handle to string 
HDDEDATA hdata;   // handle to global memory object 
DWORD dwData1;    // transaction-specific data 
DWORD dwData2;    // transaction-specific data 
{ 
    switch (uType) 
    { 
        case XTYP_REGISTER: 
        case XTYP_UNREGISTER: 
            . 
            . 
            . 
            return (HDDEDATA) NULL; 
 
        case XTYP_ADVDATA: 
            . 
            . 
            . 
            return (HDDEDATA) DDE_FACK; 
 
        case XTYP_XACT_COMPLETE: 
            
            // 
            
            return (HDDEDATA) NULL; 
 
        case XTYP_DISCONNECT: 
            
            // 
            
            return (HDDEDATA) NULL; 
 
        default: 
            return (HDDEDATA) NULL; 
    } 
} 

El parámetro uType especifica el tipo de transacción enviado a la función de devolución de llamada por DDEML. Los valores de los parámetros restantes dependen del tipo de transacción. Los tipos de transacción y los eventos que los generan se describen en los temas siguientes. Para obtener información detallada sobre cada tipo de transacción, consulte Administración de transacciones.

Gestión de cadenas

Para llevar a cabo una tarea DDE, muchas funciones DDEML requieren acceso a cadenas. Por ejemplo, un cliente debe especificar un nombre de servicio y un nombre de tema cuando llama a la función DdeConnect para solicitar una conversación con un servidor. Una aplicación especifica una cadena pasando un identificador de cadena (HSZ) en lugar de un puntero en una función DDEML. Un identificador de cadena es un valor DWORD asignado por el sistema que identifica una cadena.

Una aplicación puede obtener un identificador de cadena para una cadena determinada mediante una llamada a la función DdeCreateStringHandle. Esta función registra la cadena con el sistema y devuelve un identificador de cadena a la aplicación. La aplicación puede pasar el identificador a las funciones DDEML que deben tener acceso a la cadena. En el ejemplo siguiente se obtienen identificadores de cadena para la cadena de tema del sistema y la cadena de nombre del servicio.

HSZ hszServName; 
HSZ hszSysTopic; 
hszServName = DdeCreateStringHandle( 
    idInst,         // instance identifier 
    "MyServer",     // string to register 
    CP_WINANSI);    // Windows ANSI code page 
 
hszSysTopic = DdeCreateStringHandle( 
    idInst,         // instance identifier 
    SZDDESYS_TOPIC, // System topic 
    CP_WINANSI);    // Windows ANSI code page 
    

El parámetro idInst del ejemplo anterior especifica el identificador de instancia obtenido por la función DdeInitialize.

La función de devolución de llamada DDE de una aplicación recibe uno o varios identificadores de cadena durante la mayoría de las transacciones DDE. Por ejemplo, un servidor recibe dos identificadores de cadena durante la transacción de XTYP_REQUEST: una identifica una cadena que especifica un nombre de tema y la otra identifica una cadena que especifica un nombre de elemento. Una aplicación puede obtener la longitud de la cadena que corresponde a un identificador de cadena y copiar la cadena en un búfer definido por la aplicación llamando a la función DdeQueryString, como se muestra en el ejemplo siguiente.

DWORD idInst; 
DWORD cb; 
HSZ hszServ; 
PSTR pszServName; 
cb = DdeQueryString(idInst, hszServ, (LPSTR) NULL, 0, 
    CP_WINANSI) + 1; 
pszServName = (PSTR) LocalAlloc(LPTR, (UINT) cb); 
DdeQueryString(idInst, hszServ, pszServName, cb, CP_WINANSI); 

Un identificador de cadena específico de instancia no se puede asignar desde el identificador de cadena a la cadena y volver al identificador de cadena. Por ejemplo, aunque DdeQueryString crea una cadena a partir de un identificador de cadena y, a continuación, DdeCreateStringHandle crea un identificador de cadena a partir de esa cadena, los dos identificadores no son los mismos, como se muestra en el ejemplo siguiente.

DWORD idInst; 
DWORD cb; 
HSZ hszInst, hszNew; 
PSZ pszInst; 
DdeQueryString(idInst, hszInst, pszInst, cb, CP_WINANSI); 
hszNew = DdeCreateStringHandle(idInst, pszInst, CP_WINANSI); 
// hszNew != hszInst ! 

Para comparar los valores de dos identificadores de cadena, use la función DdeCmpStringHandles.

Un identificador de cadena pasado a la función de devolución de llamada DDE de una aplicación no es válido cuando la función de devolución de llamada devuelve. Una aplicación puede guardar un identificador de cadena para su uso después de que la función de devolución de llamada devuelva mediante la función DdeKeepStringHandle.

Cuando una aplicación llama a DdeCreateStringHandle, el sistema escribe la cadena especificada en una tabla de cadenas y genera un identificador que usa para acceder a la cadena. El sistema también mantiene un recuento de uso para cada cadena de la tabla de cadenas.

Cuando una aplicación llama a DdeCreateStringHandle y especifica una cadena que ya existe en la tabla, el sistema incrementa el recuento de uso en lugar de agregar otra aparición de la cadena. (Una aplicación también puede incrementar el recuento de uso mediante DdeKeepStringHandle.) Cuando una aplicación llama a la función DdeFreeStringHandle, el sistema disminuye el recuento de uso.

Se quita una cadena de la tabla cuando su recuento de uso es igual a cero. Dado que más de una aplicación puede obtener el identificador de una cadena determinada, una aplicación no debe liberar un identificador de cadena más veces de lo que ha creado o conservado el identificador. De lo contrario, la aplicación puede hacer que la cadena se elimine de la tabla, negando a otras aplicaciones el acceso a la cadena.

Las funciones de gestión de cadenas DDEML se basan en el gestor de átomos y están sujetas a las mismas restricciones de tamaño que estos.

DDEML y subprocesos

La función DdeInitialize registra una aplicación con DDEML, creando una instancia de DDEML. Una instancia DDEML se basa en un subproceso y está asociada al subproceso que llamó a DdeInitialize.

Todas las llamadas de función DDEML para objetos que pertenecen a una instancia de DDEML deben realizarse desde el mismo subproceso que llamó a DdeInitialize para crear la instancia. Si llama a una función DDEML desde otro subproceso, se producirá un error en la función. No se puede acceder a una conversación DDEML desde un subproceso distinto del que asignó la conversación.