Compartir a través de


Utilizar símbolos (token) y API de metadatos

Actualización: noviembre 2007

Las API de metadatos se pueden llamar desde C++. La manera en que se utilizan las API de metadatos depende en parte del tipo de cliente que las utiliza. La mayoría de los clientes de API pertenecen una de estas dos categorías:

  • Compiladores, como el compilador de Visual C++ 2005, que generan archivos .obj intermedios y después utilizan una fase de vinculación independiente para combinar las unidades de compilación individuales en un único archivo portable ejecutable (PE) de destino.

  • Herramientas de desarrollo rápido de aplicaciones (RAD) que administran todas las estructuras de datos y código en el entorno de la herramienta hasta el momento de la compilación, que es cuando generan y emiten un archivo PE en un solo paso.

Es posible que otros clientes no utilicen las API de metadatos ni de una manera ni de otra. Algunas herramientas pueden permitir que el motor de metadatos realice las optimizaciones, pero quizás no les interese la información de reasignación de símbolos (tokens). Alternativamente, podrían desear información de reasignación sólo para algunos tipos de símbolos (tokens). De hecho, es posible que un compilador no realice optimizaciones ni siquiera al emitir un archivo .obj.

Proceso de compilación y vinculación

Como sucede en el tipo de interacción con compilación y vinculación, un compilador front-end utiliza la API IMetaDataDispenserEx para establecer un ámbito de metadatos en memoria y, a continuación, utiliza la API IMetaDataEmit para declarar los tipos y miembros, trabajando con las abstracciones de metadatos descritas en Información general sobre símbolos (token) de metadatos. Sin embargo, el front-end no podrá proporcionar información de implementación de métodos (por ejemplo, si la implementación es administrada o no administrada o si el código es MSIL o nativo) ni información acerca de la dirección virtual relativa (RVA), porque dicha información no se puede determinar en tiempo de compilación. En su lugar, el vinculador necesitará proporcionar esta información posteriormente, cuando el código se haya compilado y emitido en el archivo PE.

El problema es que la herramienta back-end necesita obtener información acerca del tamaño de almacenamiento de destino del archivo binario de metadatos para dejarle espacio en el archivo PE. Sin embargo, la herramienta no podrá guardar el archivo binario de metadatos en el archivo PE hasta que no se conozcan las RVA de método y las RVA de los miembros de datos estáticos en el nivel de módulo y se emitan a los metadatos. Para calcular correctamente el tamaño de almacenamiento de destino, el motor de metadatos primero debe realizar las optimizaciones previas al almacenamiento que sean necesarias, ya que estas optimizaciones hacen que el archivo binario de destino sea más pequeño. Las optimizaciones pueden consistir en la ordenación de las estructuras de datos para que la búsqueda sea más rápida o en el enlace en tiempo de compilación de los símbolos (tokens) mdTypeRefy mdMemberRef cuando se hace referencia a un tipo o miembro declarado en el ámbito actual. Estas optimizaciones pueden tener como resultado la reasignación de los símbolos (tokens) de metadatos que la herramienta debe reutilizar para emitir la información de implementación y de RVA. En consecuencia, la herramienta y el motor de metadatos deben colaborar para realizar el seguimiento de las reasignaciones de símbolos (tokens).

Por tanto, el orden de las llamadas para conservar los metadatos durante la compilación es el siguiente:

  1. IMetaDataEmit::SetHandler, para proporcionar una interfaz IUnknown que el motor de metadatos puede utilizar para consultar IID_IMapToken, que se utiliza para notificar al cliente las reasignaciones de símbolos (tokens). Se puede llamar a SetHandler en cualquier momento después de crear el ámbito de metadatos, aunque siempre antes de llamar a IMetaDataEmit::GetSaveSize.

  2. IMetaDataEmit::GetSaveSize, para obtener el tamaño de almacenamiento del archivo binario de metadatos. GetSaveSize utiliza la interfaz IMapToken proporcionada en IMetaDataEmit::SetHandler para notificar al cliente las reasignaciones de símbolos (tokens). Si no se utilizase SetHandler para proporcionar una interfaz IMapToken, no se realizarían optimizaciones. Esto permite que un compilador que esté emitiendo un archivo .obj intermedio omita las optimizaciones innecesarias, que probablemente se volverán a realizar después de la fase de vinculación y combinación.

  3. IMetaDataEmit::Save, para conservar el archivo binario de datos después de utilizar IMetaDataEmit::SetRVA y otros métodos IMetaDataEmit, según convenga, con el fin de emitir los metadatos de implementación finales.

El siguiente nivel de complicación tiene lugar en la fase de vinculación, cuando varias unidades de compilación se combinan en un archivo PE integrado. En este caso, no sólo es necesario combinar los ámbitos de metadatos, sino que las RVA volverán a cambiar cuando se emita el nuevo archivo PE. En la fase de combinación, el método IMetaDataEmit::Merge, que funciona con un único ámbito de importación y de emisión en cada llamada, reasigna los símbolos (tokens) de metadatos del ámbito de importación al ámbito de emisión. Además, el proceso de combinación se puede encontrar con errores continuos que debe poder enviar al cliente. Una vez finalizada la combinación, para la emisión del archivo PE final se requiere una llamada a IMetaDataEmit::GetSaveSize y otra fase de reasignación de símbolos (tokens).

El orden de las llamadas para que el vinculador emita y conserve los metadatos es el siguiente:

  1. IMetaDataEmit::SetHandler, para proporcionar una interfaz IUnknown que pueda utilizar el motor de metadatos para consultar no sólo IID_IMapToken como antes, sino también IID_IMetaDataError. La última interfaz se utiliza para notificar al cliente cualquier error continuado que surja de la combinación.

  2. IMetaDataEmit::Merge, para combinar un ámbito de metadatos especificado con el ámbito de emisión actual. Merge utiliza la interfaz IMapToken para notificar al cliente las reasignaciones de símbolos (tokens) y utiliza IMetaDataError para notificar al cliente los errores continuados.

  3. IMetaDataEmit::GetSaveSize, para obtener el tamaño de almacenamiento del archivo binario de metadatos. GetSaveSize utiliza la interfaz IMapToken proporcionada en IMetaDataEmit::SetHandler para notificar al cliente las reasignaciones de símbolos (tokens). Una herramienta debe estar preparada para controlar las reasignaciones de símbolos (tokens) en Merge y de nuevo en GetSaveSize una vez realizadas las optimizaciones de formato. La última notificación de un símbolo (token) representa la asignación final que debe tener en cuenta la herramienta.

  4. IMetaDataEmit::Save, para conservar el archivo binario de metadatos, después de utilizar IMetaDataEmit::SetRVA y otros métodos de IMetaDataEmit, según sea necesario, para emitir los metadatos de implementación finales.

Proceso de las herramientas RAD

Como sucede en el tipo de interacción con compilación y vinculación, una herramienta RAD utiliza la interfaz IMetaDataDispenserEx para establecer un ámbito de metadatos en memoria y, a continuación, utiliza la interfaz IMetaDataEmit para declarar los tipos y miembros, trabajando con las abstracciones de metadatos descritas en Información general sobre símbolos (token) de metadatos.

A diferencia de lo que sucede en el proceso de compilación y vinculación, la herramienta RAD suele emitir el archivo PE en un único paso. Probablemente emitirá la información de declaración e implementación en un solo paso y seguramente nunca necesitará llamar a IMetaDataEmit::Merge. Por tanto, la única razón por la cual la herramienta RAD puede necesitar controlar la complejidad de las reasignaciones de símbolos (tokens) es aprovechar las optimizaciones previas al almacenamiento que IMetaDataEmit::GetSaveSize actualmente realiza.

En general, una herramienta que puede emitir metadatos totalmente optimizados no necesita el motor de metadatos para emitir un archivo razonablemente optimizado. Sin embargo, las implementaciones futuras del motor de metadatos y del formato de archivo pueden hacer que algunas estrategias de optimización se queden obsoletas y por ello es necesario disponer de un conjunto claro de reglas para la emisión de metadatos optimizados.

Después de emitir las declaraciones de metadatos y la información de implementación, el orden de las llamadas es el siguiente:

  1. IMetaDataEmit::SetRVA y otros métodos IMetaDataEmit, según sea necesario, para emitir los últimos metadatos de implementación.

  2. IMetaDataEmit::Save, para conservar el archivo binario de metadatos.

Vea también

Otros recursos

Información general sobre metadatos