INFORMACIÓN: Utilizar _declspec (dllimport) & _declspec(dllexport) en código

Seleccione idioma Seleccione idioma
Id. de artículo: 132044 - Ver los productos a los que se aplica este artículo
Expandir todo | Contraer todo

En esta página

Resumen

En este artículo complementa la información tratada en el siguiente artículo en Microsoft Knowledge Base:
107501INFORMACIÓN: __export reemplazados por __declspec en C++ de 32 bits
Este artículo describe las ventajas y mecanismos de utilizar _declspec (dllimport) y _declspec(dllexport) en la aplicación.

Más información

La edición de 32 bits de Visual C++ utiliza _declspec (dllimport) y _declspec(dllexport) para reemplazar la palabra clave __export utilizada previamente en versiones de 16 bits de Visual C++.

No es necesario utilizar _declspec (dllimport) para el código se compile correctamente pero, si lo hace, el compilador genere código de mejor calidad. El compilador es capaz de generar código de mejor calidad porque por asegurarse de que sabe si una función existe o no en un archivo DLL, por lo que el compilador puede producir códigos que omita un nivel de direccionamiento indirecto que normalmente se presente en una llamada de función que cruza un límite DLL.

Con la sección apropiada de .def archivo EXPORTS, _declspec(dllexport) no es necesario. _declspec(dllexport) se agregó para proporcionar una manera fácil de exportar funciones desde un archivo .exe o .dll sin utilizar un archivo .def.

El resto de este artículo proporciona una explicación bastante bajo nivel, completa de estos problemas.

El formato ejecutable Portable Win32 está diseñado para minimizar el número de páginas que debe ser utilizadas para corregir importaciones. Para ello, coloca todas las direcciones de importación de cualquier programa en un lugar denominado tabla de direcciones de importación. Esto permite que el cargador modificar sólo una o dos páginas al tener acceso a estas importaciones.

Utilizar _declspec (dllimport) para las llamadas a funciones

En el ejemplo de código siguiente, suponga func1 es una función que reside en un archivo DLL independiente del archivo .exe que contiene la función main().

Sin _declspec (dllimport), dado el código:
void main(void) {
    func1();
}
				
el compilador genera código que tiene este aspecto:
call func1
				
y el vinculador traducirá la llamada en algo así:
call 0x4000000         ; The address of 'func1'.
				
si 'func1' existe en otra, el vinculador no puede resolver esto directamente porque no tiene forma de saber cuál es la dirección de 'func1'. En entornos de 16 bits, el vinculador agrega esta dirección de código a una lista en el .exe que el cargador revisaría en tiempo de ejecución con la dirección correcta. En entornos de 32 bits, el vinculador genera un thunk para el que conoce la dirección. El código thunk tiene este aspecto:
   0x40000000:    jmp DWORD PTR __imp_func1
				
aquí __imp_func1 es la dirección de ranura de func1 en la tabla de direcciones de importación del archivo .exe. Todas las direcciones, por lo tanto, se sabe que el vinculador. El cargador sólo tiene que actualizar la tabla de direcciones de importación del archivo .exe en tiempo de carga para que todo funcione correctamente.

Por lo tanto, es mejor utilizar _declspec (dllimport), porque es mejor si el vinculador no genera thunk si no tiene que. Thunks agrandar el código (en sistemas RISC, puede ser varias instrucciones) y puede degradar el rendimiento de caché. Si indicar al compilador que la función está en una DLL, puede generar una llamada indirecta.

Ahora este código:
__declspec(dllimport) void func1(void);

void main(void) {
    func1();
}
				
genera esta instrucción:
call DWORD PTR __imp_func1
				
es no hay thunk ni ninguna instrucción jmp, por lo que el código es más pequeño y rápido.

Por otro lado, para las llamadas a función dentro de un archivo DLL, no desea tiene que utilizar una llamada indirecta. Ya sabe la dirección de una función. Tiempo y espacio son necesarios para cargar y almacenar la dirección de la función antes una llamada indirecta, por lo que una llamada directa siempre es más rápido y más pequeños. Sólo desea utilizar __declspec (dllimport) al llamar a funciones DLL desde fuera el propio archivo DLL. No, __declspec (dllimport) se utilice en funciones dentro de un archivo DLL, al generar esa DLL.

Mediante _declspec(dllexport)

Microsoft introdujo __export en la versión del compilador de 16 bits para que el compilador de generar automáticamente los nombres de exportación y colocarlos en un archivo .lib. Este archivo .lib podría utilizarse igual que un .LIB estática para vincularlo a un archivo DLL.

Microsoft agrega __declspec (dllexport) para continuar esta comodidad. Su propósito es agregar la directiva de exportación al archivo objeto, por lo que no es necesario un archivo .def.

Esta conveniencia es más aparente cuando se intenta exportar decora los nombres de función de C++. No hay ninguna especificación estándar para la decoración de nombres, por lo que el nombre de una función exportada que cambia entre versiones de compilador. Si utiliza _declspec(dllexport), volver a compilar la DLL y archivos .exe dependientes es necesario sólo cuenta para realizar cambios de cualquier convención de nomenclatura.

Muchas directivas de exportación como ordinales, NONAME y PRIVATE, se puede realizar sólo en un archivo .def, y no hay ninguna forma de especificar estos atributos sin un archivo .def. Sin embargo, utilizar _declspec(dllexport) así como utilizar un archivo .def no provoca errores de generación.

Como referencia, busque en el archivo de encabezado Winbase.H de Win32. Contiene ejemplos de uso de __declspec (dllexport) y __declspec (dllimport) preferido.

Uso de _declspec(dllexport) y _declspec (dllimport) en datos

En el caso de los datos, es conveniente utilizar _declspec (dllimport) para quita una capa de direccionamiento indirecto. Al importar datos desde un archivo DLL, tendrá que recorrer la tabla de direcciones de importación. En días de Win32 antes _declspec (dllimport), esto significaba que tenía que acordarse de realizar un nivel de direccionamiento indirecto adicional al obtener acceso a datos exportados desde el archivo DLL:
// project.h
#ifdef _DLL     // If accessing the data from inside the DLL
   ULONG ulDataInDll;

else            // If accessing the data from outside the DLL
   ULONG *ulDataInDll;
#endif
				
a continuación, se podrían exportar los datos en el archivo .def:
// project.def
LIBRARY project
EXPORTS
    ulDataInDll   CONSTANT
				
y acceso a él fuera del archivo DLL:
if (*ulDataInDll == 0L) {
   // Do stuff here
}
				
al marcar los datos como __declspec (dllimport), el compilador genera automáticamente el código de direccionamiento indirecto de. Ya no tiene que preocuparse por los pasos anteriores. Como se indicó anteriormente, no debe utilizar la _declspec (dllimport) declaración en los datos al generar el archivo DLL. Funciones dentro de la DLL no utilizará Import Address Table acceso al objeto de datos. Por lo tanto, no tendrá el nivel adicional de direccionamiento indirecto presente.

Para exportar los datos automáticamente desde el archivo DLL, utilice esta declaración:
__declspec(dllexport) ULONG ulDataInDLL;
				

Utilizar un archivo .def

Si decide utilizar __declspec (dllimport) junto con un archivo .def, deberá cambiar el archivo .def para utilizar datos en lugar de CONSTANT para reducir la probabilidad de que la codificación incorrecta hará que un problema:
// project.def
LIBRARY project
EXPORTS
    ulDataInDll   DATA
				
el gráfico siguiente muestra la causa:
Keyword     Emits in the import lib     Exports
CONSTANT    __imp_ulDataInDll           ulDataInDll
            __ulDataInDll

DATA        __imp_ulDataInDll           ulDataInDll
				
utilizar _declspec (dllimport) y CONSTANT muestra tanto la versión de __imp_ y el nombre no representativo en la biblioteca de importación de .LIB DLL se crea para permitir la vinculación explícita. Utilizar _declspec (dllimport) y datos, muestra sólo la versión de __imp_ del nombre.

Si utiliza CONSTANT, cualquiera de las siguientes construcciones de código podría utilizarse para tener acceso a la ulDataInDll:
__declspec(dllimport) ULONG ulDataInDll; /*prototype*/ 
   if (ulDataInDll == 0L)   /*sample code fragment*/ 
				
-o bien -
ULONG *ulDataInDll;      /*prototype*/ 
if (*ulDataInDll == 0L)  /*sample code fragment*/ 
				
Sin embargo, si utiliza DATA en el archivo .def, sólo el código compilado con la siguiente definición puede tener acceso a la variable ulDataInDll:
__declspec(dllimport) ULONG ulDataInDll;
if (ulDataInDll == 0L)   /*sample code fragment*/ 
				
utilizar CONSTANT es más arriesgado porque si olvida utilizar el nivel adicional de direccionamiento indirecto, puede tener acceso potencialmente a puntero de la tabla de direcciones de importación a la variable--propia variable. Este tipo de problema puede manifiesto a menudo como una infracción de acceso como Import Address Table está actualmente de sólo lectura por el compilador de Microsoft y vinculadores.

El vinculador actual de Visual C++ emite una advertencia si ve CONSTANT en el archivo .def para este caso. La única razón para utilizar CONSTANT es si no se vuelva a compilar algún archivo objeto donde el archivo de encabezado no mostrara dllimport en el prototipo.

Referencias

Los Visual C++ libros en pantalla de proporcionar una cantidad considerable de documentación en dllimport y dllexport clase de almacenamiento de atributos. Esto incluye "Los dllexport y dllimport atributos" y los temas "utilizar dllimport y dllexport en C++" en el capítulo "Modificadores específicos de Microsoft" de la referencia del lenguaje C++ y los temas "Exportar símbolos" en el capítulo "Crear archivos DLL para Win32" de la referencia de técnicas de programación. Para una exhaustiva lista Temas relacionados, busque los libros en pantalla para "dllimport" o "dllexport".

Para obtener información adicional, consulte los artículos siguientes de Microsoft Knowledge Base:
90530Cómo exportar datos desde un archivo DLL o una aplicación
107501INFORMACIÓN: __export reemplazados por __declspec en C++ de 32 bits

Propiedades

Id. de artículo: 132044 - Última revisión: martes, 02 de diciembre de 2003 - Versión: 2.0
La información de este artículo se refiere a:
  • Microsoft Visual C++ 1.0 Professional Edition
  • Microsoft Visual C++ 2.0 Professional Edition
  • Microsoft Visual C++ 2.1
  • Microsoft Visual C++ 4.0 Standard Edition
  • Microsoft Visual C++ 5.0 Enterprise Edition
  • Microsoft Visual C++ 5.0 Professional
Palabras clave: 
kbmt kbcode kbcompiler kbinfo KB132044 KbMtes
Traducción automática
IMPORTANTE: Este artículo ha sido traducido por un software de traducción automática de Microsoft (http://support.microsoft.com/gp/mtdetails) en lugar de un traductor humano. Microsoft le ofrece artículos traducidos por un traductor humano y artículos traducidos automáticamente para que tenga acceso en su propio idioma a todos los artículos de nuestra base de conocimientos (Knowledge Base). Sin embargo, los artículos traducidos automáticamente pueden contener errores en el vocabulario, la sintaxis o la gramática, como los que un extranjero podría cometer al hablar el idioma. Microsoft no se hace responsable de cualquier imprecisión, error o daño ocasionado por una mala traducción del contenido o como consecuencia de su utilización por nuestros clientes. Microsoft suele actualizar el software de traducción frecuentemente.
Haga clic aquí para ver el artículo original (en inglés): 132044
Renuncia a responsabilidad de los contenidos de la KB sobre productos a los que ya no se ofrece asistencia alguna
El presente artículo se escribió para productos para los que Microsoft ya no ofrece soporte técnico. Por tanto, el presente artículo se ofrece "tal cual" y no será actualizado.

Enviar comentarios

 

Contact us for more help

Contact us for more help
Connect with Answer Desk for expert help.
Get more support from smallbusiness.support.microsoft.com