Cómo: Migrar a /clr
Este tema discute problemas que surgen al compilar el código nativo con /clr (vea /clr (Compilación de Common Language Runtime) para obtener más información). /clr permite a los módulos de Visual C++ invocar y ser invocados desde ensamblados .NET, manteniendo la compatibilidad con módulos no administrados. Vea Ensamblados mixtos (nativos y administrados) y Interoperabilidad nativa y de .NET para obtener más información sobre las ventajas de compilar con /clr.
Problemas conocidos al compilar proyectos de biblioteca con /clr
Visual Studio contiene algunos problemas conocidos al compilar proyectos de biblioteca con /clr:
El código puede consultar tipos en tiempo de ejecución con CRuntimeClass::FromName. Sin embargo, si un tipo está en un archivo .dll en MSIL (compilado con /clr), la llamada a CRuntimeClass::FromName puede sufrir un error si se realiza antes de que los constructores estáticos se ejecuten en el archivo .dll administrado (no apreciará este problema si la llamada a FromName se produce una vez ejecutado el código en el archivo .dll administrado). Para solucionar este problema, puede forzar la creación del constructor estático administrado definiendo una función en el archivo .dll administrado, exportándola e invocándola desde la aplicación MFC nativa. Por ejemplo:
// Extension DLL Header file: __declspec( dllexport ) void EnsureManagedInitialization () { // managed code that won't be optimized away System::GC::KeepAlive(System::Int32::MaxValue); }
Compilar con Visual C++
Antes de usar /clr en cualquier módulo del proyecto, compile y vincule primero el proyecto nativo con Visual Studio 2010.
Los pasos siguientes, seguidos en orden, constituyen la forma más fácil de realizar una compilación con /clr. Es importante compilar y ejecutar el proyecto después de cada uno de estos pasos.
Versiones anteriores a Visual C++ 2003
Si se actualiza a Visual Studio 2010 desde una versión anterior a Visual C++ 2003, puede que aparezcan errores de compilador relacionados con el cumplimento de estándares de C++ mejorado de Visual C++ 2003.
Actualizar desde Visual C++ 2003
Los proyectos compilados anteriormente con Visual C++ 2003 también se deberían compilar primero sin /clr, ya que ahora Visual Studio ha aumentado la compatibilidad con ANSI/ISO y ha realizado algunos cambios importantes. El cambio que probable requiera más atención son Security Features in the CRT. Es muy probable que el código que utiliza el CRT genere advertencias sobre elementos obsoletos. Estas advertencias se pueden suprimir, pero es preferible migrar a las nuevas Security-Enhanced Versions of CRT Functions, ya que proporcionan mayor seguridad y pueden revelar problemas de seguridad del código.
Actualizar desde Extensiones administradas para C++
Los proyectos generados con Visual C++ .NET o Visual C++ 2003 que usaban Extensiones administradas para C++ necesitarán al menos un cambio de configuración, ya que estas extensiones se han quedado obsoletas. Como resultado, el código escrito con Extensiones administradas para C++ no se compilará bajo /clr. Utilice /clr:oldSyntax en su lugar.
Convertir código de C en código de C++
Aunque Visual Studio generará los archivos de C, es necesario convertirlos a C++ para una compilación /clr. El nombre de archivo real no tiene que cambiarse; puede usar /Tp (vea /Tc, /Tp, /TC, /TP (Especificar el tipo de archivo de código fuente).) Tenga en cuenta que, aunque se requieren archivos de código fuente de C++ para /clr, no es necesario refactorizar el código para utilizar paradigmas orientados a objetos.
Es muy probable que el código de C requiera cambios cuando se compile como un archivo de C++. Las reglas de seguridad de tipos de C++ son estrictas, por lo que las conversiones de tipos se deben realizar de forma explícita con conversiones de tipos. Por ejemplo, malloc devuelve un puntero nulo, pero se puede asignar a un puntero de cualquier tipo de C con una conversión de tipos:
int* a = malloc(sizeof(int)); // C code
int* b = (int*)malloc(sizeof(int)); // C++ equivalent
Los punteros a función también presentan una seguridad de tipos estricta en C++, por lo que el siguiente código de C requiere alguna modificación. En C++, es mejor crear un typedef que define el tipo de puntero a función y, a continuación, utilizar dicho tipo para convertir los punteros a función:
NewFunc1 = GetProcAddress( hLib, "Func1" ); // C code
typedef int(*MYPROC)(int); // C++ equivalent
NewFunc2 = (MYPROC)GetProcAddress( hLib, "Func2" );
C++ también requiere que se creen prototipos de las funciones o se definan totalmente para que se pueda hacer referencia a ellas o se puedan invocar.
Se debe cambiar el nombre de los identificadores utilizados en el código de C que resulten ser palabras clave en C++ (como virtual, new, delete, bool, true, false, etc.). Esto generalmente se puede hacer con operaciones simples de búsqueda y sustitución.
Finalmente, mientras que las llamadas COM de estilo C requieren el uso explícito de v-table y del puntero this, C++ no lo requiere:
COMObj1->lpVtbl->Method(COMObj, args); // C code
COMObj2->Method(args); // C++ equivalent
Redefinir la configuración del proyecto
Después de que su proyecto se compile y ejecute en Visual Studio 2010 debería crear nuevas configuraciones del proyecto para /clr en lugar de modificar las configuraciones predeterminadas. /clr es incompatible con algunas opciones del compilador, y crear configuraciones independientes le permite compilar su proyecto como nativo o administrado. Si se selecciona /clr en el cuadro de diálogo de páginas de propiedades, los parámetros de configuración del proyecto que no sean compatibles con /clr se deshabilitarán (y las opciones deshabilitadas no se restauran automáticamente si /clr se deshabilita posteriormente).
Crear nuevas configuraciones de proyecto
Puede utilizar la opción Copiar configuración de en el Nueva configuración del proyecto (Cuadro de diálogo) para crear una configuración de proyecto basada en los parámetros del proyecto existentes. Haga esto una vez para la configuración de depuración y una vez para la configuración de lanzamiento. Los cambios subsiguientes sólo se podrán aplicar a las configuraciones específicas de /clr, dejando las configuraciones de proyecto originales intactas.
Los proyectos que usen reglas de generación personalizadas puede que requieran más atención.
Este paso tiene implicaciones diferentes para proyectos que utilizan archivos make. En este caso se puede configurar un destino-versión independiente, o se puede crear una compilación específica de la versión de /clr a partir de una copia del original.
Cambiar la configuración del proyecto
/clr se puede seleccionar en el entorno de desarrollo siguiendo las instrucciones de /clr (Compilación de Common Language Runtime). Como se mencionó previamente, este paso deshabilitará automáticamente los parámetros de configuración del proyecto que entren en conflicto.
Nota
Si se actualiza un proyecto de biblioteca administrada o de servicio Web desde Visual C++ 2003, la opción del compilador /Zl se agregará a la página de propiedades Línea de comandos. Esto produce el error LNK2001. Quite /Zl de la página de propiedades Línea de comandos para resolverlo. Para obtener más información, vea /Zl (Omitir nombres de biblioteca predeterminada) y Cómo: Abrir páginas de propiedades del proyecto. O bien, agregue .lib y msvcmrt.lib a la propiedad Dependencias adicionales del vinculador.
Para los proyectos generados con archivos make, las opciones del compilador incompatibles se deben deshabilitar manualmente una vez agregado /clr. Vea /Restricciones de /clr para obtener información sobre las opciones del compilador que no son compatibles con /clr.
Encabezados precompilados
/clr admite los encabezados precompilados. No obstante, si sólo compila algunos de los archivos CPP con /clr (compilando el resto como archivos nativos) se deberán realizar algunos cambios ya que los encabezados precompilados generados con /clr no son compatibles con los generados sin /clr. Esta incompatibilidad se debe al hecho de que /clr genera y requiere metadatos. Por lo tanto, los módulos compilados con /clr no pueden usar encabezados precompilados que no incluyan metadatos, los módulos no compilados con /clr no pueden usar archivos de encabezados precompilados que contengan metadatos.
La manera más fácil de compilar un proyecto en el que algunos módulos se compilan con /clr es deshabilitar completamente los encabezados precompilados. (En cuadro de diálogo Páginas de propiedades del proyecto, abra el nodo C/C++ y seleccione Encabezados precompilados. A continuación, cambie la propiedad Crear o utilizar encabezado precompilado a "No utilizar encabezados precompilados").
Sin embargo, particularmente para los proyectos grandes, los encabezados precompilados proporcionan una velocidad de compilación mucho mejor, de modo que no es deseable deshabilitar esta característica. En este caso, es mejor configurar los archivos /clr y no /clr para utilizar encabezados precompilados independientes. Esto se puede realizar en un solo paso mediante la selección múltiple de módulos que se van a compilar con /clr utilizando el Explorador de soluciones, haciendo clic con el botón secundario en el grupo y seleccionando Propiedades. A continuación, cambie las propiedades Crear o utilizar encabezado precompilado a través del archivo y Archivo de encabezado precompilado para utilizar un nombre de archivo de encabezado y un archivo PCH diferentes respectivamente.
Corregir errores
Compilar con /clr puede producir errores de compilador, vinculador o tiempo de ejecución. En esta sección se describen los problemas más comunes.
Combinación de metadatos
Versiones distintas de los tipos de datos pueden hacer que el vinculador produzca un error porque los metadatos generados para los dos tipos no coinciden. (Esto ocurre normalmente cuando los miembros de un tipo se definen condicionalmente, pero las condiciones no son las mismas para todos los archivos CPP que usan dicho tipo). En este caso el vinculador produce un error, informando sólo del nombre de símbolo y del nombre del segundo archivo OBJ en el que se definió el tipo. A menudo resulta útil invertir el orden en que se envían los archivos OBJ al vinculador para detectar la ubicación de la otra versión del tipo de datos.
Interbloqueo del bloqueo del cargador
En Visual C++ .NET y Visual C++ 2003, la inicialización bajo /clr era susceptible al interbloqueo no determinista. Este problema se conoce como el "interbloqueo del bloqueo del cargador". En Visual Studio 2010, este interbloqueo es más fácil de evitar, se detecta y se notifica en tiempo de ejecución y ya no es no determinista. Encontrar el problema del bloqueo del cargador todavía es posible, pero ahora es mucho más fácil de evitar y corregir. Vea Inicialización de ensamblados mixtos para obtener información detallada, orientación y soluciones.
Exportaciones de datos
La exportación de datos DLL es propensa a sufrir errores, y no se recomienda. Esto se debe a que no se garantiza que se inicialice la sección de datos de un archivo DLL hasta que se haya ejecutado alguna parte administrada del archivo DLL. Haga referencia a los metadatos con #using Directive (C/C++).
Visibilidad de tipos
Los tipos nativos ahora son privados de forma predeterminada. En Visual C++ .NET 2002 y Visual C++ 2003, los tipos nativos eran públicos de forma predeterminada. Esto puede producir un tipo nativo que no sea visible fuera del archivo DLL. Resuelva este error agregando public a estos tipos. Para obtener más información, vea Type and Member Visibility.
Problemas de punto flotante y alineación
__controlfp no se admite en Common Language Runtime (vea _control87, _controlfp, __control87_2 para obtener más información). El CLR tampoco respetará align (C++).
Inicialización COM
Common Language Runtime inicializa COM automáticamente cuando se inicializa un módulo (cuando se inicializa COM automáticamente, se hace para MTA). Como resultado, la inicialización explícita de COM produce códigos devueltos que indican que COM ya se ha inicializado. El intento de inicializar COM explícitamente con un modelo de subprocesos cuando CLR ya ha inicializado COM en otro modelo de subprocesos puede hacer que la aplicación no funcione correctamente.
Common Language Runtime inicia COM de forma predeterminada como MTA; utilice /CLRTHREADATTRIBUTE (Establecer el atributo de subproceso de CLR) para modificar esto.
Problemas de rendimiento
Puede que disminuya el rendimiento si los métodos de C++ nativos generados para MSIL se llaman indirectamente (llamadas a funciones virtuales o uso de punteros a función). Para obtener más información sobre esto, vea Doble thunk (C++).
Al trasladar de nativo a MSIL, observará un aumento en el tamaño de su espacio de trabajo. Esto se debe a que Common Language Runtime proporciona muchas características para garantizar que los programas se ejecutan correctamente. Si la aplicación /clr no se está ejecutando correctamente, puede habilitar C4793 (desactivado de forma predeterminada), vea Advertencia del compilador (niveles 1 y 3) C4793 para obtener más información.
El programa se bloquea o se cierra
En algunos casos, el CLR puede que se cierre antes de que el código administrado termine de ejecutarse. El uso de std::set_terminate y SIGTERM puede producir esto. Para obtener más información, vea signal Constants y set_terminate (<exception>).
Utilizar las nuevas características de Visual C++
Después de que la aplicación, se compile, vincule y ejecute, puede empezar a utilizar las características .NET en cualquier módulo compilado con /clr. Para obtener más información, vea Language Features for Targeting the CLR.
Si utilizó Extensiones administradas para C++, puede convertir el código para usar la nueva sintaxis. Para obtener un resumen de las diferencias sintácticas, vea la Extensiones administradas para la lista de comprobación de actualizaciones de sintaxis de C++. Para obtener detalles sobre cómo convertir Extensiones administradas para C++, vea Manual de migraciones C++/CLI.
Para obtener información sobre la programación .NET en Visual C++ vea: