Mi programa recién creado no se ejecuta en otro ordenador

Suele ser una pregunta bastante habitual en los pocos foros dedicados a C++ que hay, y vamos a intentar dar aquí una respuesta contundente y razonada de por qué ocurre eso y cómo solucionarlo.

La pregunta sobre la ejecución de un programa hecho en C, C++ o C++/CLI en otro equipo (en general en el del cliente final) suele tener algunas variantes en cuanto al texto mostrado, entre las que está:

  • Salta una ventana con “Error 135”.
  • Salta una ventana o aparece un registro en el Visor de Eventos que dice algo como “No se pudo iniciar la aplicación; la configuración en paralelo no es correcta.”.
  • Salta un error con un mensaje similar a “La aplicación no se pudo ejecutar porque ha sido incorrectamente instalada”.
  • En casos más raros, y para programas creados con versiones antiguas de Visual C++, suele aparecer algún mensaje del tipo “Falta ordinal <número> en <nombre>.DLL”.
  • Aparece un mensaje que dice “No se encuentra el archivo <nombre>.DLL.”

En resumen, el mensaje, a veces muy críptico, confunde por completo al programador novel y al no tan bisoño porque a veces, reconozcámoslo, el texto es todo un galimatías.

Existen varios motivos por los cuales se puede presentar el problema, pero principalmente están todos englobados dentro del mismo concepto: el ordenador destino carece de algunos ficheros que son necesarios para la ejecución de nuestra aplicación.

***

Debug vs Release. Cuando nosotros creamos un proyecto en Visual Studio, este se crea en modo Debug por defecto. Esto quiere decir que se está compilando de una forma un poco “especial”, ya que tanto el compilador como el enlazador insertan una serie de funciones extra que nos van a permitir muchas veces detectar un error en nuestro código con solo ejecutar el programa, y a veces incluso hasta en tiempo de compilación.

También se enlaza con una serie de bibliotecas modificadas que nos van a permitir, de nuevo, encontrar errores de forma mucho más rápida.

Por ejemplo, si nuestro programa tiene una fuga de memoria (es decir, una variable dinámicamente asignada pero nunca liberada), nos aparecerá un mensaje en la ventana de “Output” de Visual Studio por cada una de las variables que queden sin liberar al cierre de la aplicación. Aparecerá un número y los bytes que no se han recobrado. Siguiendo unos pasos que describiré la semana que viene, es posible encontrar de forma automática el new correspondiente al delete que se ha omitido. De esta forma algo que puede llevar varias horas o días, Microsoft nos lo soluciona en segundos.

Otra cosa que también se detecta fácilmente son los desbordamientos de búfer y el uso de variables no asignadas, casi por el mismo procedimiento descrito en el párrafo anterior. Nunca antes había sido tan fácil encontrar este tipo de problemas.

Las compilaciones Debug comprueban otras muchas cosas, como aserciones en el código de MFC para detectar pifias que de otra manera podrían terminar en un pete de la aplicación bastante difícil de solucionar.

Una vez tenemos nuestra aplicación funcionando correctamente sin mensajes de error en la ventana de “Output”, debemos pasar al modo Release, que se hace mediante un desplegable en la barra de herramientas de Visual Studio o con las opciones del proyecto.

Debemos entonces volver a comprobar nuestra aplicación para que no tenga errores. En general el paso de Debug a Release se suele hacer de forma bastante poco traumática, pero hay veces que el cambio hace que nuestra aplicación no funcione.

No hay problema, todavía podemos depurar en Release, pero el flujo del programa puede resultar extraño porque veremos saltos sin mucho sentido y ejecuciones de código raras, ya que el código está optimizado y el compilador de Visual C++ es muy bueno en eso. Aquí debemos tener cuidado a la hora de depurar, porque lo que veamos en las ventanas de inspección seguro que no es lo que esperamos. La forma de asegurarnos que una variable es lo que es, es utilizar la función OutputDebugString(), que nos imprimirá lo que enviemos en la ventana de “Output” de Visual Studio.

En general, los errores que se presentan en Release pero no en Debug se suelen deber a temas de sincronización entre hilos y a tiempos, porque el ejecutable va mucho más rápido, por lo que es por donde debemos empezar a buscar los problemas ya que los punteros locos los hemos solventado en Debug.

***

Ya llego, ya. Uno de los problemas que podemos tener al ejecutar nuestro programa en otro ordenador es que estemos intentándolo con una versión Debug. Aparte de que la licencia de uso de Visual C++ nos lo impide, en la máquina de destino faltan una serie de elementos que son necesarios para el ejecutable en modo Debug, y es por eso por lo que obtenemos el error. En nuestra máquina de desarrollo no falla porque dichos elementos están presentes en el sistema.

Nota: la licencia no nos impide ejecutar código Debug en una máquina cliente, lo que nos impide es distribuir como final una compilación Debug. Por experiencia personal a veces es necesario instalar toda la parafernalia de depuración remota en la máquina cliente para poder solucionar errores que no se presentan ni en los ordenadores de desarrollo ni en los de pruebas.

***

Runtimes. Es muy posible que, pensando que estamos con C++, creamos que no existen runtimes al más puro estilo de Visual Basic. Pues bien, sí que existen.

C y C++ son lenguajes agnósticos en cuanto a biblioteca o, en otras palabras, el lenguaje no exige ningún tipo de biblioteca para funcionar, por oposición a Java o a C# que necesitan de toda una parafernalia asociada a ellos para actuar.

Pero todos sabemos que C tiene funciones como printf(), strlen() y demás, funciones que no ofrecen ni Win32 ni ningún otro sistema operativo. A C++ le pasa lo mismo, tiene la STL y compañía (y no vamos a entrar en detalles si es biblioteca de C++, STL o la madre que lo parió).

Eso tiene que ir en algún sitio, y va en una serie de bibliotecas que suelen acompañar a los compiladores y, aunque nadie nos obliga a utilizarlas (yo de hecho tengo algunos programas que no las usan y sólo usan Win32 puro y duro), lo habitual es que lo hagamos.

En Visual C++ y en la mayoría de los compiladores hay dos formas de tener bibliotecas: estáticas y dinámicas. Las estáticas son ficheros .LIB que se añaden a las opciones de nuestro compilador y se integran dentro de nuestro ejecutable. Las dinámicas suelen ser ficheros DLL que se asocian a nuestro ejecutable a través de otro fichero .LIB que contiene una indicación de lo que hay en cada DLL (ver mi entrada Todo lo que quisiste saber sobre las DLL y no te atreviste a preguntar (II) ).

En general, en Visual C++, y si no se indica otra cosa, la compilación y enlazado se realiza a través de la versión en DLL del motor de tiempo de ejecución. ¿Por qué se hace así por defecto? Pues porque teniendo el runtime en DLL, Windows Update puede actualizarlo si se produjera algún fallo de seguridad. Si el código que reside en la DLL a actualizar está en nuestro ejecutable, la única manera de hacerlo sería que instaláramos la actualización en nuestro compilador y recompiláramos el programa.

Podemos cambiar en las opciones del compilador para enlazar estática o dinámicamente. Fijaros en la imagen adjunta:

image

Podemos elegir tanto versiones debug como release de las DLL así como de las estáticas. MFC también tiene la misma posibilidad:

image

Para no liarnos, debemos enlazar con las versiones Debug cuando estemos en compilación Debug y lo mismo en Release.

No obstante yo os recomiendo que dejéis la opción DLL porque así vuestra aplicación será más segura.

Cuando ejecutemos un programa con enlace dinámico en un ordenador que no tenga instalado el runtime adecuado también nos saltará un mensaje de error de los descritos arriba. Cuando se producía un error de este tipo con los programas escritos en Visual C++ 6 y anteriores, saltaba una ventana que te indicaba que no se había encontrado el fichero tal. Y cuando lo añadías te saltaba otra con un nuevo fichero hasta que habías copiado todos. Pero a partir de las versiones 2002 de Visual C++, se muestran los citados mensajes crípticos, cosa que realmente no entiendo, con lo fácil que es colocar en el stub de arranque (lo que se enlaza antes que tu programa) un mensaje diciendo que no se encuentra el runtime o que éste es incorrecto. Inescrutabilidades de la gente de Microsoft.

En principio Windows trae de serie todos los runtimes instalados. Pero claro, sólo hasta la fecha en la que el sistema operativo salió. Con el tiempo la gente de Microsoft empieza a compilar los parches y las actualizaciones con versiones nuevas de Visual C++ (sí, Windows y Office están compilados con los mismos compiladores que usamos nosotros, o casi, ya que Microsoft suele utilizar unas versiones internas estables algo más antiguas, pero en muchos momentos nos mantenemos sincronizados y, por ejemplo, Windows 7 está compilado con el mismo compilador que viene con el SDK de su versión, o eso dicen).

Conforme Microsoft va usando versiones más modernas, estas se van instalando en Windows cuando pasamos Windows Update o instalamos alguno de sus productos, por lo que a veces nuestro programa falla al ejecutarse en un ordenador y lo hace en otro casi igual pero que por ejemplo tiene una versión más moderna de Office o de otro producto.

***

Cuando llegamos a uno de estos errores debemos instalar lo que Microsoft llama el Visual C++ Redistributable y que podemos bajar de su Web de descargas. Eso sí, nos tenemos que bajar la versión adecuada a la versión de Visual C++ y a la plataforma (32 ó 64 bits) que estemos usando. Algunos redistributables son:

  • Visual Studio 2005 (x86, x64)
  • Visual Studio 2005 Service Pack 1 (x86, x64)
  • Visual Studio 2008 (x86, x64)
  • Visual Studio 2008 Service Pack 1 (x86, x64)
  • Visual Studio 2010 (x86, x64)

Una vez instalado, la próxima vez que Windows Update haga una pasada, actualizará todos los problemas de seguridad que puedan tener.

Una cosa curiosa de todo esto es el “WinSxS”, que está en la carpeta C:Windowswinsxs. Si nos metemos ahí dentro veremos una buena espuerta de DLL y otros ficheros agrupados por carpetas. Debemos hacerlo con cuidado porque si tocamos algo ahí sin querer quizás tengamos que reinstalar Windows porque es una carpeta muy sensible para la estabilidad del sistema. Ahí dentro es donde están todas las versiones de todos los runtimes que hemos instalado y/o usado a lo largo del tiempo, con todas sus actualizaciones de seguridad y demás parches. Microsoft utiliza una serie de heurísticas no documentadas para determinar qué versión conectar con qué programa, y si una actualización de algún tipo hará que el programa falle y por tanto deberá seguir enlazándolo con la versión anterior en lugar de la nueva. Y sí, también con nuestros programas, y resulta curioso porque a todos los efectos la aplicación está viendo una serie de DLL situadas en C:WindowsSystem32.

***

C++/CLI y .NET. Sólo nos queda ya la última posibilidad. Si estamos usando C++/CLI y .NET debemos tener instalada la versión adecuada en el ordenador de destino. A veces el programador novel no sabe que está usándolo, pero si utilizas Forms y demás, seguro que lo estás haciendo.

Aquí las apuestas suben, porque a toda la complicación anterior añadimos el .NET. En general en este caso solemos tener un “Error 135” (pero no siempre), que quiere decir que el cargador del sistema no entiende la firma del ejecutable y no sabe qué entorno de ejecución aplicar.

Una vez instalada la versión adecuada del .NET, si nos sigue saltando algún error como los del principio, debemos aplicar lo mismo que hasta ahora. Es decir, pasar a Release (aunque el .NET no lo exige, ciertas partes del propio C++/CLI sí) e instalar el runtime adecuado o cambiar a compilación estática.

A veces no podremos pasar a compilación estática porque por ejemplo la opción “/clr:pure” lo impide y tendremos que instalar el runtime por narices. También puede ocurrir que hagamos dos programas y uno nos exija el runtime y el otro no. Dependerá de qué estemos usando en C++/CLI y si empleamos interoperación por atributos, del tipo IJW (usar C++ y C++/CLI de forma mixta), ambas o ninguna. Ver mi entrada ¿Qué es C++ y qué es C++/CLI?).

***

Instalador. Y siempre, siempre, podremos crearnos un instalador, que añadirá todos los runtimes y demás zarandajas de forma que llevando todo eso al ordenador cliente, y una vez instalado, nuestro programa funcionará perfectamente.

El problema aquí es que mucha gente no quiere instaladores por la basura que introducen en el sistema, y hay empresas que exigen que los programas no necesiten ni instalación ni runtimes.

También, a veces, hacer un instalador para un programa sencillito que nos han pedido puede ser matar moscas a cañonazos y tardamos más tiempo haciendo el instalador que el programa. Además, a veces hacer un instalador es un oficio en sí.

7 comentarios sobre “Mi programa recién creado no se ejecuta en otro ordenador”

  1. hola saber ojala me soluciones mi problema, en mi visual c++ 6.0 no ejecuta mis codigos realizo todo lo que debe ser pero aparece obj. errros 1
    por fa quisiera saber por que no funviona ..

    a tengo instalado el expres 2008 tendra que ver algo

  2. Y como somos magos, en un alarde de adivinación, sabemos qué error te da.

    No, el que tengas ambas versiones no debe tener nada que ver, siempre que primero hayas instalado Visual C++ 6 y luego la 2008.

  3. Tengo instalado el .NET el la máquina cliente, no me salta ninguna ventana de error. El programa trabaja con un puerto haciendo transmisión y recepción, esta parte de la aplicacion es la que no funciona en la máquina cliente.Que solución existe?

  4. He realizado un programa y me funciona bien en el ambiente de desarrollo, de IDE. Hice el instalador, lo puse las librerias, NetFrameWk 3.5, y los compomentes de Crystal Report. osea no le falta nada. Pero al ejecutarlo en otro PC no corre, no muestra mensaje de error, ni nada..
    Lo instalé en el mismo equipo que lo desarrollé pensando que faltaban librerias y en el mí sí correría, y tampoco se ejecutó… ¿Que faltará?
    Por favor… llevo una semana buscando la solucion… gracias

Responder a anonymous Cancelar respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *