AddIn’s para MSN Messenger

No se como algo tan sencillo puede acabar resultando tan complicado…

Hace bastante tiempo estuve enredando con losAddIn’s de MSN Messenger. A raíz de lo de las voces, estuve mirando por System y me encontré System.Speech, y pensé que sería sencillísimo hacer uno que leyese lo que te escriben (iluso de mi). Lo que es hacerlo es de lo más sencillo, pero después que funcione no lo es tanto…

Empecemos por el principio. Un AddIn es lo que toda la vida se ha llamado plugin. En nuestro caso, se puede crear una dll que cumpla determinadas condiciones para cargar en el MSN Messenger y así añadirle funcionalidad.

HABILITAR ADDIN’S PARA MSN MESSENGER

Para habilitar los add in debemos añadir en el registro de windows un valor de tipo DWORD con el nombre AddInFeatureEnabled en la clave HKEY_CURRENT_USER\Sofgtware\Microsoft\MSNMessenger\ y asignarle un 1. En este archivo hay un .reg que hace esto automáticamente. Hecho esto, al reiniciar el messenger en Herramientas\Opciones encontraremos (creo que solo es a partir de la version 8) un nuevo apartado llamado Accesorios en el que se puede indicar la dll del accesorio que queremos añadir (por ejemplo la que está en el mismo rar). Con esto el AddIn queda agregado pero no activo, pero no nos adelantemos.

ESTRUCTURA DE UN ADDIN PARA MSN MESSENGER

Un add-in es una dll que tiene como nombre EspacioDeNombres.Clase.dll. Esta dll deberá tener una referencia a MessengerClient.dll (por lo tanto deberá estar junto a esta para ejecutarse correctamente), en todos los sitios que he leido indican que hay que añadir también el espacio de nombres Microsoft.Messenger, pero supongo que si lo antepones a cualquier miembro de ese espacio no es necesario. El proyecto tiene que tener un espacio de nombres, que deberá se el mismo que se indique en la primera parte del nombre de la dll. Además debe existir una clase (cuyo nombre es la segunda parte del nombre de la dll) que implemente la interfaz IMessengerAddIn cuyo único requisito es que exista un metodo initialize:

Sub Initialize(ByVal ElMessengerClient As MessengerClient) Implements IMessengerAddIn.Initialize

En este caso ElMessengerClient es el objeto que lanza los eventos que podemos controlar, y sobre el que podemos realizar acciones como enviar mensajes, etc. por este motivo es recomendable que nos le guardemos. Algunos eventos que pueden interesarnos son:

'Se dispara cuando llega un mensaje
Private Sub MiClienteMSN_IncomingTextMessage(ByVal sender As Object, ByVal e As Microsoft.Messenger.IncomingTextMessageEventArgs) Handles MiClienteMSN.IncomingTextMessage
...

'Se dispara cuando se aprieta el botón de configuración del accesorio
Private Sub MiClienteMSN_ShowOptionsDialog(ByVal sender As Object, ByVal e As System.EventArgs) Handles MiClienteMSN.ShowOptionsDialog
...

USO DE UN ADDIN PARA MSN MESSENGER

Es tan simple como colocar la dll adecuada donde queramos, pero asegurandonos de que tiene a su lado la dll MessengerClient.dll que encontraremos en el directorio de intalación del messenger. Por ejemplo la podemos colocar en (directorio por defecto) c:\Archivos de programa\MSN Messenger\. Una vez hecho esto y teniendo habilitados los addin como hemos indicado antes, abrimos el messenger y vamos a herramientas>opciones>accesorios y pulsamos en [Agregar a Messenger] exploramos hasta el lugar en el que la hemos puesto, la seleccionamos y aceptamos. Si este AddIn implementa un manejador de ShowOptionsDialog se habilitará el botón [Configuracion] a traves del cual normalmente se podrá configurar el AddIn. Una vez hecho esto esta añadido, pero no activo, podremos indicar ahí que se active automaticamente con el cambio de estado, o bien, activarlo manualmente desde la venta principal del messenger en la «flecha» que sale junto al nombre.

ACCESO DE UN ADDIN A LOS RECURSOS DEL SISTEMA

Hasta aquí la parte sencilla, con esto podremos hacer un bonito robot con el que hablen nuestros amigos cuando no estemos, y poco mas…. Al parecer los AddIn se ejecutan en una «caja de arena», un entorno seguro para que no pueda hacer nada que comprometa la seguridad sin que el usuario sea consciente de ello. Esto provoca que no podamos acceder a los recursos del sistema, por lo tanto nos condena a no hacer practicamente nada.

Por ejemplo no podemos crear un objetoSpeechSynthesizer para hacer que el sistema nos lea los mensajes que recibimos. Si hacemos esto en uno de los manejadores de eventos (o el procedimiento Initialize) directamente obtendremos un error 8013150A, o si lo hacemos en una función que se llame desde uno de los manejaremos nos dirá que«Ese ensamblado no permite llamadores de confianza parcial».

LLegados a este punto te puedes volver loco a mirar por el msdn, y todo el sitio de microsoft, yo al menos no he encontrado nada de nada. Asombrosamente la solución la encontré en un sitio de microsoft, pero es algo como «alternativo», y para colmo a esa pagina llegue a traves de unos blogs chinos o japoneses o lo que sea (salían caracteres que no se leer). Lo que nos interesa empiza a partir de «Add-ins run under Code Access Security….» básicamente dice que lo que hay que hacer es firmar el componente, y añadirlo a la cache de ensamblados global. Lo de firmarlo se puede hacer mediante las propiedades del proyecto, y lo de añadirlo a la cache los que tengamos intalado algún visual studio podemos usar el gacutil (segun donde tengamos las cosas y que versiones tengamos):

"C:\Archivos de programa\Microsoft Visual Studio 8\SDK\v2.0\Bin\gacutil.exe" /i  "C:\Archivos de programa\MSN Messenger\LectorWLMAddIn.AddIn.dll"

Si todo ha ido bien nos dirá «Ensamblado agregado correctamente a la caché.». LLegado a este punto si podremos acceder a los recursos del sistema (entrada/salida, web, etc.).

Si nos damos cuenta esto es de lo mas simple, es una manera de que el usuario diga que dicha dll es de confianza y que puede hacer lo que quiera, pero no, no se les ocurre decirlo donde se habla de la API ni nada por el estilo. Con lo facil que habría sido decir algo como (perdonar mi ingles): «For security your AddIn will execute in a sandbox that not permit acces to IO system and other system resources. If you want this you must sign the assemby and add it to the global assembly cache (GAC)», vamos digo yo, y así no me habría matado mirando y probando cosas como compartición de objetos, etc., etc., etc. Perdonar la exaltación, pero es que hasta que lo encontré he dado mil vueltas…

Bueno, a parte del archivo que os he indicado antes que contiene la dll y el .reg para habilitarlo, he colgado el proyecto, por si alguien quiere echarle un vistazo, o incluso extenderlo. A partir de ahi supongo que se podrían configurar para que contactos queremos tenerlo activado y para cuales no, o incluso que voces queremos usar para cada uno de estos.

Aquí os dejo algunos enlaces donde hay mas ejemplos: [1] [2] [3] [4] [5]

Voces de Loquendo…

Mirando a ver si encontraba voces buenas en español para la opción de «Leer en voz alta» del acrobat reader, que fuesen mejores que las latas disponibles gratuitamente de Microsoft, y que no me costaran pasta pues era para hacer el tocho, he encontrado un blog (bastante recomendable) en el que están disponibles las voces de loquendo con un parche. El parche no hace que las puedas usar con cualquier programa que soporte SAPI5, como el acrobat, pero si con el programa que pone en la misma entrada (Text Aloud), con el que se puede leer de todo. Sin embargo, si para lo que quereis probar no os vale con eso, podeís mirar en in.solit.us.

Naturalmente, como ya sabreis, no se puede craquear un programa para usarlo sin haber pagado por el (yo ya me desinstalé las voces despues de hacer el tocho), pero con el precio de estos sintetizadores no está de más hacer un par de pruebas antes de comprarlo, para ver si se ajustan a lo que necesitamos.

Dependiendo de cuales sean vuestras necesidades, si es que las teneis, tal vez podais usar un script en ELZA que escribí hace un par de años, en el que se usan las voces de loquendo pero a traves de un servicio de pruebas gratuito.

GLAT – 17

17. Considerar una función la cual, para un entero n, devuelve el número unos requeridos para escribir todos los numero entre 0 y n. Por ejemplo f(13)=6. Note que f(1)=1 Cual es de los siguientes el más grande n el cual cumple que f(n)=n?

Este es con el que me atasqué. Intuía como encontrar la respuesta y por donde andaría, pero no llegue a ella en su momento porque las primeras cuentas básicas las había echado mal, y despues no me las replantee. Hoy en cuanto he revisado lo que habia hecho me he dado cuenta del error.

Bueno, vamos al grano:

Me he basado en las diferencias (D=n-f(n)). Vamos a comprobar los numeros maximos de n cifras, así como el siguiente numero empieza por uno, por lo tanto no suman, y se pasa por números que restan, si es pequeño solo habrá que ver cuantos se necesitan restar. Además es una buena forma de ver como crecen las funciones implicadas:
Max(N(1))=9; f(9)=1; D(1)=8
Max(N(2))=99; f(99)=20; D(2)=79
Max(N(3))=999; f(999)=300; D(3)=699
Se ve claramente como evolucionan, asi que llegando al numero en que la cifra mas significativa de la diferencia sea 0 estaremos más cerca del número que buscamos. Por si quedan dudas me he hecho un programa. Lo he hecho en fortran con el compilador Plato3 (o el que integra el entorno). Si hubiese usado el hugs de haskell, habría podido obtener cuantos valores quisiera, pero este ya lo tengo bastante olvidado (falta de uso) y me tuve que mirar la sintaxis del otro para explicarselo a la chavala, así que he usado lo que tenía más fresco. Además es mas que suficiente:

program diferencias integer::i,diferencia,max,f,error=0    character(len=15),parameter::archivo='diferencias.txt'    i=1 f=0    open(unit=10,file=archivo,status='UNKNOWN',action='WRITE',iostat=error,access='SEQUENTIAL',position='ASIS')    if (error/=0) Then   write (*,*) 'Ha ocurrido un error al intentar abrir el archivo: ',archivo else   !Habra un overflow si llegamos a 10      do while(i<10)    max = (10**i) -1       f = (f*10)+(10**(i-1))       diferencia=max - f    write(10,*,iostat=error) i,' digitos: ', diferencia, '; max=',max,'; f=',f        if (error/=0) Then       write (*,*) 'Ha ocurrido un error (',error,') al intentar escribir el archivo: ',archivo          exit        end if        i = i +1      end do      close(10)    end ifend program diferencias

No hacía falta lo de sacarlo a un archivo, pero bueno, por enredar.
El resultado es:

1 digitos:            8; max=           9; f=           12 digitos:           79; max=          99; f=          203 digitos:          699; max=         999; f=         3004 digitos:         5999; max=        9999; f=        40005 digitos:        49999; max=       99999; f=       500006 digitos:       399999; max=      999999; f=      6000007 digitos:      2999999; max=     9999999; f=     70000008 digitos:     19999999; max=    99999999; f=    800000009 digitos:     99999999; max=   999999999; f=   900000000

Por suerte, antes del overflow por la capacidad de los enteros, hemos conseguido reducir la cifra mas significativa de la diferencia hasta 0 ;).
Además se ve claramente que debido al crecimiento de f(n) en la siguiente iteración tendría una cifra más que max,con lo que la siguiente linea sería:

10 digitos: -1; max= 9999999999; f= 10000000000

Con lo que f(9999999998) = 9999999998.

Ahora bien, me acabo de dar cuenta de que me he acelerado, y puede que esa no sea la respuesta, ya que se pide el menor n, y f(n) vale 9999999998 dese 9999999991. Aunque es posible (muy posible ya que n crece linealmente y f(n) a saltos) que en el momento en que se cruzan las funciones f(n) y n no se de la igualdad f(n)=n, pero sin embargo sin comprobarlo de algun modo, no puedo decir que esta sea la respuesta….

Vaya mierda!!, jeje.

CORRECCIÓN: Al releer todo me he dado cuenta de que he metido la para dos veces: no se pide el menor, y f(9999999998) = 10000000000. Me he liado, pero bueno, está cerca la cosa, otro dia lo acabo.