Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Importante
La información de este tema se aplica a todas las versiones de Windows 10 y versiones posteriores. Aquí nos referiremos a esas versiones como "Windows", llamando a las excepciones cuando sea necesario.
Los conjuntos de API se basan en la compatibilidad con el sistema operativo en el cargador de biblioteca para introducir una redirección de espacio de nombres de módulo en el proceso de enlace de biblioteca. Un nombre de contrato del conjunto de API no asigna un nombre a un archivo. El cargador realiza una redirección en tiempo de ejecución desde ese nombre de contrato al binario host que contiene la implementación.
Cuando el cargador encuentra una dependencia en un conjunto de API en tiempo de ejecución, consulta los datos de configuración de la imagen para identificar el binario del host para ese conjunto de API. Estos datos de configuración se denominan esquema del conjunto de API de . El esquema se ensambla como una propiedad del sistema operativo y la asignación entre conjuntos de API y archivos binarios puede diferir en función de los archivos binarios que se incluyan en un dispositivo determinado. El esquema es lo que permite que una función importada en un solo binario se enrute correctamente en diferentes dispositivos, incluso cuando se ha cambiado el nombre del módulo que hospeda la implementación, se ha dividido o refactorizado.
Cómo llegan las importaciones a una implementación
Un binario puede llegar a una implementación de conjunto de API de dos maneras, decidido por el nombre en su tabla de importación:
- Importación de conjunto de API directa. El archivo binario importa un nombre de contrato del conjunto de API. El cargador resuelve ese nombre a través del esquema del conjunto de API en el binario host del dispositivo actual.
- Importación de módulo heredado. El archivo binario importa un nombre de módulo de Windows heredado, como samplefeature.dll. En una edición que incluye ese módulo, el cargador se enlaza directamente a él. En una edición que lo ha reemplazado, un reenviador inverso que lleva el mismo nombre redirige la importación a un conjunto de API, que el cargador resuelve a través del esquema.
Cuál de esos nombres termina en la tabla de importación normalmente viene determinado por la biblioteca con la que se vincula en lugar del origen que escribe. Consulte Windows bibliotecas paraguas.
Prefiere el nombre del contrato del conjunto de API para el código que tiene como destino las versiones actuales de Windows. El cargador lo resuelve directamente en el host, sin reenviador entre sí. Importe el nombre del módulo heredado cuando necesite un único binario que también se ejecute en Windows versiones publicadas antes de que existiera el conjunto de API. El reenvío inverso mantiene ese binario trabajando en ediciones en las que se ha reemplazado el módulo heredado.
Importación de conjunto de API directa
La resolución es una secuencia de tres pasos:
- El archivo binario importa un nombre de contrato del conjunto de API o pasa uno a LoadLibrary.
- El cargador busca el contrato en el esquema del conjunto de API en el dispositivo actual y busca el binario host al que el esquema lo asigna.
- El cargador carga ese binario host y enlaza la función importada a la exportación del host.
Dado que la asignación reside en el esquema en lugar de en el sistema de archivos, la misma importación puede resolverse en archivos binarios diferentes en distintos dispositivos:
| Dispositivo |
api-win-core-samplefeature se asigna a |
|---|---|
| Un dispositivo que incluye la característica | samplefeature.dll |
| Un dispositivo que envía una implementación refactorizado | samplefeaturecore.dll |
| Un dispositivo que no incluye la característica | No asignado |
Los samplefeature nombres que se usan aquí son nombres ilustrativos para un componente de Windows ficticio.
El binario de consumo no es consciente del host al que estaba enlazado. Ese es el punto del mecanismo: el contrato es estable, mientras que el módulo que implementa es libre de cambiar de un dispositivo a otro.
Una importación de un nombre de contrato se resuelve en una sola operación, sin ningún módulo de reenviador intermedio implicado. Es el formulario más eficaz y la ruta de acceso normal para el código escrito en conjuntos de API.
Nombres de conjuntos de API y el sufijo .dll
Dado que las asignaciones se mantienen en el esquema en lugar de en el disco, un nombre de conjunto de API que termina con .dll no hace referencia a un archivo de ese nombre. La parte de.dll es solo una convención de nomenclatura, que se transfiere de la forma en que los nombres de módulo se escriben en una tabla de importación. El nombre del conjunto de API es más parecido a un alias o un nombre virtual para un archivo DLL físico.
Cuando una operación del cargador recibe un nombre que comienza por api- o ext-, el cargador lo enruta al entorno de ejecución del conjunto de API, una extensión del cargador que resuelve los contratos a través del esquema. El entorno de ejecución del conjunto de API analiza el nombre por reglas de nomenclatura de conjuntos de API en lugar de como un nombre de archivo, por lo que el sufijo .dll no forma parte del nombre del contrato que se resuelve. Incluya el sufijo cuando trabaje desde un nombre tal como aparece en una tabla de importación; de lo contrario, puede dejarlo desactivado.
El cargador resuelve ambas formas de nombre de contrato, un nombre de contrato con versiones y un alias de contrato, a través del mismo esquema. Para conocer las convenciones que rigen estos nombres, consulte Nombres de contrato de conjunto de API.
La estabilidad del nombre no es la misma que la disponibilidad.
Un nombre de conjunto de API es estable en Windows dispositivos, en el sentido de que el mismo nombre siempre identifica el mismo contrato dondequiera que se reconozca. Es una propiedad del espacio de nombres, no una garantía sobre ningún dispositivo determinado.
Un contrato determinado puede estar ausente de un dispositivo o presentarlo, pero no asignado a un host. Nada sobre el nombre le dice qué. Para averiguar si la implementación está realmente ahí, consulte Detección de la disponibilidad del conjunto de API.
¿Qué resolución requiere?
Para que una llamada a través de un conjunto de API llegue a una implementación, todas las siguientes tienen que contener:
- El contrato está presente en el esquema del dispositivo actual.
- El esquema asigna el contrato a un binario host y ese host se puede cargar.
- El host exporta la función específica a la que llama el archivo binario.
Cuando uno de estos no contiene, donde se muestran los errores depende de cómo importó el conjunto de API:
| Estilo de importación | Comportamiento cuando no se puede resolver el contrato |
|---|---|
| Importación estática | El proceso no se puede iniciar. El cargador resuelve las importaciones estáticas antes de que se ejecute cualquiera de los códigos. |
| Importación cargada con retraso | El proceso se inicia normalmente. La resolución se aplaza a la primera llamada a la API, donde el código puede controlar el error. |
Una exportación que falta se notifica por separado de un contrato que falta; Un binario que importa una función que el host no exporta produce un error de punto de entrada que falta.
Lo que una carga correcta no le indica
La resolución enlaza un contrato a un host. No evalúa el estado de una funcionalidad individual dentro de ese contrato.
Un contrato puede organizar sus funcionalidades disponibles individualmente en grupos con nombre. Un grupo puede no estar disponible en un dispositivo aunque el contrato que lo lleva se resuelva normalmente, ya que el cargador se enlaza con la granularidad del contrato y no consulta el estado del grupo cuando se enlaza. Esto es deliberado: rechazar enlazar un host es irrecuperable para una importación estática, por lo que el cargador toma la ruta de acceso permisiva y deja la pregunta más fina al autor de la llamada.
La consecuencia del código es que una carga correcta o una llamada a LoadLibrary correcta no es evidencia de que haya disponible una funcionalidad determinada. Haga esa pregunta explícitamente con una consulta de disponibilidad. Consulte Detección de disponibilidad del conjunto de API.
Conjuntos de API opcionales y carga retrasada
Si la aplicación llama a un conjunto de API que podría no estar presente, una comprobación de disponibilidad por sí sola no es suficiente: con una importación estática, el proceso no se puede iniciar, por lo que la ejecución nunca llega a la comprobación.
Para mantener accesible la ruta de acceso de código opcional, configure el módulo que lleva la API opcional para retrasar la carga, o resuelva el destino dinámicamente con LoadLibrary y GetProcAddress después de que una consulta de disponibilidad se realice correctamente. Para obtener los detalles de ambos enfoques, consulte Detección de la disponibilidad del conjunto de API.
Reenvío inverso
Aunque los nombres de conjunto de API proporcionan un espacio de nombres estable para los módulos entre dispositivos, no siempre resulta práctico convertir todos los archivos binarios en este sistema. Una aplicación podría haber estado en uso común durante muchos años y volver a compilar sus archivos binarios podría no ser factible. Algunas aplicaciones también deben seguir ejecutándose en sistemas creados antes de que se introdujeran conjuntos de API específicos.
Para dar cabida a eso, las ediciones que no llevan los módulos originales incluyen un conjunto de reenviadores inversos: archivos binarios de compatibilidad que llevan los nombres de módulo introducidos originalmente en equipos Windows y que redirigen sus exportaciones a conjuntos de API.
Una edición de escritorio completa incluye los módulos originales, por lo que una importación de un nombre de módulo heredado se enlaza al módulo como siempre lo tiene. En una edición que ha reemplazado a ese módulo, el reenviador inverso que lleva el mismo nombre cubre la brecha.
La operación del cargador se comporta de la siguiente manera:
- El cargador se presenta con una dependencia en un nombre de módulo de pc heredado Windows que no está presente en el dispositivo.
- El cargador busca un reenviador inverso que lleva ese nombre de módulo y lo carga.
- El reenviador inverso redirige la función importada a un conjunto de API.
- El cargador resuelve ese conjunto de API mediante el esquema, como se describió anteriormente en este tema.
Conceptualmente, la asignación tiene el siguiente aspecto:
DLL importado: samplefeature.dll
- En una edición que tenga el módulo original: samplefeature.dll
- En una edición que lo ha reemplazado: samplefeature.dll reenviador inverso ->
api-win-core-samplefeature> -samplefeaturecore.dll
El límite de esta ruta de acceso es la cobertura de exportación. Un reenviador inverso solo lleva las exportaciones que tienen equivalentes de conjuntos de API, por lo que no exporta necesariamente todas las funciones que hizo el módulo original. Un binario que importa una función que el reenviador inverso no lleva a cabo no se carga con un error de punto de entrada que falta.
El reenvío inverso también es una razón para no tratar una resolución correcta como prueba de que existe una implementación. Una llamada GetProcAddress con un nombre de módulo heredado puede devolver un puntero de función válido que se resuelve en un código auxiliar que devuelve un error. Disponibilidad de consultas explícitamente en su lugar. Consulte Detección de disponibilidad del conjunto de API.
Nota
El reenvío inverso cubre solo un subconjunto de la superficie de la API de Win32. No permite que las aplicaciones destinadas a versiones de escritorio de Windows se ejecuten en todos los dispositivos Windows. Si el binario tiene como destino las versiones actuales de Windows, el nombre del contrato del conjunto de API es la opción más directa.