domingo, abril 27, 2008

El caso del espacio en negro en Agregar o quitar programas

Uno de mis sistemas XP estaba experimentando un problema extraño: Al abrir Agregar o quitar programas noté un espacio de color negro bastante grande, con franjas de color blanco, entre dos programas. La siguiente captura de pantalla lo muestra:


Hace tiempo ya había analizado otro caso parecido. En aquella ocasión se mostraba un espacio enorme en blanco, y eso se debe a que la ruta del icono a mostrar contiene un "-1". (La función ExtractIcon devuelve el tamaño total de iconos disponibles en el ejecutable si se le pasa como índice para el icono un "-1").

Así pues, lo primero que hice fue descubrir qué aplicación era la culpable de ese espacio enorme en color negro. Lo que hice en primer lugar es anotar cuidadosamente la lista de aplicaciones que se mostraban correctamente y comparé dicha lista con la de la clave de Registro HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall. La aplicación que faltaba, y por tanto culpable de ese espacio en negro resultaba ser TI-Graph Link, perteneciente a una calculadora gráfica de Texas Instruments. Decidí investigar un poco más en detalle para desvelar el misterio.


Por diseño, la herramienta Agregar o quitar programas obtiene los iconos de las aplicaciones de la lista de dos maneras:


  • Si un valor DisplayIcon está presente en la subclave correspondiente de Uninstall, lo usará preferentemente.

  • Si no hay valor DisplayIcon, intentará obtener el icono desde la ruta del ejecutable del programa.

Se trataba del segundo caso, así que arranqué Process Monitor (http://technet.microsoft.com/en-us/sysinternals/bb896645.aspx?PHPSESSID=d926bdd849b5aab10f7263dd7f5904f2) y establecí como filtro la ruta ejecutable del programa de la calculadora gráfica.


¿Qué tiene de especial ese ejecutable? Al examinarlo un poco, observé que se trata de un ejecutable de 16 bits. Según la documentación de MSDN, ExtractIcon no soporta la extracción de iconos desde ejecutables de 16 bits, así que empecé a sospechar.


Desde el propio shell de Windows intenté cambiar el icono de un acceso directo a la aplicación y seleccioné el icono que ocupa la posición 0 dentro del ejecutable. Aparentemente se veía correctamente, pero recordé rápidamente que Agregar o quitar programas extrae un icono pequeño (10x10) para mostrarlo en la lista. Lo que hice, pues, es cambiar la vista de la carpeta a Lista, para así forzar la extracción de un icono pequeño. ¡Bingo!, el resultado fue el de la siguiente captura de pantalla:



Al parecer, la función de extracción de iconos tiene problemas para proporcionar un icono de tamaño pequeño para el recurso que ocupa el índice 0 del ejecutable. Como quería investigar un poco más, primero me aseguré de qué función se trata. Para ello, seleccioné con el botón derecho una de las entradas del listado de Process Monitor y pulsar sobre Stack con el objetivo de visualizar la pila de llamadas del proceso Rundll32.exe encargado de abrir la herramienta Agregar o quitar programas.

La función que me llamó la atención fue ExtractIconFromEXE, así que decidí empezar a depurar desde ahí.


Al abrir el ejecutable en Windbg, establecí el correspondiente breakpoint y ejecuté. Como esa función es llamada para diferentes programas de la lista, tuve que idear una forma que me permitiera saber cuándo había llegado al punto en el que se extrae el icono del ejecutable de la calculadora. Como la función recibe un handle al ejecutable, con Process Explorer examiné la lista de handles hastá que vi por ahí el ejecutable en cuestión, tal y como se muestra en la imagen.


Cuando llegué al breakpoint, examiné la pila con el comando dd esp:


0:000> g

Breakpoint 0 hit

eax=00005a4d ebx=00000000 ecx=00000000 edx=7c91eb94 esi=7c810760 edi=10000080eip=7e3a325a esp=0007d25c ebp=0007d6cc iopl=0 nv up ei pl zr na pe nccs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246

USER32!ExtractIconFromEXE:7e3a325a 6a38 push 38h

0:000> dd esp

0007d25c 7e3a2f7d 000003e8 00000000 00000010

0007d26c 00000010 0007d74c 00000000 00000001

0007d27c 00000000 00000000 00000000 0007d74c

0007d28c 3df27be2 01c8a88b 00000000 011afd3c

0007d29c 0000000c 00000000 0007d74c 000003e8

0007d2ac 003a0043 0041005c 00630072 00690068

0007d2bc 006f0076 00200073 00650064 00700020

0007d2cc 006f0072 00720067 006d0061 005c0061


El primer valor, 7e3a2f7d, es la dirección de retorno; el siguiente, el primer parámetro, y así sucesivamente. Como puede ver, el tamaño del icono es 10x10, que corresponde al tercer y cuarto parámetro de la función. ¿Qué ocurriría si la función en lugar de pedir un icono 10x10 pidiese uno 20x20, por ejemplo? Según las pruebas que hice anteriormente con la función Cambiar icono, debería funcionar, pues es solo la extracción de iconos pequeños la que falla.


Para modificar el contenido de una posición de memoria, puede usar el comando del depurador ed dirección contenido. Tenga mucho cuidado, pues si modifica posiciones de memoria puede hacer que el programa que está siendo depurado no haga lo que debe.


0:000> ed esp+c 20


0:000> ed esp+10 20


0:000> dd esp


0007d25c 7e3a2f7d 000003e8 00000000 00000020


0007d26c 00000020 0007d74c 00000000 00000001


0007d27c 00000000 00000000 00000000 0007d74c


0007d28c 3df27be2 01c8a88b 00000000 011afd3c


0007d29c 0000000c 00000000 0007d74c 000003e8


0007d2ac 003a0043 0041005c 00630072 00690068


0007d2bc 006f0076 00200073 00650064 00700020


0007d2cc 006f0072 00720067 006d0061 005c0061


Una vez modificado el tamaño de icono pedido, dejé que la aplicación se ejecutara completamente y este fue el resultado obtenido:


¡Bingo! Ya pude confirmar que un icono un poco más grande es extraído correctamente.

En resumen:


  • Agregar o quitar programas tiene que extraer un icono para cada uno de los programas que conforman la lista de programas instalados.

  • La función implicada es ExtractIconEx, que, dependiendo del tipo de fichero (EXE, DLL, ICO, etc.) llama a una u otra función. En el caso de ejecutables se trata de la función ExtractIconFromEXE (no documentada).

  • La documentación nos indica que no se soporta la extracción de iconos de ejecutables NE (16 bits). En nuestro caso, la extracción del icono de índice 0 del ejecutable en tamaño pequeño (10x10) es defectuosa. Agregar o quitar programas por diseño quiere obtener un icono 10x10.

  • Al "forzar" la extracción de un icono un poco mayor (20x20), todo funciona correctamente.

Una vez desvelado el misterio, me puse en contacto con el fabricante para ver si había disponible alguna actualización para su software (que ya no fuera de 16 bits, a ser posible).


¡Caso cerrado!

5 comentarios:

Samu dijo...

Me ha gustado esta entrada. Como no soy experto no sabría hacer muchas de las cosas que comentas, pero sí me he enterado del hilo. Resulta interesante ver cómo has resuelto este problema real. Es como un CSI informático :P
Te animo a que publiques más entradas de este tipo :)

Un saludo.

Anónimo dijo...

a mi pasa esto con el mixer de vista
http://img523.imageshack.us/my.php?image=strangefx2.jpg
sera el mismo caso?

Daniel Martín dijo...

No, no es el mismo caso. Tu caso parece estar documentado en la KB de Microsoft: http://support.microsoft.com/kb/938308/en-us

Anónimo dijo...

Nada sigo con el mismo problema.
Hay algo que pueda hacer? Sera por el aero?
Tengo vista x64 sp1.

Anónimo dijo...

Gracias a su explicación he encontrado el problema a mi caso.

Un programa en la lista del registro sin la clave DisplayIcon generaba el error ( Lo he hallado porque no aparecia en la lista de agregar y quitar programas como indicaba ), he creado la clave y añadido la ruta a un icono que me convenia y resuelto.

Gracias. Un saludo.