Uso de la Run-Time C

En este artículo se describe cómo usar el tiempo de ejecución de C.

Versión original del producto: Visual C++
Número de KB original: 94248

Sección 1: Hay disponibles tres formas de bibliotecas de C Run-Time (CRT)

Hay tres formas de la biblioteca en tiempo de ejecución de C proporcionada con el SDK de Win32:

  • LIBC. LIB es una biblioteca vinculada estáticamente para programas de un solo subproceso.

  • LIBCMT. LIB es una biblioteca vinculada estáticamente que admite programas multiproceso.

  • CRTDLL. LIB es una biblioteca de importación para CRTDLL.DLL que también admite programas multiproceso. CRTDLL.DLL forma parte de Windows NT.

Microsoft Visual C++ edición de 32 bits también contiene estos tres formularios, pero CRT en un archivo DLL se denomina MSVCRT. LIB. El archivo DLL se puede redistribuir. Su nombre depende de la versión de VC++ (es decir, MSVCRT10.DLL o MSVCRT20.DLL). Sin embargo, tenga en cuenta que MSVCRT10.DLL no se admite en Win32s, mientras que CRTDLL. LIB es compatible con Win32s. MSVCRT20.DLL viene en dos versiones: una para Windows NT y la otra para Win32s.

Sección 2: Uso de las bibliotecas CRT al compilar un archivo DLL

Al compilar un archivo DLL que usa cualquiera de las bibliotecas en tiempo de ejecución de C, con el fin de asegurarse de que CRT se inicializa correctamente, ya sea

  1. La función de inicialización debe tener nombre DllMain() y el punto de entrada debe especificarse con la opción del enlazador. -entry:_DllMainCRTStartup@12

    o

  2. El punto de entrada del archivo DLL debe llamar explícitamente al CRT_INIT() proceso de asociación y desasociación de procesos.

Esto permite a las bibliotecas en tiempo de ejecución de C asignar e inicializar correctamente los datos en tiempo de ejecución de C cuando un proceso o subproceso se asocia al archivo DLL, limpiar correctamente los datos en tiempo de ejecución de C cuando un proceso se desasocie del archivo DLL y para que los objetos globales de C++ del archivo DLL se construyan y destruyan correctamente.

Los ejemplos del SDK de Win32 usan el primer método. Úselas como ejemplo. Consulte también la referencia del programador de Win32 para DllEntryPoint() y la documentación de Visual C++ para DllMain(). Tenga en cuenta que DllMainCRTStartup() llama a CRT_INIT() y CRT_INIT() llamará a DllMain() de la aplicación, si existe.

Si desea usar el segundo método y llamar al código de inicialización de CRT usted mismo, en lugar de usar DllMainCRTStartup() y DllMain(), hay dos técnicas:

  1. Si no hay ninguna función de entrada que realice el código de inicialización, especifique CRT_INIT() como punto de entrada del archivo DLL. Suponiendo que haya incluido NTWIN32. MAK, que se define DLLENTRY como @12, agregue la opción a la línea de vínculo del archivo DLL:-entry:_CRT_INIT$(DLLENTRY).

    o

  2. Si tiene su propio punto de entrada DLL, haga lo siguiente en el punto de entrada:

    1. Use este prototipo para CRT_INIT(): BOOL WINAPI _CRT_INIT(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved);

      Para obtener información sobre los CRT_INIT() valores devueltos, vea la documentación DllEntryPoint; se devuelven los mismos valores.

    2. En DLL_PROCESS_ATTACH y DLL_THREAD_ATTACH (vea DllEntryPoint en la referencia de la API win32 para obtener más información sobre estas marcas), llame a CRT_INIT(), primero, antes de que se llame a cualquier función en tiempo de ejecución de C o se realicen operaciones de punto flotante.

    3. Llame a su propio código de inicialización/terminación de subprocesos o procesos.

    4. En DLL_PROCESS_DETACH y DLL_THREAD_DETACH, llame al CRT_INIT() último, después de que se haya llamado a todas las funciones en tiempo de ejecución de C y se completen todas las operaciones de punto flotante.

Asegúrese de pasar a CRT_INIT() todos los parámetros del punto de entrada; CRT_INIT() espera esos parámetros, por lo que es posible que las cosas no funcionen de forma confiable si se omiten (en particular, fdwReason es necesario para determinar si es necesario inicializar o finalizar el proceso).

A continuación se muestra una función de punto de entrada de ejemplo de esqueleto que muestra cuándo y cómo realizar estas llamadas a CRT_INIT() en el punto de entrada dll:

BOOL WINAPI DllEntryPoint(HINSTANCE hinstDLL, DWORD fdwReason,
LPVOID lpReserved)
{
    if (fdwReason == DLL_PROCESS_ATTACH || fdwReason == DLL_THREAD_ATTACH)
    if (!_CRT_INIT(hinstDLL, fdwReason, lpReserved))
    return(FALSE);

    if (fdwReason == DLL_PROCESS_DETACH || fdwReason == DLL_THREAD_DETACH)
    if (!_CRT_INIT(hinstDLL, fdwReason, lpReserved))
    return(FALSE);
    return(TRUE);
}

Nota:

Esto no es necesario si usa DllMain() y -entry:_DllMainCRTStartup@12.

Sección 3: Uso de NTWIN32. MAK para simplificar el proceso de compilación

Hay macros definidas en NTWIN32. MAK que se puede usar para simplificar los archivos Make y para asegurarse de que se compilan correctamente para evitar conflictos. Por este motivo, Microsoft recomienda encarecidamente usar NTWIN32. MAK y las macros en el mismo.

Para la compilación, use: $(cvarsdll) for apps/DLLs using CRT in a DLL.

Para la vinculación, use una de las siguientes opciones:

  • $(conlibsdll) for console apps/DLLs using CRT in a DLL
  • $(guilibsdll) for GUI apps using CRT in a DLL

Sección 4: Problemas detectados al usar varias bibliotecas CRT

Si una aplicación que realiza llamadas en tiempo de ejecución de C llama a vínculos a un archivo DLL que también realiza llamadas en tiempo de ejecución de C, tenga en cuenta que si ambos están vinculados a una de las bibliotecas en tiempo de ejecución de C vinculadas estáticamente (LIBC). LIB o LIBCMT. LIB), el .EXE y el archivo DLL tendrán copias independientes de todas las funciones en tiempo de ejecución de C y las variables globales. Esto significa que los datos en tiempo de ejecución de C no se pueden compartir entre el .EXE y el archivo DLL. Algunos de los problemas que pueden producirse como resultado son:

  • Pasar identificadores de flujo almacenados en búfer desde el .EXE/DLL al otro módulo

  • Asignación de memoria con una llamada en tiempo de ejecución de C en el .EXE/DLL y reasignación o liberación en el otro módulo

  • Comprobar o establecer el valor de la variable errno global en el .EXE/DLL y esperar que sea el mismo en el otro módulo. Un problema relacionado es llamar a perror() en el módulo opuesto desde donde se produjo el error en tiempo de ejecución de C, ya que perror() usa errno.

Para evitar estos problemas, vincule el .EXE y el archivo DLL con CRTDLL. LIB o MSVCRT. LIB, que permite que tanto el .EXE como el archivo DLL usen el conjunto común de funciones y datos contenidos en CRT en un archivo DLL, y los datos en tiempo de ejecución de C, como los identificadores de secuencia, pueden ser compartidos por el .EXE y el archivo DLL.

Sección 5: Combinación de tipos de biblioteca

Puede vincular el archivo DLL con CRTDLL. LIB/MSVCRT. LIB independientemente de con qué esté vinculada la .EXE si evita mezclar estructuras de datos de CRT y pasar identificadores de archivo CRT o punteros CRT FILE* a otros módulos.

Al mezclar tipos de biblioteca, siga estos pasos:

  • Los identificadores de archivo CRT solo pueden funcionar con el módulo CRT que los creó.

  • Los punteros CRT FILE* solo pueden funcionar con el módulo CRT que los creó.

  • La memoria asignada con la función malloc() CRT solo puede liberarse o reasignarse mediante el módulo CRT que la asignó.

Para ilustrar esto, tenga en cuenta el ejemplo siguiente:

  • .EXE está vinculado a MSVCRT. LIB
  • LA DLL A está vinculada a LIBCMT. LIB
  • El archivo DLL B está vinculado a CRTDLL. LIB

Si el .EXE crea un identificador de archivo CRT mediante _create() o _open(), este identificador de archivo solo se puede pasar a _lseek(), _read(), _write(), _close()etc. en el archivo .EXE. No pase este identificador de archivo CRT a ninguno de los archivos DLL. No pase un identificador de archivo CRT obtenido de DLL al otro archivo DLL o al .EXE.

Si dll A asigna un bloque de memoria con malloc(), solo el archivo DLL A puede llamar a free(), _expand()o realloc() para operar en ese bloque. No se puede llamar malloc() desde el archivo DLL A e intentar liberar ese bloque del .EXE o del archivo DLL B.

Nota:

Si los tres módulos se vincularon con CRTDLL. LIB o los tres estaban vinculados con MSVCRT. LIb, estas restricciones no se aplicarían.

Al vincular archivos DLL con LIBC. LIB, tenga en cuenta que si existe la posibilidad de que un programa multiproceso llame a un archivo DLL de este tipo, el archivo DLL no admitirá varios subprocesos que se ejecuten en el archivo DLL al mismo tiempo, lo que puede causar problemas importantes. Si existe la posibilidad de que los programas multiproceso llamen al archivo DLL, asegúrese de vincularlo a una de las bibliotecas que admiten programas multiproceso (LIBCMT). LIB, CRTDLL. LIB o MSVCRT. LIB).