Silverlight y los Sockets

 Hace tiempo que vengo intentando cubrir algunos huecos en mi formación sobre .NET que por falta de tiempo se me había hecho imposible llevar adelante. Se trata de Silverlight y WPF. Ahora y por cuestiones de trabajo, me ha tocado hacer frente a estos “baches”.

Como soy programador y no diseñador, paso un poco de toda esta tecnología en la que las posibilidades de crear User Interface (UI) ricas en animaciones o efectos visuales cobra mayor importancia. Así, enfrentar este nuevo reto me lleva por encontrar desde el punto de vista de análisis y desarrollo, las posibilidades que estas nuevas tecnologías traen asociadas.

Uno de los principales problemas cuando desarrollamos en Web, es la posibilidad de una comunicación asincrónica entre cliente y servidor. Con esto me refiero a la posibilidad de que la comunicación fluya en ambos sentidos y no tenga que ser el cliente (Aplicación WEB) quien realice constantemente las peticiones al servidor.

Silverlight es un Framework que corre del lado del cliente y que contiene una infraestructura que nos permite aumentar de modo significativo el alcance de nuestras aplicaciones Web. Partiendo de la problemática anterior y conociendo las ventajas de esta nueva forma de desarrollo, me planteo para mi propio estudio la utilización de sockets dentro de SilverLight para realizar una conexión en la que el servidor sea quien se mantenga brindando continua información al cliente.

Esquema de desarrollo:

El esquema lo que muestra es una aplicación que desde un servidor interactúa con una pizarra telefónica (PBX) mediante TAPI y envía hacia un cliente WEB desarrollado con Silverlight el estado actual de cada una de las extensiones conectadas a la PBX. Para el artículo, solo nos concentraremos en la comunicación por socket entre el cliente Web y el servidor. He dibujado el diagrama completo para que vean las líneas que representan el sentido en que fluye la información en toda la aplicación. (Desde el servidor hacia el cliente)

Empezamos desarrollando una aplicación de consola con un socket server como normalmente lo hemos visto siempre en cualquier aplicación Windows. Este socket server estará a la escucha mediante un determinado puerto de cualquier petición de conexión realizada desde un cliente Web.

El TcpListener: Voy a pasar por alto la implementación detallada del servidor ya que no es el objetivo del artículo.

//create our TCPListener object
_sockServer = new System.Net.Sockets.TcpListener(IPAddress.Any, 4530);

Console.WriteLine(«Started server…»); 

try
{
  
//start the chat server
  
_sockServer.Start();

   while (true)
  
{
     
_tcpClientConnected.Reset();

      Console.WriteLine(«Waiting for client connection…»);

       _sockServer.BeginAcceptTcpClient(new AsyncCallback(OnBeginAccept), null);
     
_tcpClientConnected.WaitOne(); //Block until client connects
  
}
}

catch
(Exception ex)
{
  
Console.Write(ex.ToString());
}

… OnBeginAccept

var _client = _sockServer.EndAcceptTcpClient(result);
InitializeExtensionList(_extensions); 

_tcpClientConnected.Set(); //Allow waiting thread to proceed

Con este código inicializamos nuestro servidor y lo mantenemos a la escucha sobre el puerto 4530 de cualquier petición de conexión. En el momento en que una conexión es solicitada, se inicializa el listado de extensiones que no es más que enviar al cliente la información de todas las extensiones conectadas a la PBX.

La primera observación es el puerto seleccionado. Esto no es un antojo, nuestro cliente será una aplicación Silverlight y el mismo solo permite realizar conexiones por sockets a los puertos comprendidos en el rango de 4502 hasta el 4534. Cualquier intento de conexión desde una aplicación desarrollada en Silverlight a un puerto fuera de este rango, será denegado automáticamente.

Segunda observación es que usamos TCP como protocolo de comunicación, esto es lo normal cuando trabajamos con sockets aunque no está de más indicar que es el único protocolo permitido por Silverlight.

Lanzamos nuestro server para asegurarnos que todo marcha bien…

Todo bien… así que seguimos con lo que realmente importa, la aplicación cliente.

MainPage.xaml.cs : No voy a entrar en detalles de diseño o implementación del socket para no hacer extenso el artículo.

public MainPage()
{
  
InitializeComponent();
  
this.Loaded += Page_Loaded;    
}

En el constructor de mi UserControl me subscribo al evento Loaded en el cual conectaré al servidor y esperaré como respuesta el listado de extensiones.

void Page_Loaded(object sender, RoutedEventArgs e)
{
   
var endPoint = new DnsEndPoint(Application.Current.Host.Source.DnsSafeHost, 4530);
   
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

    SocketAsyncEventArgs args = new SocketAsyncEventArgs();
   
args.UserToken = socket;
   
args.RemoteEndPoint = endPoint;
   
args.Completed += new EventHandler<SocketAsyncEventArgs>(OnSocketConnectCompleted);

    socket.ConnectAsync(args);
}

Aquí vamos… Explicación rapidita: Este código identifica en primer lugar el servidor de la aplicación e inicializa un EndPoint mediante el puerto 4530 (Recuerden que Silverlight sólo puede conectarse a los servidores mediante los puerto 4502 al 4532). Posteriormente se crea un objeto Socket capaz de comunicarse mediante el protocolo TCP. Una vez que el objeto Socket es creado, creamos una instancia del objeto SocketAsyncEventArgs y asignamos el Socket a la propiedad UserToken de modo que podamos utilizarlo a través de otros métodos. El punto final de destino en el servidor se establece mediante la propiedad RemoteEndPoint y el evento Completed nos va a indicar el resultado de la conexión. Una vez que estos objetos son creados y están listos para su uso, El método ConnectAsync del objeto Socket puede ser llamado indicando como argumento el SocketAsyncEventArgs.

private void OnSocketConnectCompleted(object sender, SocketAsyncEventArgs e)
{
  
if (e.SocketError == SocketError.Success)
  
{
     
… Success Connection
  
}
  
else
  
{
     
Dispatcher.BeginInvoke(() =>
        
System.Windows.Browser.HtmlPage.Window.Alert(e.SocketError.ToString()));

  
}
  

}

Con este código ya listo, lanzo mi aplicación cliente:

¡¡¡BOOM!!! ¿Acceso denegado? Y yo aprendiendo Silverlight, menudo problema. Mis primeros intentos de solución son los tan “mal” acostumbrados “prueba y error”. Sin tener muy claro qué estaba pasando empecé a intentar adivinar.

– Ya sé… el Firewall de Windows. Fuera Firewall pero el error seguía – Umm.. ya sé… el servidor no está funcionando bien. Hice una pequeña aplicación de consola que conectara al servidor y… ¡¡¡funcionó bien!!!

Cansado de tanto estira y encoje hice lo que debía hacer desde el principio. ¡Leer más!

Resulta que nuestro nuevo amiguito Silverlight establece unas políticas de seguridad, muy acertadas a mi criterio, en las que no permite conexión alguna si antes el servidor no valida que realmente se tenga acceso a este recurso.

El MSDN dice:

“Silverlight 2 y las versiones posteriores son compatibles con la conectividad entre dominios, lo que permite a una aplicación obtener acceso a recursos situados en ubicaciones que no son el sitio de origen. Se trata de una característica importante para que las aplicaciones de Silverlight puedan utilizar los servicios existentes en la Web.

El sistema de directivas de seguridad del motor en tiempo de ejecución de Silverlight requiere que se descargue un archivo de directivas desde un dominio de destino antes de permitir que una conexión de red tenga acceso a un recurso de red que pertenezca a ese dominio de destino. Este sistema de directivas de seguridad afecta al acceso de red entre dominios para las clases WebClient y HTTP en el espacio de nombres System.Net.”

¿Qué archivo es este y qué contiene? ¿Dónde pongo este archivo?

El archivo que expone las directivas de seguridad para que Silverlight pueda validar los recursos de red a los cuales deseamos acceder es un XML con el siguiente formato:

<?xml version=»1.0″ encoding=»utf-8″?>
<access-policy>
 
<cross-domain-access>
   
<policy>
     
<allow-from>
       
<domain uri=»*» />
     
</allow-from>
     
<grant-to>
       
<socket-resource port=»4530″ protocol=»tcp» />
     
</grant-to>
   
</policy>
 
</cross-domain-access>
</access-policy>

Relajitos con mi servidor ¡NO! 😉

Mediante este archivo nosotros le indicamos a cualquier cliente que intente conectar a nuestro recurso de red, que solo tendrá permiso si la petición se realiza desde un dominio que esté validado por nosotros (En este caso, vía libre a todo el mundo) y el recurso al que esté intentando conectar sea sí y solo sí, el puerto 4530. Muy bien aquí por Microsoft 😉

Ahora bien… ¿dónde pongo este archivo? De nuevo MSDN:

“En Silverlight versión 3, en el caso de una solicitud de conexión mediante System.Net.Sockets al sitio (entre dominios o sitio de origen), el runtime de Silverlight intenta abrir una conexión utilizando TCP a un puerto conocido (el puerto 943) en el sitio de destino. Si se puede establecer una conexión TCP, el runtime de Silverlight envía la cadena especial al servidor para solicitar un archivo de directivas de sockets de Silverlight.

El motor en tiempo de ejecución de Silverlight, a continuación, espera recibir una respuesta desde el sitio de destino que contenga un archivo de directivas de Silverlight. Si se devuelve este archivo de directivas de sockets de Silverlight (aun cuando haya un error de análisis del archivo), se utiliza como archivo de directivas para esa solicitud de sockets y para todas las solicitudes subsiguientes a ese sitio de destino durante la sesión completa de la aplicación de Silverlight.”

Esto significa que para probar mis aplicaciones en Silverlight, de algún modo necesito tener un servidor escuchando peticiones por el puerto 943 y, en caso de que se produzca una conexión, validamos que se trate de un cliente solicitando un archivo de directiva de seguridad para retornarle el XML.

Esto sería un problema si pensamos hostear servicios en servidores que no sean dedicados, por eso, para el caso de Silverlight 4, la petición de las directivas de seguridad se realizan tanto por el puerto 80, como por el puerto 943. De esta forma podemos adicionar a nuestro proyecto web el archivo XML para que los clientes accedan directamente a él.

Para pruebas o cuando ejecutamos la aplicación dentro de Visual Studio, no podríamos publicar el XML por el puerto 80 ya que nuestra aplicación se ejecuta dentro del servidor web que trae el propio Visual Studio.

Para poder probar mis aplicaciones sin problemas me cree un pequeño Socket server que valida y retorna el archivo de directivas de seguridad solicitado por Silverlight.

class PolicySocketServer
{
  
private TcpListener _listener = null;
  
private TcpClient _client = null; 

   private static ManualResetEvent _tcpClientConnected = new ManualResetEvent(false);
  
private const string _policyRequestString = «<policy-file-request/>»;

   private int _receivedLength = 0;
  
private byte[] _policy = null;
  
private byte[] _receiveBuffer = null;

   private void ReadXmlPolicyData()
  
{
     
string policyFile = ConfigurationManager.AppSettings[«PolicyFilePath»];
     
using (FileStream fs = new FileStream(policyFile, FileMode.Open))
     
{
         
_policy = new byte[fs.Length];
        
fs.Read(_policy, 0, _policy.Length);
     
}

      _receiveBuffer = new byte[_policyRequestString.Length];
   
}

   public void StartSocketServer()
  
{
     
ReadXmlPolicyData(); 

      _listener = new TcpListener(IPAddress.Any, 943);
     
_listener.Start();

      Console.WriteLine(«Policy server listening…»);

      while (true)
     
{
        
_tcpClientConnected.Reset();
        
Console.WriteLine(«Waiting for client connection…»);

         _listener.BeginAcceptTcpClient(new AsyncCallback(OnBeginAccept), null);
        
_tcpClientConnected.WaitOne();
     
}
   
} 

    private void OnBeginAccept(IAsyncResult result)
   
{
      
_client = _listener.EndAcceptTcpClient(result);           

       _client.Client.BeginReceive(
           
_receiveBuffer, 0, _policyRequestString.Length, SocketFlags.None,
            
new AsyncCallback(OnReceiveComplete), null);
   
}

    private void OnReceiveComplete(IAsyncResult result)
   
{
      
try
      
{
         
_receivedLength += _client.Client.EndReceive(result); 

          if (_receivedLength < _policyRequestString.Length)
         
{
             
_client.Client.BeginReceive(
                       
_receiveBuffer,
                       
_receivedLength,
                       
_policyRequestString.Length – _receivedLength,
                       
SocketFlags.None,
                       
new AsyncCallback(OnReceiveComplete), null); 

               return;
         
} 

          //Check <policy-file-request/>
         
string request = System.Text.Encoding.UTF8.GetString(
               
_receiveBuffer,
               
0,
               
_receivedLength); 

          if (StringComparer.InvariantCultureIgnoreCase.Compare(request, _policyRequestString) != 0)
         
{
            
//Isn’t valid… bye bye
            
_client.Client.Close();

            
return;
         
}

          //Is Okay….send policy file
         
_client.Client.BeginSend(
                   
_policy,
                   
0,
                   
_policy.Length,
                   
SocketFlags.None,
                   
new AsyncCallback(OnSendComplete),
                   
null);
      
}
     
 catch (Exception ex)
      
{
         
_client.Client.Close();
         
Console.Write(ex.ToString());
      
}

       _receivedLength = 0;
      
_tcpClientConnected.Set(); //Allow waiting thread to proceed
 
}

  private void OnSendComplete(IAsyncResult result)
 
{
     
tr
     
{
        
_client.Client.EndSendFile(result);
     
}
     
catch (Exception ex)
     
{
        
Console.Write(ex.ToString());
     
}
     
finally           
     
{
        
_client.Client.Close();
     
}
  
}
}

 

Apuntes de un evento (POO) Parte 1

Era mi primer evento online. Nunca había dado una clase o evento sin poder ver las caras de las personas que me escuchan. En principio pensé que esto sería malo, pero ya creo que también tiene su parte buena.

Lo malo, no podré saber si están entendiendo o no lo que les estoy contando Lo bueno, no podré saber si tienen esa cara que se les queda a todo el mundo cuando se pregunta, “que tontería está hablando este” 😉

Ya parejos, F5:

Encuesta: Con el objetivo de conocer la opinión de los presentes sobre programar o pensar orientado a objetos, se realizó la siguiente encuesta.

1- ¿Programamos orientado a objetos?
2- Cuando programamos, ¿Pensamos orientado a objetos?

Presentación

En este evento pretendíamos repasar de forma muy elemental los principales conceptos sobre programación orientada a objetos. La mayoría de los lenguajes de programación son hoy en día orientados a objetos. Esto puede traernos confusión al pensar que por programar sobre un lenguaje que sea orientado a objetos, estamos programando orientado a objetos.

Un posible ejemplo de esta confusión es el desarrollo de aplicaciones ASPNET y el Desarrollo en Capas. Podemos crear un proyecto ASPNET donde todo el código esté en un mismo proyecto, donde las consultas se realicen escribiendo directamente el SQL en el code behind y nuestro programa funcionaría, pero ¿Es correcto? NO

Un poco de historia

¿Cómo surge todo esto de la programación orientada a objetos y dónde radica su verdadera importancia?

Todo empezó en la llamada “crisis del software” entre los años 60 y 70.

¿En qué consistía esta crisis?

Los «grandes» grupos de programadores que programaban los «grandes» sistemas para los «grandes” ordenadores tenían serios problemas de organización y productividad. Pocos sistemas lograban terminarse, en pocos se terminaban cumpliendo los requerimientos iniciales y no todos los que se terminaban cumpliéndolos, se usaban según lo planificado.

El problema, que en aquel entonces se llamó de forma incorrecta de “mantenimiento”, consistía en cómo adaptar el software a nuevos requerimientos imposibles de haber sido planificados inicialmente.

La planificación y previsión es contrario a la propia realidad.

Nosotros, las personas, aprendemos y creamos a través de la experimentación, no de la planificación. Si no hay experiencia, no aprendemos, y la experiencia solo llega experimentando y no planificando.

Soy un niño y quiero correr (Experimentar) me caí y me di un golpe (Experiencia) ya sé que no puedo correr sino que debo caminar (enseñanza)

Es imposible y eso creo que todos los sabemos, que exista un proyecto que pueda ser planificado por entero antes de escribir una línea de código, y mucho más imposible, convertir la planificación en una camisa de fuerza en el desarrollo y evolución del sistema.

Aún en la actualidad, se lucha por crear planificaciones adaptables. Las Metodologías ágiles, por ejemplo, intentan planificar un proyecto de software adaptándolo constantemente a los cambios que surgen durante su etapa de desarrollo.

¿Por qué toda esta historia?

¿Es la POO un mejor paradigma que otros? En cierto sentido sí lo es. ¿Es una cura de todos nuestros problemas? No, no lo es. ¿Significa que «crisis del software» desaparecerá? Probablemente no. Y entonces se preguntarán, ¿qué es lo grande de la Programación Orientada a Objetos?

Acercar el ordenador al problema y no al revés. En lugar de tratar de modelar un problema en algo familiar al ordenador, se trata ahora de acercar el ordenador al problema. Se trata en todo momento de modelar los problemas existentes en la vida real

Principales pilares de la programación orientada a objetos.

Encapsulamiento:

Es importante limitar correctamente el acceso a un objeto. Un objeto es un elemento que contiene propiedades y que realiza acciones.

Un avión tiene ruedas, un color, puertas, halas alas. Además realiza acciones como despegar, aterrizar, planear, girar a un sentido u otro. Sobre el avión se realizan acciones que cambian su comportamiento y este, modifica el estado.

Supongamos que el avión contiene un estado que nos indica si está en tierra o en el aire. Jamás deberíamos poder modificar ese estado si no es mediante una acción.

– Estado (En tierra)
– Acción (Despegar). Si despegar fue correcto, cambiar estado (en el aire)
– Estado (En el aire)

Herencia:

Hay que procrear, procreen mucho y así damos continuidad a nuestra especie. Pero, cuidado, no se puede ir haciendo hijos por ahí si no estamos seguros de que le vamos a transmitir y que van a usar todo lo que sabemos.

Esto es herencia. Necesitamos hijos que se apoyen de nuestra experiencia y la apliquen en forma de enseñanza. Lo sé, porque mi padre me lo dijo y listo. Cómo lo supo el padre no importa, eso queda en el encapsulamiento 😉

Piensen en el ejemplo del niño que corre. Los padres constantemente intentan transmitir cuidado, enseñanzas de que no se debe correr para evitar un golpe. El niño no se preocupa cómo sus padres lo aprendieron, “casi siempre” obedecen.

Pero, de nuevo tenemos que tener mucho cuidado. Si un hijo, a pesar de conocer la experiencia del padre termina por no usarla y crear una personalidad propia, entonces ya no existe herencia, sino que serían dos personas totalmente distintas. Dos objetos distintos.

La herencia, entre otras cosas, es para un informático el pan nuestro de cada día. Reutilización, no escriban lo mismo dos veces. El tiempo es dinero y hasta un minuto puede costar caro. Piensen, piensen y piensen. Si hay dos objetos que tienen similar comportamiento, el mantenimiento de los mismos de manera separada multiplica por dos el tiempo para realizarlo.

Cohesión/ Especialización (ver comentarios al final del artículo):

Esto es la especialización de cada objeto. Tenemos que llegar a lograr que cada objeto se especialice solo en lo que sabe hacer, extendernos haría que termináramos mezclando funcionalidad.

La silla, fue diseñada solo para sentarnos. Algunos la usamos como escalera, pero no fue diseñado para eso. Cuando usamos la silla como escalera y nos caemos decimos: claro, era una silla y no una escalera.

El perro es un perro y el gato es un gato, no hay un perro-gato. Un coche es un coche y un avión es un avión. Y si no me creen, intenten volar con un coche.

Los objetos mientras mayor sea su cohesión, mejor podremos definir su comportamiento.

Abstracción:

Aquí pensamos en qué hace y no en cómo lo hace. Dentro de la programación orientada a objeto la Abstracción nos permite definir características del objeto, modelar su comportamiento sin llegar a especificar el cómo lo hace.

En la realidad, el ser humano no piensa en las cosas como un conjunto de pequeñas cosas sino que las entendemos como objetos con comportamientos bien definidos.

Una persona es un objeto con un comportamiento bien definido. Cuando una persona desea desplazarse, el cuerpo responde caminando. En ese momento no pensamos cómo nuestro deseo llegó al cerebro y este envía el mensaje en forma de acción hacia los músculos de los pies para provocar el movimiento. Simplemente, caminamos y eso define el comportamiento.

El principio de abstracción es precisamente esto: Lograr tener objetos con comportamientos bien definidos e irlos dividiendo sucesivamente en conjuntos de subsistemas cada vez más especializados.

El objeto animal es un ejemplo de abstracción, tiene características como edad, sexo, color y realiza acciones como comer, dormir o desplazarse. La especialización de esta abstracción podría ser un objeto perro, gato, paloma o persona, cada nuevo objeto implementa el cómo hará cada acción.

Polimorfismo:

Es la capacidad que permite mantener organizada varias acciones en objetos heredados que aun significando lo mismo, se realizan de manera diferente.

Existen aviones de combate que pueden despegar desde un porta-aviones en el medio del mar, otros que no tienen tal capacidad y lo hacen siempre desde una pista. La acción a realizar es Despegar, pero la manera en que lo hacen es diferente.

En POO, el polimorfismo se aplica cuando necesitamos sobre escribir métodos o atributos llamados de forma idéntica pero con un comportamiento distinto.

No confundir polimorfismo con sobre-carga: Polimorfismo (Entre clases, se resuelve en tiempo de ejecución) Sobre-carga (métodos de la misma clase, se resuelve en tiempo de compilación)

Acoplamiento:

Es la dependencia existente entre objetos para poder funcionar.

La comunicación entre objetos siempre se realiza mediante acciones (llamadas a métodos) y es conocida como comunicación por mensajes. Haz esto, hora tú haz aquello para yo luego poder hacer lo mío.

Si bien es verdad que mientras menos acoplamiento exista entre los objetos, mejor encapsulado quedará el objeto, debemos tener mucho cuidado en no mezclar responsabilidades.

Una tuerca es un objeto con una funcionalidad bien definida: Apretar o asegurar algo. A nadie hasta ahora se le ha ocurrido crear una tuerca que se ajuste por sí misma. A la tuerca, se le hace rosca, que es quien permite el ajuste y por fuera se le da diferentes formas según la herramienta que se desee utilizar posteriormente para ajustar dicha tuerca. Unir la funcionalidad de ambos objetos (tuerca y llave) en uno solo implica que estemos mezclando responsabilidades.

Una manera de darnos cuenta de estas cosas es cuando nuestras clases crecen y crecen y crecen. Si esto pasa, paremos por un instante a pensar si no estamos mezclando funcionalidad de varios objetos en uno solo.

PD: Este artículo continuará con la explicación de los ejemplos utilizados en el evento. De todas formas, pueden descargarse toda la documentación y ver el evento desde la página del grupo de usuarios SNUG

Salu2

Bibliografía utilizada: Programación Orientada a Objetos en C++ – Miguel Katrib Mora.

CommonApplicationData y sus permisos

Aquí les dejo un tips de WinForm tan rápido como me lo permite el tiempo.

Problemática: Necesitamos crear archivos desde nuestra aplicación que sean modificados por todos los usuarios del sistema operativo sin importar los permisos otorgados. Microsoft aconseja que cuando necesitemos guardar este tipo de información, usemos el CommonApplicationData, el cual es accesible para todos los usuarios.

Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)

La estructura organizativa que se aconseja mantener en dicho directorio sería [Nombre de la compañíaNombre de la aplicación], pero esto incluye una problemática a nuestro desarrollo, y es que a los archivos creados desde nuestra aplicación, se les asignan permisos de escritura restringidos solo a su creador (El usuario que ejecuta la aplicación) y a cuentas administrativas (Administrador local y administradores de dominio). Si posteriormente nos autentificamos en el sistema operativo con otro usuario cuyos derechos están limitados solamente al grupo [Todos los usuarios], se nos denegará el acceso a modificar los archivos creados por el usuario anterior.

Resolución: Al crear la carpeta que contendrá nuestros archivos para la aplicación, asignémosle permisos especiales que le permitan a todos los usuarios tener acceso total a la misma, posteriormente indicamos que dichos accesos son heredables por todos los objetos que se encuentren dentro y… problema resuelto.

Aquí les dejo una pequeña clase que permite crear carpetas desde nuestra aplicación indicando si deseamos asignar acceso total a todos los usuarios.

public class CommonApplicationData
{
  public static void CreateFolder(string folderName, bool allUsers)
  {
    if (Directory.Exists(folderName)) return;

    var m_securityIdentifier = 
        new SecurityIdentifier(WellKnownSidType.WorldSid, null);

    var m_directoryInfo = Directory.CreateDirectory(folderName);

    if (!allUsers) return;

    bool m_modified;
    var m_directorySecurity = m_directoryInfo.GetAccessControl();

    AccessRule m_rule =
      
new FileSystemAccessRule(m_securityIdentifier,
                  
FileSystemRights.FullControl, 
                   InheritanceFlags.ContainerInherit | 
                   InheritanceFlags.ObjectInherit, 
                   PropagationFlags.InheritOnly, 
                   AccessControlType.Allow);

    m_directorySecurity.
           
ModifyAccessRule(AccessControlModification.Add, 
                              
m_rule, out m_modified); 

    m_directoryInfo.SetAccessControl(m_directorySecurity);
  }

}

Salu2

 

4 horas de trabajo

4 horas de trabajo me costó leer un registro de Windows desde una aplicación.

Esta vez voy a dejar la Web, y me voy a Windows Form, pero en particular, trataré un hecho que me ocurrió hace unos días y que me costó mucho rato resolver. Actualmente me encuentro desarrollando un Smartclient. Algunos valores de configuración, la aplicación los lee del registro de Windows. El instalador de dicho Smartclient ya tiene toda la lógica pensada, pero aún no está realizado.

Durante el momento de la instalación, y dependiendo de determinados procesos, se crearán los registros con los valores que necesita posteriormente la aplicación para su correcto funcionamiento. Como el instalador aún no está, usé el “regedit” para crear los registros a mano y que el desarrollo no se detuviera. Aquí empezaron los problemas….

Voy a re-crear la situación por la que pasé en una aplicación de ejemplo, la configuración del entorno de desarrollo fue Windows 7 64 bits y Visual Studio 2008, aunque el ejemplo lo haré ahora con Visual Studio 2010.

Mi aplicación de consola:

class Program
{
  
public static long Prueba
   {
     
get
     
{
       
var masterKey = Registry.LocalMachine.OpenSubKey(@»SOFTWAREPrueba»);
        
if (masterKey == null) throw new ApplicationException(«No existe el registro»);

        return
Convert.ToInt64(masterKey.GetValue(«Prueba»));
      }
   }

   static void Main(string[] args)
   {
     
Console.WriteLine(«Valor del registro: {0}», Prueba);
      
Console.ReadLine();
   }
}

 

Esta aplicación simplemente tiene una propiedad Prueba que busca un valor en el HKEY_LOCAL_MACHINESOFWAREPrueba y retorna su valor como un entero de 64 bits. Para probar mi aplicación, voy al registro de Windows y creo manualmente lo que estoy necesitando.

 

Al ejecutar mi código se genera el ApplicationException que en mi código informa que el registro no existe.

Algo pasa aquí, el código es tan sencillo que me hace dudar de un posible error, de todas maneras me voy al MSDN y busco la documentación. Según MSDN, Registry.OpenSubKey, cito textualmente, “Contiene los datos de configuración correspondientes al equipo local. Este campo lee la clave base HKEY_LOCAL_MACHINE del Registro de Windows”. Por ahí todo estaba bien. Este campo retorna un objeto del tipo RegistryKey, del cual MSDN nos dice que “representa un nodo de nivel de clave en el Registro de Windows.”

Todo parece correcto pero mi código sigue sin funcionar. Así que después de darle vueltas y vueltas a todo esto, decidí cambiar el método OpenSubKey, por CreateSubKey.

 var masterKey = Registry.LocalMachine.CreateSubKey(@»SOFTWAREPrueba»);
 

Ahora sí que pasó, claro, mi código si por alguna razón la clave no existía, la creó y por eso funcionó todo sin problemas.

Quiero ver que todo es como lo imagino, así que elimino el registro Prueba y ejecuto nuevamente la aplicación. En este punto, vuelve a pasar sin problemas, supongo que el registro fue creando nuevamente. La sorpresa llegó cuando por más que buscaba el registro en HKEY_LOCAL_MACHINESOFWAREPrueba este no aparecía. Decido realizar una búsqueda dentro del Regedit, y aparece mi registro Prueba.

Observen lo que está pasando, yo estoy desde mi aplicación creando un registro para HKEY_LOCAL_MACHINESOFWAREPrueba
y el registro se crea en HKEY_LOCAL_MACHINESOFWAREWow6432NodePrueba

En ese momento supe lo que pasaba. Mi aplicación interactúa con un Hardware cuyo driver solo funciona para 32 bits. Esto nos obligó a cambiar en el Build de la aplicación a una plataforma x86. Pues bien, si nuestra aplicación está compilada solo para trabajar con 32 bits y operamos registros de Windows sobre 64 bits, Windows 7 crea una ruta distinta a la que trazamos en nuestro código.

Si desde nuestra aplicación creamos y leemos los registros, no tendríamos problema, porque esta ruta ficticia es transparente para el Framework. El peligro, y por lo que me decidí a escribir el artículo, es porque en muchos casos los registros se crean desde instaladores, y podemos encontrarnos que lo que esperábamos que existiese porque el instalador supuestamente ha hecho su trabajo, no existe.

Nota: No quería terminar sin agradecer a Javier Conesa, miembro del grupo de usuarios SecondNug, por ayudarme a capturar las pantallas en Windows 7 64 bits. 😛 

Salu2 

La herencia mal planteada

En cuestiones de ORM (Object Relational Mapping) casi siempre asociamos una relación uno a uno (1:1) en bases de datos relacionales, con una herencia de clases. Esquematizar este tipo de diseño puede llevarnos a futuros errores cuando nos planteamos la arquitectura de cualquier proyecto.

Vamos a ver ejemplos que nos permitan entender dónde está la trampa.

Si tratamos de representar la estructura de mascotas en un diagrama de clases, pudiéramos pensar que todas tienen un dueño, al igual que todas tienen un nombre y una fecha de nacimiento. De ahí, y derivando en acciones más específicas, vemos que algunas mascotas se pueden acariciar (perro, gato), y otras no tanto (por ejemplo una serpiente, sí sí, una serpiente, que dueños locos se sobran en este mundo).

Representando esto, tendríamos una clase mascota, que tendría una propiedad persona (dueño), otra propiedad fecha de nacimiento, y todas aquellas que entendamos pueden ser comunes a todas las mascotas. Luego, cada mascota, heredaría su funcionalidad e implementaría sus funciones más específicas (no quiero complicar el diagrama porque al final no es el objetivo de este artículo)

Este diagrama, traducido a una base de datos relacional, nos quedaría con tres tablas, mascota, perro y serpiente, donde tendríamos una relación de uno a uno entre ellas, y cualquier ORM decente, sabría que leer un perro implica leer la información asociada al mismo en la tabla mascota, y lo mismo ocurriría si tratamos de insertar o actualizar cualquiera de las clases que implementen mascota.

Pero…  (Siempre hay un pero)

Si analizamos esto mismo con alumnos y maestros, ¿qué pasaría? Vamos allá…

Un maestro, al igual que un alumno, son personas, ambos tienen un nombre, apellidos, padres, edad, etc. Mientras más elementos en común tengan varias clase, más confirmamos la presencia de una herencia, así que si llevamos esto a un diagrama de clases, nos quedaría de la siguiente forma:

Visto desde el punto de vista de bases de datos, igual caeríamos en tres tablas, con una relación uno a uno entre ellas (persona, maestro y alumno) y todo perfecto con el ORM. Todo perfecto hasta que a un maestro se le ocurre estudiar. Plantearnos esta situación nos crea un problema, ¿cómo digo yo que un maestro también es un alumno? ¿Cómo puedo asumir que una misma persona ahora es estudiante y maestro?

La encapsulación de la propia herencia no lo permite, yo no podría reutilizar la información de persona ahora en alumno, por lo que aquí la herencia no aplica y, la solución pasa por convertir la herencia, en una asociación de Persona tanto en la clase Maestro como en la clase Alumno, o como una alternativa, pudiéramos definir una lista de responsabilidades asociadas a una persona, de manera que pudiera ocupar la función de alumno y maestro al mismo tiempo.

¿Cómo podemos identificar este tipo de problema cuando nos enfrentamos a un diseño de cualquier aplicación?

Roles que definen responsabilidad

Los roles son actividades que pueden ser compartidas por un elemento de nuestro diseño, siempre que exista un mismo objeto que pueda compartir más de un rol, la herencia no es la solución. Es por esto que en el análisis de mascotas esto no ocurre, ya que un perro no podrá ser nunca una serpiente y tampoco viceversa, al menos hasta hoy 😉

Fabio Maulo, team leader de para mí el mejor ORM que existe hoy en día, NHibernate, hace mucho tiempo hacía una historia sobre un personaje que vive en las colinas de Italia y que ejemplificaba este problema con la herencia.

Salu2

JSONP

Este ejemplo me decidí a escribirlo después de leer la serie de artículos publicados en el blog de José Alarcón sobre JSONP.

1- Llamadas AJAX a servidores remotos.
2- Soporte desde ASP.NET AJAX 4.0
3- Cuestiones de seguridad y ASP.NET rompiendo la compatibilidad en 3.5

Requisitos: Crear un input tipo text con auto completamiento de las ciudades de España. Para esto usaré geonames.org, que es un servicio gratuito que nos permite obtener geo-localizaciones a partir de determinados parámetros y cuya documentación la encontramos en su sitio:

http://www.geonames.org/export/geonames-search.html

Como JQuery está de moda… y este soporta JSONP, voy a usar el pluging que existe para el auto-completamiento y que lo pueden encontrar en http://bassistance.de/jquery-plugins/jquery-plugin-autocomplete/.

Con esto creo que completo todo lo que necesito así que vamos con el código. Empezamos con lo de siempre, indicando cual es el control al cual le vamos a aplicar el “autocomplete” y cuál será la fuente de datos.

$(«#txt_city»).autocomplete(«http://ws.geonames.org/searchJSON»,

Vamos a realizar un pequeño truco, porque al final el objetivo de este artículo es justificar el uso de JSONP, así empezamos indicando los parámetros del “autocomplete” esperando como respuesta un JSON.

dataType: ‘json’,

Seguimos especificando una función que realizará un Parse de los datos que me retorna geonames, para poder entregárselos al autocomplete de la manera que él lo entiende.

parse: function(data)
{
      var rows = new Array();
      data = data.geonames;

      for(var i=0; i<data.length; i++)
      {
         rows[i] = 
         {
           
data:data[i],
            value:data[i].name,
            result:data[i].name
         };
      }               

      return rows;
},

Nada importante hasta aquí.. una función que convierte el resultado en un arreglo que contiene el dato, el valor de cada elemento y el resultado. Seguimos con la función que se llamará a la hora de mostrar el autocomplete e indicamos que solo mostraremos el nombre de la ciudad.

formatItem: function(row, i, n)
{
      return row.name;
},

Continuamos incluyendo los parámetros necesarios para realizar la búsqueda (País, indicar que no deseo lugares geográficos, etc.) Todos estos parámetros están bien documentados en el sitio de geoname.

extraParams:
{
      q: »,
      limit: »,
      country: ‘ES’,
      featureClass: ‘P’,
      maxRows: 50,
      name_startsWith: function ()
      {
        return $(«#txt_city»).val()
      }
},

Luego decimos que nuestro autocomplete espera un máximo de 50 elementos y listo.

max: 50

Con definir nuestro html con un input cuyo ID=”txt_city”, tendremos nuestro autocomplete, así que ejecutamos….

OPS!!! Acceso denegado… Este error proviene precisamente por la imposibilidad de hacer un pedido a un dominio que no es el nuestro, estamos intentando hacer un Cross Site Scripting.

¿Cómo lo resolvemos? Un buen comienzo es estudiando la serie de artículos propuestos al inicio de este post, pero para nuestro ejemplo, JQuery lo hace tan simple como ya nos tiene acostumbrado. Cambiamos el tipo de dato que espera el autocomplete a jsonp:

dataType: ‘jsonp’,

y….

We are happy 🙂

Aquí les dejo un enlace al ejemplo completo, que no es más que una simple página html ya que no usamos ASP.NET para nada, es código html duro y puro.

Ejemplo

Al fin en Geeks

Hola,

Mi nombre es Omar del Valle Rodríguez, trabajo con NET desde el año 2000 cuando toda esta maravillosa tecnología era solo un programa Beta. Actualmente soy MCP, MCTS y MCPD Enterprise en NET 3.5. Tengo mi propio blog en www.odelvalle.com pero pertenecer a esta gran comunidad ha sido siempre un sueño.

Espero no desperdiciar la oportunidad que ahora me dan de hacerlo realidad, y poder aportar algo de lo que tanto he aprendido siguiendo muchos de los blogs que aquí existen.

Unas gracias especiales a Toni, y también a todo el grupo de chicos que diariamente hace posible que Second Nug siga adelante. Gracias a ellos he podido estar y sentirme mucho más cerca de la comunidad.

Hasta muy pronto…
Omar