Cómo migrar una aplicación .NET de 32 a 64 bit

Con el lanzamiento de la versión 2.0 de la plataforma .NET, Microsoft incluyó soporte para entornos de 64 bit basados en microprocesadores Itanium y x64. Las anteriores versiones 1.1 y 1.0 no ofrecían este tipo de soporte.

El entorno de desarrollo de aplicaciones .NET (SDK, Visual Studio 2005) permite compilar las aplicaciones en tres modos diferentes: sólo para ser ejecutadas sobre 32 bit, sólo para ser ejecutadas sobre 64 bit, o compiladas sólo para lenguaje intermedio (MSIL). En este último caso los ensamblados no contienen información específica de la plataforma hardware de destino, por lo que se conoce como modo agnóstico.

Teóricamente, una aplicación .NET compilada en modo agnóstico, el modo por defecto en que compila Visual Studio 2005, se puede ejecutar indistintamente en entornos de 32 ó 64 bit. Bastaría con desplegarla en cualquiera de los dos entornos. Sin embargo, no siempre el escenario es tan simple.

La clave para que una aplicación sea multiplataforma es la ausencia de dependencias del hardware en el código. Por lo tanto, para migrar una aplicación de 32 a 64 bit es necesario investigar la aplicación para determinar si existen dependencias de bajo nivel. Si se detectaran, habría que estudiar cada caso ya que no existen recetas universales. Por dependencias de bajo nivel entendemos la utilización de APIs nativas (como la Win32) o de objetos COM.

Esta investigación debe dirigirse en dos direcciones: (1) descubrir y corregir las posibles dependencias del código fuente de la aplicación (nuestro código), y (2) analizar y evaluar las componentes de terceros. Si alguna de estas componentes no cumpliera los requisitos debería ser sustituida por otra equivalente válida.

De forma resumida, se dice que una aplicación está escrita con código seguro si sólo se ejecuta en el entorno del CLR, o sea, no se utilizan punteros de forma explícita ni llamadas de tipo pInvoke (llamadas a código no administrado, como la API Win32). Esto implicaría que nuestro código no esté atado al hardware, que es lo que buscamos para que la aplicación pueda ejecutarse correctamente sobre 32 y 64 bit.

Una pregunta común en esta situación es: ¿Cómo saber en qué entorno se ejecuta la aplicación? Cuando ejecutemos la aplicación migrada, sería útil conocer si realmente está corriendo sobre 64 bit. Sabemos que los punteros sobre 32 bit ocupan 4 bytes, mientras que sobre 64 bit tienen un tamaño de 8 bytes. Entonces, basta con conocer en tiempo de ejecución este dato.

La estructura IntPtr, del espacio de nombres System, nos resultará útil para averiguarlo. Esta estructura contiene una propiedad entera estática llamada Size, que nos devuelve justamente lo que necesitamos: un 4 si estamos en .NET 32 bit, o un 8 si estamos sobre 64 bit.

Puede verse claramente ahora que una aplicación que utilice punteros podría realizar operaciones que dependan de su tamaño. Si la aplicación «piensa» que está sobre 32 bit, pero está sobre 64, el resultado de estas operaciones no tiene que ser el que se esperaba. Con gran probabilidad se generará un error.

Conocer las zonas de nuestro código que no son seguras nos ayudará a decidir si las convertimos en código seguro, o si las hacemos dependientes de una arquitectura específica.

Para investigar si el código de nuestra aplicación es seguro Microsoft ofrece con el SDK una herramienta que se llama Peverify.exe, que podemos utilizar para analizar todas las DLLs que genera el proyecto.

Deberemos eliminar todas las referencias a objetos COM, buscando soluciones alternativas en cada caso, siempre basadas en código administrado.

Por último, debemos verificar en el entorno de desarrollo Visual Studio 2005 que la compilación de la solución se realice en modo agnóstico (Any CPU), que es el modo por defecto.

Hasta aquí habríamos «limpiado» o «asegurado» nuestro código para que se ejecute transparentemente sobre 32 o 64 bit pero, ¿qué pasa si dependemos de componentes de terceros? Estas también tienen que haber sido desarrolladas con código seguro y haber estado compiladas en modo agnóstico. Si no se cumplen estas dos condiciones habría que sustituir las componentes inadecuadas para 64 bit por otras que sí lo sean.

Para conocer si han sido escritas con código seguro podemos usar nuevamente la herramienta PEVerify, analizando cada uno de los ensamblados ajenos.

El problema de conocer cómo han sido compilados estos ensamblados ajenos se resuelve mediante la Reflexión, la capacidad del entorno de ejecución de .NET de acceder a los metadatos de los ensamblados y obtener información sobre los mismos. Si obtenemos el mensaje de ILOnly, es decir, modo agnóstico (sólo lenguaje intermedio), ya hemos llegado a la meta. Si no fuera así, tendrá que buscar componentes sustitutas que le ofrezcan las mismas funcionalidades y, además, sean aptas para entornos de 64 bit o multiplataforma. Depende de cuáles sean sus objetivos.

El siguiente ejemplo muestra cómo obtener la información que buscamos:

Al ejecutar GetAssemblyInfo() obtendremos la siguiente cadena de caracteres:

Ensamblado de System.DateTime: ‘mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089’. Compilado para: ‘ILOnly, Required32Bit’ – Plataforma: ‘I386’

Es decir, la versión de System.DateTime que estamos utilizando pertenece al ensamblado mscorlib, que está compilado en modo agnóstico.

Enlaces de interés:

Migrating 32-bit Managed Code to 64-bit
http://msdn.microsoft.com/en-us/library/ms973190.aspx

64-Bit .NET Framework
http://msdn.microsoft.com/es-es/netframework/aa496329(en-us).aspx

64-bit (From Wikipedia, the free encyclopedia)
http://es.wikipedia.org/wiki/64_bits

Microsoft 64-bit Computing Overview
http://www.microsoft.com/servers/64bit/overview.mspx

Herramienta PEVerify Tool (Peverify.exe)
http://msdn.microsoft.com/es-es/library/62bwd2yd(VS.80).aspx

System.IntPtr.Size
http://msdn.microsoft.com/es-es/library/system.intptr.size(VS.80).aspx