jueves, 18 de diciembre de 2008

Hace un tiempo les comente de una aplicación con un ActiveX WebBrowser basado en Gecko y Mozilla. Esta aplicación esta hecha en .NET (NetFramework de Microsoft) con Windows Forms y el control principal, pues es un ActiveX. Obviamente esta aplicación es para Windows, pero por ciertas necesidades que aquí no vienen a cuento pero requieren que el explorador quede abierto después de una manipulación inicial al contenido de la página mostrada, se requirio un navegador así pero en Linux, entonces ¿Podria hacer una aplicación multiplataforma en lugar de dos especializadas? Me aventure a intentarlo. Aclaro desde ahora que aquí solo incluyo un ejemplo simple de como montar crear un explorador basico y nada más.


Empece por contemplar posibilidades para hacerlo. Hay algunas como por ejemplo WebControl con Mono 2.0 porque en esta versión se incluyo en el framework un control basado en Mozilla a modo de emular la manera en que el NetFramework de Microsoft puede manejar el motor de Internet Explorer para incluir navegación web en controles windows forms. Mono 2.0 incluye soporte para windows forms permitiendo usar aplicaciones windows como multiplataforma.


Del caso anterior no me gusto mucho el uso de Windows forms porque después de todo en mi caso particular mi prioridad es que funcione en linux me parecio mejor algo con GTK así que escogí probar a incluir un control de Gecko en una ventana GTK. Para esto use el control Gecko#. Hay un control similar llamado GtkMozEmbed que también permite incluir browsers en aplicaciones GTK pero me parecio que esta algo verde, aunque no la probe solo lei la documentación de su página y reconocen que esta limitada. No encontre como manejar el DOM, solo cargar páginas.


También existe en desarrollo un control para incluir WebKit en ventanas GTK#. WebKit también es un motor de navegadores web y es usado por Safari y puede ser incluido en las aplicaciones .NET consiguiendolo para agregarlo como referencia en la aplicación. El componente se llama webkit-sharp para quien quiera profundizar en sus posibilidades. Esta opción la encontre cuando ya tenia un poco avanzado el desarrollo y por eso no indague más pero me parece bueno mencionarla también.

AMBIENTACIÓN Y REQUERIMIENTOS

Desarrollo esta aplicación usando SharpDevelop (2.2.1), compilando con Mono 2.0 y creando el entorno grafico con GTK#; todo sobre Windows XP en español, el sistema operativo que instalaron en mi trabajo. Extraño el diseñador de ventanas Stetic que trae MonoDevelop pero ya que, si lo necesito en mi casa tengo Ubuntu con MonoDevelop y ambiente grafico Gnome que me servira para pruebas complementarias de compatibilidad y también dispongo de una computadora con la distribución slackware con el ambiente grafico KDE 3.4 donde también probare el desarrollo. Uhm, me falta un Mac... ademas no me atrae nada de nada seguirle con esto en casa, salvo la curiosidad de ver si funciona o no, cosa que no me llevará mucho tiempo.


Dando por hecho que es obvio que hay que tener instalado MONO (estoy usando el 2.0.1) y de preferencia un IDE como MonoDevelop (GNU/Linux) o Sharp Develop (Windows) para manejarlo, aunque te las podrias arreglar con un editor de texto y compilando por linea de comandos si te gusta "lo retro", te gusta presumir que programas "como los hombres" o si por alguna razón particular no hay una mejor opción en tu caso.


Para poder compilar adecuadamente con SharpDevelop y Mono en Windows XP en español tuve que hacer unos ajustes a las variables de entorno de Windows XP. Hay que crear la variable MONO_EXTERNAL_ENCODINGS para que mono trabaje perfectamente con una PC que no tenga el sistema operativo por default en ingles. si, ya se, que chafa, decia, es así:


Ah, no faltara quien no sepa donde y como poner las variables así que... Click derecho a Mi PC -> Propiedades -> Pestaña Opciones Avanzadas -> Variables de entorno. Con eso les aparecera la ventana donde poner variables de entorno para tu usuario y globales para todos. Si una misma variable existe en ambos, se le da prioridad a la del usuario sobre la global. La global solo se toma si en tu usuario no existe. Toma en cuenta esto. En mi caso declare a MONO_EXTERNAL_ENCODINGS como global.


Variables de entorno


Variables de entorno


Todavia no cierres la ventana, aun hay unas variables de entorno que necesite modificar. Primero la variable Path, donde agregue la dirección del directorio bin de mi instalación de MONO. Las otras variables de entorno que modifique son TEMP y TMP. Estas ultimas indican donde Windows pondra y buscará los archivos temporales que valla creando. En mi caso lo cambie porque en las variables de entorno de mi usuario, al estar el sistema operativo en español, me dejaba los archivos temporales en C:\Documents and Settings\adan\Configuración local\Temp. Esto es un problema por los acentos y espacios en la ruta por lo mismo de que al parecer no validaron que hay programadores fuera de los paises de habla inglesa, y que provoca un error al compilar (creo que era el CS2011) proyectos GLADE# y GTK# (por lo menos en esos me dio problema a mi, tal vez halla más) así que le di la misma dirección que existia en las variables de entorno globales.


Entonces las variables de entorno me modificadas por mi quedan así:

MONO_EXTERNAL_ENCODINGS=default_locale
Path=C:\Archivos de programa\Mono-2.0.1\bin
TEMP=%SystemRoot%\TEMP
TMP=%SystemRoot%\TEMP

Instalando Gecko#


Vamos por partes como el descuartizador, primero necesitamos el Gecko Runtime Enviroment, o GRE para los amigos. Esto nos provee de las librerias necesarias para incluir el motor de navegación de Gecko en nuestras aplicaciones. En Linux esto ya es parte de la distribución en casos que ya traen exploradores basados en Mozilla desde el principio. En Windows se necesita instalar un GRE especial porque ademas de que no esta y al parecer el oficial tiene un problema con pasarle cadenas en UTF8 a Gecko#


Yo use el instalador que se encuentra en la página de Novell, GRE-GeckoSharp-1.7.12-0.1.exe. Si todo salio bien tendran también una nueva variable de entorno que dirá GECKOSHILLA_BASEPATH=C:\Archivos de programa\Archivos comunes\gtkmozembed-win32 y también también debe estar en el Mono Global Assembly Cache. Busquen la dll en la carpeta GAC de la instalación de MONO en Archivos de programa. Esta variable de entorno es requerida en Windows para que la aplicación encuentre la libreria gtkembedmoz.dll, necesaria para usar el WebControl.


Agregando DLL al GAC de MONO


Puedes agregar la dll de Gecko# al GAC de MONO desde linea de comandos con el comando gacutil -i gecko-sharp.dll desde la carpeta donde quedo instalado originalmente. Algo así como en la imagen mostrada arriba.


Ahora si, ya podemos poner el Gecko# en nuestras aplicaciones. Cree un proyecto que usa GTK# para la creación de ventanas y agregue la referencia a la .dll de Gecko# para poder usar el Gecko.WebControl en nuestro código.


agregar referencia


Agregando referencia a Gecko# en nuestro proyecto


Ocurrio un extraño problema que lo deja a uno con cara de WTF? Resulta que a la hora de compilar la aplicación con la dll de Gecko# agregada como referencia el compilador arroja lo siguiente:

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Microsoft.Common.targets(0,0) : Advertencia MSB3247: Found conflicts between different versions of the same dependent assembly.

¿Pero que rayos? Estoy compilando con MONO ¿Por qué se mete el NetFramework aquí? Solo he instalado una vez el Gecko#, no veo porque deberia tener versiones diferentes regadas por la PC. Basta de preguntas, voy por soluciones. Voy a buscar documentación en internet un rato.


Ok. Al parecer no hay manera de usar MONO al ejecutar el debugger en SharpDevelop, solo con el NetFramework de Microsoft. Para poder usar a MONO y que el .NET de Hasecorp no me tire errores lo que he hecho es lo siguiente.


Primero seleccionar a Mono 2.0 como mi framework indicado. Para eso edito las propiedades del proyecto como se ve a continuación.


Usando el Framework de MONO


Seleccionando MONO 2.0 como mi framework a usar


Con eso usara el Gmcs (o Mcs con MONO 1.1) para correr la aplicación después de compilarla. Pero si uso las opciones de debug sigue recurriendo al de Microsoft y por eso me marca el error antes mencionado arriba. Para poder correr la aplicación con el Framework debo preparar los parametros de linea de comando de esta manera, como muestro en la imagen siguiente hay que indicar que se va a iniciar con un programa externo (MONO) así que indico la ruta donde esta en la instalación y también un par de parametros por linea de comando ${TargetPath} y ${TargetDir}


Preparando para correr con MONO


Preparando el proyecto para correrlo con MONO


Pero como mencione antes, si tratas de usar debugger entonces SharpDevelop recurre al Framework de Microsoft, el cual lanza excepciones raras, así que uso la opción Ejecutar sin depurador o Run without debugger para los que lo tengan en ingles. De este modo no pasará por el debugger y generará el ejecutable compilandolo con MONO, pero siempre que uses el debbuger NetFramework seguira marcando el mismo problema antes mencionado.


Hecho todo esto, ya estoy listo para programar.

DESARROLLO DE LA APLICACIÓN

Me puse a codificar para crear un explorador basico de prueba, muestro el código fuente a continuación. Es solo un explorador sencillo que ejecuto con MONO y enviandole la dirección URL de la página que desee abrir y nada más. En esto han venido a resumirse las horas de documentación y desarrollo en las que he trabajado ultimamente.

using Gtk;
using System;
using Gecko;
namespace GeckoBrowser
{
/// Ejemplo sencillo de explorador web basado en Gecko#
public class MainWindow : Window
{
#region Variables de la clase
/// Explorador web incluido
WebControl web;
/// Direccion de la pagina cargada en el explorador
static string UrlPagina;
#endregion
#region Punto de entrada de la aplicacion
[STAThread]
public static void Main(string[] arg)
{
Application.Init();

if (arg.Length > 0)
{
UrlPagina = arg[0]; // Obtener la pagina indicada por parametros
}

new MainWindow();
Application.Run();
}
#endregion
#region Ventana y sus eventos
public MainWindow() : base("MainWindow")
{
DeleteEvent += new DeleteEventHandler(MainWindowDeleteEvent);
#region Codigo requerido por GRE and Gecko# en Windows
string mozillaEnvPath = System.Environment.GetEnvironmentVariable("GECKOSHILLA_BASEPATH");

if (mozillaEnvPath != null && mozillaEnvPath.Length != 0)
{
Gecko.WebControl.CompPath = mozillaEnvPath;
}

#endregion

web = new WebControl(); // Inicializar el explorador
web.LoadUrl(UrlPagina); // Abrir la pagina indicada
this.Add(web); // Agregar el control a la ventana
ShowAll();
}
void MainWindowDeleteEvent(object o, DeleteEventArgs args)
{
Application.Quit();
args.RetVal = true;
}
#endregion
}
}

Para que este código funcione he agregado como referencia en mi proyecto las siguientes DLL que he usado y no estan incluidas en NetFramework. Yo las tengo por MONO y por el mismo GTK#


  • atk-sharp

  • gdk-sharp

  • gecko-sharp

  • glib-sharp

  • gtk-sharp

  • pango-sharp

Ademas de estas dll, también pongo en la carpeta junto al ejecutable a gtkembedmoz.dll debido a que cuando lo probe en GNU/Linux (un Ubuntu 8.10 con Gnome para más señas) me arrojo un mensaje diciendo que me faltaba ese archivo. Esta dll la tome de la carpeta donde se instala GTK# como explique al principio de la nota. En linux puedes tomarla de la instalación de Mozilla o FireFox que seguramente estará en /usr/lib/[carpeta de tu navegador] por lo regular.


Navegador


Así se ve el navegador


La aplicación me funciono bien excepto en un caso, cuando la ejecute para abrir una página segura me solicito la instalación del Personal Security Manager (PSM) como requisito para mostrar las páginas HTTPS, con las demas sin problema. Esto es cosa del navegador, Gecko en este caso. Este paquete consiste en un set de librerias para operaciones Cryptograficas, incluida SSL por lo que es necesaria para visualizar "páginas seguras" (https) y manejo de certificados. Al parecer los navegadores como Mozilla FireFox incluyen este componente pero lo hacen en si mismos, en una capa por encima de Gecko así que por eso no lo estoy usando de manera predeterminada.


En GNU/linux me dio una excepción debida a falta de ambientación. El mensaje es este:

Unhandled Exception: System.TypeInitializationException: An exception was thrown by the type initializer for Gecko.WebControl --->
System.DllNotFoundException: gtkembedmoz.dll
at (wrapper managed-to-native) Gecko.WebControl:gtk_moz_embed_get_type ()
at Gecko.WebControl.get_GType () [0x00000]
at GtkSharp.GeckoSharp.ObjectManager.Initialize () [0x00000]
at Gecko.WebControl..cctor () [0x00000] --- End of inner exception stack trace ---
at GeckoBrowser.MainWindow..ctor () [0x00000]
at GeckoBrowser.MainWindow.Main (System.String[] arg) [0x00000]

Entonces abri el archivo

/etc/environment para agregar la variable LD_LIBRARY_PATH="[inserte aquí la ruta de los .so de mozilla, firefox o donde sea que este]" y ya con eso funciona.


Explorador solicitando Personal Security Manager


Otro problema que puede llegar a darse y que aun no he resuelto es cuando el navegador trabaja tras un firewall y necesites darle una IP particular al browser como salida a Internet. Aun no se como indicarselo.


Estos casos aun no los resuelvo, así como tampoco he implementado control de los eventos del navegador y del DOM de las páginas deplegadas en él, pero esos puntos me interesa trabajarlos también aunque este ultimo no estoy seguro que tan profundamente se pueda manejar con Gecko# o si será mejor cambiar de enfoque. En cualquier caso practicar el uso de GTK# me cayo bien para refrescar memoria pues hace tiempo no lo usaba más que en GNU/Linux con el diseñador de ventanas STETIC de MonoDevelop.

Acepto sugerencias de mejora y resolución de lo que falta.

0 comentarios:

Publicar un comentario

Por favor trata de escribir bien, no te pido que no te falte ni un acento pero por favor evita escribir como metroflogger o facebookero. Este blog es un sitio decente. Gracias.

Subscribe to RSS Feed Follow me on Twitter!