Vamos a programar #23 - Haciendo un instalador en NSIS #2 - Datos utiles.

Hola de nuevo a todos, el día de hoy vamos a continuar con el tema de los instaladores, en el post anterior vimos lo básico de como escribir el código en NSIS. Hoy veremos unas cuantas cosas que nos ayudaran a hacer un instalador más detallado.

Tip #1 - El Desintalador.

Cuando escribimos el instalador, lo primero que nos debemos de preguntar, es si el usuario no va a cambiar de opinión, si en algun momento decide ya no usar la aplicación, querrá borrarla de su computadora. Y para esto, recurrirá al des-instalador. La manera más sencilla de hacerlo, es mediante el panel de control.
Si observamos bien, vemos que hay una entrada a nuestro des-instalador, pero no sabemos nada más.
Para poder identificar de manera más precisa cual es nuestro programa debemos de incluir información que se encargue de esa tarea.
Este es un ejeplo de un programa que muestra información que puede ser de ayuda para el usuario.
Entre los detalles más importantes que debemos de incluir, están los siguiente:
  • Nombre que identifique al des-instalador.
  • Ruta en la que se encuentra.
  • Nombre del publicador.
  • Icono.
  • Tamaño de la instalación.
  • Versión del programa.
  • Vinculo a la página del creador del programa.
  • Vinculo de ayuda del programa.
Para poder hacerlo, necesitamos agregar algunas entradas al registro. Windows tiene una seccion dedicada a los instaladores, y ademas a definido una serie de entradas por default. si incluimos la informacion de estás, windows la mostrará, si no lo hacemos, el instalador aparecerá cómo en la primer imagen; sin información.
Sabiendo lo anterior, en NSIS podemos escribir claves en el registro durante el proceso de instalación. Para agregar la información que requerimos podemos agregar código como el siguiente:

WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Vencoder" "DisplayName" "Vencoder 2.0"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\VEncoder" "UninstallString" '"$INSTDIR\uninstall.exe"'
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\VEncoder" "Publisher" "MDev 2015"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\VEncoder" "DisplayIcon" "$INSTDIR\Main.ico"
${GetSize} "$INSTDIR" "/M=*.* /S=0K " $0 $1 $2
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\VEncoder" "EstimatedSize" "$0"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\VEncoder" "DisplayVersion" "2.1" 
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\VEncoder" "URLInfoAbout" "http://xworkforall.blogspot.com"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\VEncoder" "HelpLink" "http://xworkforall.blogspot.com/2016/07/el-video-correcto-2.html"
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\VEncoder" "NoModify" 1
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\VEncoder" "NoRepair" 1


Las lineas de código scriben entradas en el registro de Windows la instruccion "WriteRegStr" escribe un valor de cadena (texto por lo general), la instruccion WriteRegDWORD, escribe un valor DWORD.
En orden de aparicion, las lineas del código anterior se usan para representar la siguiente información:

  1. Nombre que se mostrara en la entrada al des-instalador.
  2. Ruta en la que se encuentra el des-instalador, siempre debe de ir entre comillas.
  3. Información del publicador (empresa).
  4. Icono que se usara para mostrarse en la lista de programas.
  5. ${GetSize} Es una función de NSIS que se usa para obtener el tamaño de un folder o archivo. Está la veremos con mayor detalle un poco más adelante, pero en este caso se usa para obtener el tamaño de la instalación. Para que funcione es importante que previamente ya se hayan copiado todos los archivos a la carpeta de instalación.
  6. Cuando ya obtuvimos el tamaño de la instalación; está función escribe esa información. Para que funcione, el tamaño debe de estar en KiloBytes.
  7. Muestra la versión del programa.
  8. Vinculo a una dirección de internet "Acerca de".
  9. Vinculo a una dirección con ayuda para el programa.
  10. Cuando NO se tiene la opcion de modificar los componente ya instalados.
  11. Cuando NO se tiene la opcion de reparar la instalacion.
Para que des-instalador funcione, minimamente se deben de escribir las entradas "DisplayName" y "UninstallString", de ahí en fuera las otras son totalmente opcionales, pero siempre es recomendable incluir al menos todas las anteriores para tener el mínimo de información necesaria.

Al escribir todas las entradas anteriores, cuando vayamos a la seccion de des-instalar programas, nuestra aplicación tendrá el siguiente aspecto:

Y bien, es todo por hoy, en el siguiente post veremos cómo asociar una extensión con nuestro programa, recuerda que el código de este post lo puedes bajar de mi dropbox, viene con un ejemplo de un instalador para VEncoder 2.

P.D Hay que adaptar el código de VEncoder para funcionar desde "Program Files", ya que cuando se usa desde está ruta, requiere  de algunos cambios.

Los leo luego.

Vamos a programar #22 - Haciendo un instalador en NSIS.

Hola de nuevo a todos, el día de hoy les voy a mostrar el código detrás de un instalador que hice ya hace tiempo.
Cómo muchos de ustedes sabrán. Yo soy programador y he hecho programas de muchos tipos, algunos más complejos que otros. Dependiendo del grado de complejidad muchos de ellos vienen divididos, es decir; algunas funciones criticas las hago en bibliotecas DLL y al momento de distribuir el programa, cuando llega al usuario final, en ocasiones los usuarios "pierden alguna" DLL o algún recurso que el programa necesita. Inicialmente solo empaquetaba todo en un archivo ZIP y en el programa principal, programaba que hiciera los cambios en el sistema que fueran necesarios para que tdo funcionara correctamente, además siempre incluía un manual de ayuda (que al parecer nadie leía) para que el usuario pudiera arreglar los problemas comunes.
Pero cómo siempre aparecían errores decidí buscar una manera de minimizarlos lo más posible. Para eso busque la forma de hacer un instalador, y poder distribuir todo lo necesario en un solo paquete y además hacer los ajustes necesarios para que el usuario solo ejecutará la aplicación y evitar disgustos.

Al inicio probé con una herramienta que se llama Create Install Free, al inicio era gratuita, funcionaba de maravilla, permitía agregar y registrar bibliotecas en windows, fuentes y controles OCX, era una solución muy buena. Tiempo después paso a tener costo, y me hubiera gustado comprarlo, pero no tenía una tarjeta de crédito valida para realizar la compra. Entonces dejé de usarlo y busqué otra forma de crear un paquete de instalación.

Así fue cómo llegué a NSIS.


Nullsoft Scriptable Install System (NSIS) es un "manejador de script" Windows de código abierto con requerimientos mínimos, desarrollado por Nullsoft, los creadores de Winamp. NSIS ha crecido en popularidad como una alternativa al uso extenso de productos comerciales como InstallShield y es actualmente utilizado para un sin número de aplicaciones distribuidas a través de Internet.
NSIS es liberado bajo una combinación de licencias de software libre, principalmente la licencia de zlib/libpng, de esta forma haciendo a NSIS software libre..

Creando un instalador.

Lo primero que vamos a hacer es descargar el programa, una vez que está descargado e instalado, vamos a escribir un archivo de texto con notepad++ o con cualquier editor de texto plano.
Hoy veremos directamente cómo hacer uso de Nsis Modern User Interface. Prefiero ir directamente a está parte porque la mayoría de las funciones o procesos que vamos a realizar,  se pueden llevar a cabo con el uso de este plug-in que viene integrado por default. En adición, este se encarga de que todas las ventanas del instalador tengan la misma apariencia que el sistema operativo.

Una vez que tenemos todo listo vamos a empezar a escribir el código. Tomemos está parte de código y revisemos cada una de las partes que lo componen.

!include "MUI2.nsh"
!include "FileFunc.nsh"
!include "Library.nsh"

SetCompressor lzma
SetOverwrite on
SetDatablockOptimize on
!define VERSION "2.0"
Name "VEncoder 2"

OutFile "Setup.exe"
InstallDir "$PROGRAMFILES\Vencoder 2"
InstallDirRegKey HKCU "Software\MPM\VENCODER 2" ""
ShowInstDetails show
RequestExecutionLevel admin
CRCCheck on
XPStyle on

La descripcion del código anterior es la siguiente:

  • Lo primero que hacemos es incluir "MUI2.nsh" que es la "librería" que contiene todas las funciones del plugin MUI, en NSIS cuando se quiera incluir una librería, se debe de hacer usando la palabra reservada "!include" seguida por el nombre del plug-in ó "librería", para NSIS, estás tienen una extension "nsh".
  • SetCompressor, selecciona el compresor que se usará, en este caso usaremos el compresor LZMA, pero ademas de este, podemos usar ZLIB o BZIP2, por lo general yo uso este porque a mi criterio; ofrece mayores niveles de compresión..
  • SetOverWrite, Sirve para indicar si se deben de sobre-escribir los archivos que ya existan en el lugar de destino..
  • SetDataBlockOptimize. Sirve para hacer optimizaciones en el bloque de datos. Si este ya contiene un dato, en lugar de agregarlo de nuevo, simplemente se hace referencia a el. Puede ayudar a ahorrar un poco de espacio. (NSIS: SetDataBlockOptimize)
  • Definimos la versión de la aplicación.
  • Establecemos el nombre (es este caso VEncoder 2)
  • Establecemos el nombre de salida para el instalador una vez que ya está compilado. Puede ser cualquiera.
  • Establecemos cual va a ser el directorio de salida para los archivos que se van a instalar, hacemos uso de $PROGRAMFILES, que es una palabra reservada de nsis que generalmente se refiere a "C:\Program Files (x86)\" o la carpeta que contiene todos los programas instalados.
  • InstallDirRegKey. Obtiene la ruta de instalación del registro de windows. Cabe aclarar que esta solo tendrá un valor SOLO si anteriormente ya hemos instalado algún programa que haga uso de esta.
  • ShowInstDetails. Cuando se lleva a cabo el proceso de instalación, muestra cual es archivo que se está copiando.
  • RequestExecutionLevel. Indica el nivel de ejecución que requiere el instalador. Pueden ser los siguiente valores: none, user, highest, admin.
  • CRCCheck. Comprueba la integridad del instalador antes de empezar la instalación.
  • XPStyleOn. Habilita que el instalador tenga la apariencia de Windows.

Por lo general el pedazo de código anterior es un buen ejemplo de cómo empezar un script, solamente debes de modificar las partes correspondientes a tu aplicación a instalar.

Ahora sigue definir todas las páginas que componen el instalador.
Página de selección de componentes.
Cada instalador está compuesto por páginas, cada una de ellas tiene la función de guiar paso a paso al usuario. Para hacer uso de ellas hay que "declararlas" en código de nsis. Un instalador "básico" puede usar el siguiente código para definir las páginas más comunes.

!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE "COPYING.txt"
!insertmacro MUI_PAGE_COMPONENTS
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_UNPAGE_WELCOME
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_LANGUAGE "Spanish"

El código anterior crea las siguientes páginas:
MUI_PAGE_WELCOME: Crea la página que contiene un mensaje de bienvenida.
MUI_PAGE_LICENSE "COPYING.txt": Crea la página que muestra un archivo de texto llamado "Copying.txt" cómo licencia, se puede usar cualquier fichero de texto y asignarlo cómo parámetro.

MUI_PAGE_COMPONENTS: Muestra una página que permite seleccionar que secciones son las que queremos instalar.


MUI_PAGE_DIRECTORY: Muestra la página que nos permite la selección de un folder para llevar a cabo la instalación.

MUI_PAGE_INSTFILES: Muestra el progreso de la instalacion.


MUI_PAGE_FINISH: Muestra un mensaje al finalizar la instalación.

Ademas las siguientes intrucciones MUI_UNPAGE_WELCOME, MUI_UNPAGE_INSTFILES crean las páginas correspondientes en el desisnstalador.
Luego hay que indicar que todas las páginas usan el idioma español, para eso usamos la siguiente instruccion MUI_LANGUAGE "Spanish", en donde "spanish" puede ser cualquier idioma soportado por NSIS,
Si miramos la ultima imagen, veremos un texto que esta en negritas y dice: "Las instalación ha terminado". Este string hay que definirlo, de hecho antes de crear las páginas, hay que definir los strings para todas las partes de cada pagina, el código que hace eso no lo pongo aquí, porque haria muy extenso el Post, pero al final agrego el código fuente para que lo revises.

NSIS trabaja con apartados llamados secciones, en ellas es donde se agregan los archivos que queremos incluir en la instalación, la ventaja de esto es que podemos crear varios apartados en los cuales se incluyan los archivos que son realmente importantes en la aplicación y otra sección en la que se incluyan archivos miscelanéos.
Para este caso tenemos tres secciones, 2 de ellas obligatorias y una de ellas opcional.

Section "Principal" MainSec
##Indicamos que está seccion es obligatoria
SectionIn RO
##Establecemos la carpeta de destino
SetOutPath "$INSTDIR"
File "Copying.txt"
File "Main.Ico"
File "MediaInfo.dll"
File "MediaInfoNET.dll"
File "Vencoder 2.exe"
File "Vepx.ico"
File "ListViewEx.dll"
SetOutPath $INSTDIR\Doc
File /r doc\*.*
SectionEnd

Section "Archivos adicionales" AdditionalSec
SetOutPath "$DOCUMENTS\Profiles"
File "MB300.Vepx"
File "PSP.vepx"
File "PSVita.vepx"
File "Screen HD.Vepx"
File "Screen FHD.Vepx"
SectionEnd

Con el código previo, definimos dos secciones, cada "section" recibe 2 parámetros, uno de ellos es una descripción y el otro es un nombre que servirá para identificar cada una de las secciones.
Para la primera sección indicamos que es obligatoria con "SectionIn RO" cuando se establece, en la página de selección de componentes, aparecerá un casilla de selección de solo de lectura, cuando no se incluye, el usuario puede marcar o des-marcar la casilla de selección, así indicando que esa sección no se quiere instalar.
En la seccion "MainSec", después de que establecimos que sea obligatoria, agregamos todos los archivos necesarios de nuestra aplicación, primero establecemos que todos se copiaran a la carpeta de instalacion "$INSTDIR" ("C:\Program Files (x86)\VENcoder2\"), luego ponemos una lista en la que cada archivo inicia con la palabra clave "File" seguida por el nombre del archivo que queremos incluir, un poco más abajo encontramos la instrucción "SetOutPath" nuevamente con la ruta "$INSTDIR\Doc", esto indica que se debe de cambiar la salida de los archivo a está nueva dirección, luego al comando "File" le agregamos el parámetro "\r" que indica que buscara de forma recursiva lo archivos en la carpeta doc.

Cada seccion debe de incluir una descripcion, para definirlas, se usa código similar al siguiente:

!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
!insertmacro MUI_DESCRIPTION_TEXT ${MainSec} "Contiene todos los archivos escenciales de la aplicacion"
!insertmacro MUI_DESCRIPTION_TEXT ${LinkSec} "Accesos directos a la aplicacion)"
!insertmacro MUI_DESCRIPTION_TEXT ${AdditionalSec} "Archivos de perfil con las configuraciones mas frecuentes (opcional)"
!insertmacro MUI_FUNCTION_DESCRIPTION_END

Despues llamamos a las macros que se encarhgan de crear las descripciones; MUI_FUNCTION_DESCRIPTION_BEGIN, recibe dos parámetros, uno de ellos es el nombre de la seccion a la cual se quiere poner su descripción y el segundo, es la cadena de texto.

Lo que se mostró en el post son las partes principales, pero el código completo junto con lo archivos, como siempre; lo dejo en mi dropbox para que lo descargues y lo revises. Incluye todo lo necesario para crear un instalador funcional para VEncoder.
En el siguiente Post vamos a ver alguna funciones no vitales, pero que resultan muy útiles.

Por ahora es todo, los leo luego

Vamos a programar #21 - Formateando código para HTML.

Hola de nuevo a todos. El día de hoy traigo una aplicación muy corta. Cómo todos sabrán, la mayor parte de este blog trata sobre cosas de programación, por lo que es muy común que publique código fuente en variedad de lenguajes.
Pero al momento de publicar algo, surge un pequeño problema, al copiar el código del editor al blog, este pierde todo el formato. Actualmente uso unos scripts que permiten darle color a la sintaxis, pero cosas como la indentación, se pierden. En lo personal, siempre me gusta ver código que este visiblemente bien, es decir; que se distinga en "donde estás". Un indicador muy claro, es la sangría. Si vez que todo está a la misma distancia, resulta fácil ver que todo es parte de un "if" o parte de un "while".
Para solucionar todo esto y poder formatear el código de una forma más rapida, decidí hacer una aplicación que se encargue del trabajo sucio.

La aplicación es realmente simple, de hecho solo son 10 lineas de código, es una versión beta, y lo único que hace es convertir todos los caracteres que no están admitidos en el html (como "< o >" ).
Dentro las funciones que nos proporciona el api de .NET existe a siguiente función:

RtBResultCode.Text = WebUtility.HtmlEncode(RTBInputCode.Text);

Está función se encuentra en el espacio System.Net; y hace exactamente lo que queremos, formatear todo el código para que lo podamos usar en el lenguaje HTML. Pero bueno si solo quisiera hacer eso no sería una solución a la indentación, de hecho si revisamos el siguiente código (que viene de este blog)
<resources>
<string name="app_name">Calculador de resistencias</string>
 <string-array name="Colores1">
 <item>Negro</item>
 <item>cafe</item>
 <item>Rojo</item>
 <item>Naranja</item>
 <item>Amarillo</item>
 <item>Verde</item>
 <item>Azul</item>
 <item>Violeta</item>
 <item>Gris</item>
 <item>Blanco</item>
 </string-array>
 <string-array name="Colores2">
 <item>Dorado</item>
 <item>Plata</item>
 </string-array>
</resources>


Pero eso no es lo que quiero, si nos damos cuenta todas las tabulaciones han sido reemplazadas por espacios que visualmente son más pequeños.
En cambio cuan usamos el código producido por el programa; lucirá de la siguiente forma:

<resources>
<string name="app_name">Calculador de resistencias</string>
 <string-array name="Colores1">
  <item>Negro</item>
  <item>cafe</item>
  <item>Rojo</item>
  <item>Naranja</item>
  <item>Amarillo</item>
  <item>Verde</item>
  <item>Azul</item>
  <item>Violeta</item>
  <item>Gris</item>
  <item>Blanco</item>
 </string-array>
 <string-array name="Colores2">
  <item>Dorado</item>
  <item>Plata</item>
 </string-array>
</resources>

Por ahora el código solo hace eso, pero si por ejemplo el código tiene espacios en blanco, no obtendremos un resultado diferente al del primer caso.

En la siguiente versión vamos implementar funciones que se encarguen de hacer todo de forma automatica. Si lo que quieres es publicar código, lo ,mejor es que luzca, que al momento de echar un vistazo rápido, puedas ver que al menos está bien estructurado.
 Además lo dejo así porque ando un poco corto de tiempo, pero espero ya no estarlo para la próxima semana. Como siempre, el programa lo puedes bajar de dropbox, en cuanto lo actualice haré el siguiente Post.

Por ahora es todo. Los leo luego.

Vamos a programar #20 - SlideShow en JavaScript + CSS

Hola de nuevo a todos, hace algunos días contacté con alguien que me encontré en Internet con la intención de hacernos publicidad mutuamente, a lo mejor ya viste el banner que puse en la esquina superior derecha.
EL problema es que no encontraba una forma de acomodar todas la cosas que ya había en la pagina principal, ¿Cómo debería acomodar esto?, la opción más sencilla era insertar todas las cosas una tras otra y esperar a que la pagina no se extendiera tanto. Si se pone una imagen diferente para cada página "amiga", si pongo al menos 3, ocuparía un buen espacio.
Para solucionar este problema decidí hacer un pequeño script que se encargue de intercambiar imágenes, así en lugar de poner 4 o 5 imágenes y desplazar todo 600 pixeles hacia abajo, puedo poner n numero de imágenes en un solo espacio de 320 * 110 (o el tamaño que sea la imagen más grande).

Para nuestro ejemplo tomemos las siguientes imágenes:




Al inicio estaba buscando una forma de hacerlo totalmente en javascript, pero alguien me dijo: "Si usas CSS3, es más sencillo". Entonces decidí hacer uso de ambas para lograrlo.

El código CSS.

El código CSS que vamos a usar  es el siguiente:

@keyframes FadeFromRight{
 from{
  right:-150px;
  opacity:0
 }
 to{
  right:0;
  opacity:1}
 }

.FadeImage{
 position:relative;
 -webkit-animation:FadeFromRight 0.5s;
 animation:FadeFromRight 0.5s
}
img{border-style:none}


Primeramente creamos una animación a la que llamamos "FadeFromRight", lo que va a hacer es crear el objeto desde la posición -150px con una opacidad del 0 (Transparente)  y hará una animación en la que se moverá hasta posición 0 y ademas, su nivel de opacidad sera de 1 (solido).

Después creamos una clase llamada ".FadeImage", en la cual ponemos que la animación que haga la imagen (o lo que sea que haga uso de esta clase), durará 0.5s.

Finalmente definimos un estilo para la etiqueta IMG.

El código HTML.


Luego insertaremos las Imágenes que vayamos a usar, Es preferible que todas estén dentro de una capa, luego a cada una de las imágenes que conformaran el Slide Show, les pondremos el mismo nombre de clase, suponiendo que las imágenes que vayamos a usar se llaman Ban1, Ban2, Ban3 y Ban4, el código html para insertarlas luciria como el siguiente:


<div>
 <img class="Banners FadeImage" src = "BAN1.jpg"/>
 <img class="Banners FadeImage" src = "BAN2.jpg"/>
 <img class="Banners FadeImage" src = "BAN3.jpg"/>
 <img class="Banners FadeImage" src = "BAN4.jpg"/>
</div>

Con el Código anterior estamos indicando que las 4 imágenes pertenecen a la clase "Banners" y también a la clase "FadeImage", con ello tomará los estilos que definimos antes para "FadeImage" y "IMG".

El Código JavaScript.

Para lograr el efecto que deseamos, hay que modificar los estilos, javascript nos permite hacerlo sin tanto problema. El código que nos permite cambiar los estilo de las imágenes es el siguiente:

var myIndex = 0;
ChangeImage();

function ChangeImage() {
 var i;
 var x = document.getElementsByClassName("Banners");
 for (i = 0; i < x.length; i++) {
  x[i].style.display = "none";
 }
 myIndex++;
 if (myIndex > x.length) {myIndex = 1}
 x[myIndex-1].style.display = "block";
 setTimeout(ChangeImage, 5000);
}


El código anterior recorrerá todos los elementos que estén en la clase "Banners" y a todos le asignara el estilo "none" que hará que los elementos no se vean, luego comprobará que el valor iterador esté en lo limites  de la cantidad de elementos que la clase "Banners" posea, después al final, se establece que la función "ChangeImage" se ejecutará cada 5000ms (5 segundos).

Tras implementar el código anterior obtendremos algo como lo siguiente (Si solo ves cuatro imágenes fijas, prueba actualizar tu navegador):



No está demás recordar que para que esto funcione, necesitamos un navegador no tan viejo, ademas de que se debe de hacer referencia a las imágenes usando su dirección absoluta. Si quieres hacer uso del código así tal cual, debes de asegurarte primero de que las imágenes están en el mismo directorio y que se llaman Ban1, Ban2, etc.

Cómo siempre, el código completo lo puedes descargar desde mi dropbox, recuerda que puedes usarlo, modificarlo o hacerle lo que quieras, si te resulta útil, no olvides mencionar que lo encontraste en el blog de Xwork.

Para terminar. Si no has visitado la página Gent0.com. ¡Qué estás esperando?!, hay buen información acerca de está distro de linux.

Bien, por ahora es todo. Los leo luego.

P,D. H071?? (solo para los que saben).