Blog alskare

.Net y lo que surja

Traslado del blog

Posted by alskare en 02/11/2010

Bueno, pues como que no hay bien ni mal que cien años dure, ha llegado la hora de probar nuevos vientos. ¡Cómo me complico la existencia para comentar que, sencillamente, traslado el blog a una nueva ubicación: http://geeks.ms/blogs/jvelasco/

Confío que desde allí pueda seguir disfrutando tanto como lo he hecho aquí y que, entre otras cosas, pueda mantener una periodicidad de publicación superior a la que he tenido aquí.

Gracias a todos los que habéis ido siguiendo el blog y, por supuesto, gracias a todos por vuestros comentarios que han ido ayudando con ideas y en la depuración del código.

Posted in Personales | Comentarios desactivados en Traslado del blog

Calculo de Semana Santa con C#

Posted by alskare en 05/04/2010

Hacía ya bastante tiempo que no tocaba el blog, así que ya va siendo hora de añadir algún que otro post. A ver si tengo tiempo estos días para poder hacer un par de entradas que tengo en mente, aunque el quehacer diario nos impide dedicarnos todo lo que queremos.

En esta ocasión le toca el turno a unos cálculos que he tenido que realizar para poder calcular ciertos días de la Semana Santa. Para documentarme, debo reconocer que tan sólo he echado mano de nuestra querida wikipedia (http://es.wikipedia.org/wiki/C%C3%A1lculo_de_la_fecha_de_Pascua#C.C3.A1lculo), así que ha sido bastante sencillo dicho cálculo. Como siempre, intento explicar un poco los pasos seguidos para lograr el fin.

A primera vista, los cálculos se ven sencillos, puesto que se trata de conseguir los residuos de una serie de divisiones. Quizás el único hándicap puede venir representado por la tabla de constantes que se muestra en la página de la Wikipedia. ¿Cómo lo solvento?. Pues, como siempre intento hacer, sin complicarme la vida, es decir, una estructura dentro de la clase que me permita almacenar los valores y un método que los cargue:

#region Constantes cálculo
private struct ParConstantes
{
    public int M { get; set; }
    public int N { get; set; }
}

private ParConstantes getPar(int anio)
{
    ParConstantes p = new ParConstantes();
    if (anio < 1583) { throw 
            new ArgumentOutOfRangeException("El año deberá ser superior a 1583"); }
    else if (anio < 1700) { p.M = 22; p.N = 2; }
    else if (anio < 1800) { p.M = 23; p.N = 3; }
    else if (anio < 1900) { p.M = 23; p.N = 4; }
    else if (anio < 2100) { p.M = 24; p.N = 5; }
    else if (anio < 2200) { p.M = 24; p.N = 6; }
    else if (anio < 2299) { p.M = 25; p.N = 0; }
    else { throw 
        new ArgumentOutOfRangeException("El año deberá ser inferior a 2299"); }
    return p;
}
#endregion

 

Así, una vez está solventado el problema de la tabla de las constantes, lo único que queda es el tema de los cálculos. En esta ocasión, dejo la clase completa, puesto que no tiene mayor complicación.

using System;


namespace jnSoftware.Calculos
{

    /// <summary>
    /// Cálculo de Semana Santa
    /// </summary>
    class SemanaSanta
    {
        private int a;
        private int b;
        private int c;
        private int d;
        private int e;
        private DateTime pascuaResurreccion;

        private int anio;


        /// <summary>
        /// Constructor de la clase
        /// </summary>
        /// <param name="anio">Entero que representa el año del que se quiere calcular la 
        /// semana santa.</param>
        /// <exception cref="ArgumentOutOfRangeException">Se produce cuando se intenta calcular
        /// la semana santa de un año no contemplado.</exception>
        public SemanaSanta(int anio)
        {
            try
            {
                this.anio = anio;
                calculaDomingoPascua();
            }
            catch { throw; }
        }


        /// <summary>
        /// Cálculo del domingo de Pascua o domingo de Resurrección.
        /// </summary>
        private void calculaDomingoPascua()
        {
            ParConstantes p = getPar(anio);
            a = anio % 19;
            b = anio % 4;
            c = anio % 7;
            d = (19 * a + p.M) % 30;
            e = (2 * b + 4 * c + 6 * d + p.N) % 7;

            if (d + e < 10)
            {
                pascuaResurreccion = new DateTime(anio, 3, d + e + 22);
            }
            else
            {
                pascuaResurreccion = new DateTime(anio, 4, d + e - 9);
            }

            // Excepciones
            if (pascuaResurreccion == new DateTime(anio, 4, 26))
                pascuaResurreccion = new DateTime(anio, 4, 19);

            if (pascuaResurreccion == new DateTime(anio, 4, 25) && d == 28 && e == 6 && a > 10)
                pascuaResurreccion = new DateTime(anio, 4, 18);
        }


        #region Constantes cálculo
        private struct ParConstantes
        {
            public int M { get; set; }
            public int N { get; set; }
        }

        private ParConstantes getPar(int anio)
        {
            ParConstantes p = new ParConstantes();
            if (anio < 1583) { throw 
                    new ArgumentOutOfRangeException("El año deberá ser superior a 1583"); }
            else if (anio < 1700) { p.M = 22; p.N = 2; }
            else if (anio < 1800) { p.M = 23; p.N = 3; }
            else if (anio < 1900) { p.M = 23; p.N = 4; }
            else if (anio < 2100) { p.M = 24; p.N = 5; }
            else if (anio < 2200) { p.M = 24; p.N = 6; }
            else if (anio < 2299) { p.M = 25; p.N = 0; }
            else { throw 
                new ArgumentOutOfRangeException("El año deberá ser inferior a 2299"); }
            return p;
        }
        #endregion


        #region Propiedades públicas

        public DateTime MiercolesCeniza
        {
            get { return SabadoSanto.AddDays(7 * -6 - 3 ); }
        }
        
        public DateTime ViernesDolores
        {
            get { return pascuaResurreccion.AddDays(-9); }
        }

        public DateTime DomingoRamos
        {
            get { return pascuaResurreccion.AddDays(-7); }
        }

        public DateTime JuevesSanto
        {
            get { return pascuaResurreccion.AddDays(-3); }
        }

        public DateTime ViernesSanto
        {
            get { return pascuaResurreccion.AddDays(-2); }
        }

        public DateTime SabadoSanto
        {
            get { return pascuaResurreccion.AddDays(-1); }
        }

        public DateTime DomingoResurreccion
        {
            get { return pascuaResurreccion; }
        }

        #endregion

    }
    
}

Tal como puede apreciarse, tan sólo se trata de calcular el Domingo de Resurrección ( o Domingo de Pascua) y, a partir de ahí, realizar la diferencia en días para obtener el resto.

No sé si me he dejado alguna otra fecha así que animo, como siempre ha hecho el lector de este blog, a completar, entre todos, las posibles fechas que puedan ser calculadas a partir de las fechas aquí calculadas.

Posted in C# | 2 Comments »

Enviar correo electrónico desde ASP.NET (II) – Configurando el Web.Config

Posted by alskare en 03/01/2010

¡Hay que ver lo que tiene esto de dedicarse al mundillo de la programación!, que a veces te cansas de hacer un copiar y pegar de cosas que tienes por ahí perdidas y ni te preocupas de mirar cómo solventarlo de otra manera. Me llegó la hora de entonar el mea culpa por no haber visto antes un punto tan sencillo.

En una de las empresas en las que trabajo tienen un server dedicado en 1and1 y la verdad es que es todo un lujazo programar en un sitio en el que no tienes que preocuparte de si el ISP te deja añadir un componente u otro. La verdad es que ya nos ha tocado sufrir lo suyo en este sentido.

Hace ya algún tiempo escribí un artículo – Enviar correo electrónico desde ASP.NET – en el que dejaba una clase que servía para enviar correos electrónicos desde ASP (bueno, la verdad es que la misma clase tiene utilidad si quieres usarla en una aplicación de escritorio). No obstante, enviar un correo electrónico desde ASP es una tarea mucho más fácil si almacenas la configuración del servidor SMTP en el Web.Config del modo siguiente:

<system.net>
  <mailSettings>
    <smtp from="usuario@dominio.com"> <!--Cuenta válida en el servidor de correo SMTP-->
      <network host="servidorSMTP" password="contraseña" userName="userSMTP" />
    </smtp>
  </mailSettings>
</system.net>

De esta manera, cada vez que queramos crear un nuevo mensaje de correo electrónico, bastará con introducir unas pocas sentencias de código que, lógicamente, pueden complicarse dependiendo de la necesidad:

/// <summary>
 /// Envío de un mensaje de correo electrónico
 /// </summary>
 /// <param name="address">Dirección de destino</param>
 /// <param name="subject">Asunto del mensaje</param>
 /// <param name="body">Mensaje</param>
 /// <remarks>
 /// En caso de cambio de la configuración de la cuenta del remitente, 
 /// deberá realizarse en el fichero Web.config
 /// </remarks>
 public void SendMessage(string address, string subject, string body)
 {
     System.Net.Mail.MailMessage message = new System.Net.Mail.MailMessage();
     // Dirección de destino
     message.To.Add(address);
     // Asunto 
     message.Subject = subject;
     // Mensaje 
     message.Body = body;

     System.Net.Mail.SmtpClient smpt = new System.Net.Mail.SmtpClient();
     smpt.Send(message);
 }

Posted in ASP.Net | 2 Comments »

Gestionar cuentas de Outlook desde C#

Posted by alskare en 06/12/2009

Tras haberme peleado un tiempo con el tema de poder configurar la firma de Outlook desde una aplicación propia, tema que puede encontrarse en el post https://alskare.wordpress.com/2009/11/09/aadir-firma-en-outlook/, me quedé con las ganas de poder profundizar un poco más en el tema de cómo se gestiona el tema de las cuentas de correo en el registro del sistema, más que nada, porque estoy más que seguro que en breve necesitaré modificar no sólo la firma en la cuenta predeterminada.

Aunque la idea inicial es la de partir del artículo anterior, rápidamente me doy cuenta de alguna imprecisión cometida por mi parte. Imagino que las prisas y las ganas de acabar rápido logran que, en un primer intento, te dejes algún cabo suelto. En este caso, opto por dejar el artículo anterior tal cuál está y así me permitiré, desde aquí, realizar todas las correcciones o anotaciones que considere necesarias.

Una clave del registro controla las cuentas

La principal imprecisión a la que me refería es cuando indicaba que el primer byte de una determinada clave del registro “apuntaba” a la dirección de la cuenta por defecto. Después de haber intentado algún cambio en mi propio ordenador, casi me quedo sin poder entrar en Outlook al producir un error nada más entrar.

– ¿Qué has hecho para poder provocar este error?.
– Casi nada, tan sólo cambiar directamente, desde el registro, el primer byte que se muestra (el de la cuenta predeterminada) por el de otra cuenta válida
– Así, ¿sin más?. ¿Qué ha ocurrido?
– Poca cosa. No me di cuenta que el valor de dicha entrada no es un byte, son 4 bytes en formato Little Endian. Y además, esta misma clave contiene el orden en el que se encuentran las cuentas de correo. Fíjate en el ejemplo siguiente, mi cuenta por defecto es la que está en la clave 0000000b, la siguiente cuenta en la lista de Outlook es la 00000005 y así sucesivamente.

 registro

– ¿has conseguido arreglarlo?.
– Siempre que toco el registro, hago antes una copia de la rama (menos mal que no le digo que he tenido que descubrir lo del Litte Endian porque no conseguía entrar en Outlook)

Lectura de las cuentas configuradas

Una vez que conocemos tanto la cuenta predeterminada como el resto de cuentas de Outlook, tan sólo nos hace falta hacer una lectura de las mismas, así que empezamos creando una lectura, precisamente de los nombres de las cuentas.

/// <summary>
/// Obtiene una lista con las claves de las cuentas de Outlook
/// </summary>
/// <returns></returns>
public string[] getOutlookAccounts()
{
    RegistryKey rkRaiz = Registry.CurrentUser;

    string ramaCuentas =
          "Software\\Microsoft\\Windows NT"
           + "\\CurrentVersion\\Windows Messaging Subsystem"
           + "\\Profiles\\Outlook\\9375CFF0413111d3B88A00104B2A6676";

    List<string> listaCuentas = new List<string>();
    RegistryKey rkCuentas = rkRaiz.OpenSubKey(ramaCuentas);

    // Lectura del valor que contiene las cuentas de Outlook
    object oCuentas = rkCuentas.GetValue("{ED475418-B0D6-11D2-8C3B-00104B2A6676}");
    if (oCuentas != null && oCuentas.GetType().Equals(typeof(byte[])))
    {
        byte[] cuentas = (byte[])oCuentas;
        for (int i = 0; i < cuentas.Length; i += 4)
        {
            // Traspaso de Little Endian a Big Endian
            StringBuilder sb = new StringBuilder();
            sb.Append(((byte)(cuentas.GetValue(i + 3))).ToString("X2").ToLower());
            sb.Append(((byte)(cuentas.GetValue(i + 2))).ToString("X2").ToLower());
            sb.Append(((byte)(cuentas.GetValue(i + 1))).ToString("X2").ToLower());
            sb.Append(((byte)(cuentas.GetValue(i))).ToString("X2").ToLower());

            listaCuentas.Add(sb.ToString());
        }
    }
    return listaCuentas.ToArray();
}

Leer información de una cuenta

Una vez tenemos una lista con las cuentas de correo existentes, podemos empezar a pensar en cómo extraer la información de cada una de las cuentas. Para ello, nos creamos una clase auxiliar para poder almacenar la información.

namespace jnSoftware.Outlook.Accounts
{

    /// <summary>
    /// Clase que representa una cuenta de correo de Outlook en el registro
    /// </summary>
    public class MailAccount
    {

        /// <summary>
        /// Obtiene el orden en el que se encuentra la cuenta de correo en la lista de
        /// Outlook
        /// </summary>
        public int OrderList { get; internal set; }


        /// <summary>
        /// Obtiene el nombre de la clave que contiene la información de la cuenta en 
        /// el registro
        /// </summary>
        public string AccountID { get; internal set; }


        /// <summary>
        /// Obtiene un valor que indica si la cuenta es la predeterminada
        /// </summary>
        public bool IsDefaultAccount
        { get { return OrderList == 0; } }


        /// <summary>
        /// Obtiene la cuenta de correo asociada a la cuenta
        /// </summary>
        public string Email { get; internal set; }


        /// <summary>
        /// Obtiene el nombre de la cuenta de correo.
        /// </summary>
        public string AccountName { get; internal set; }


        /// <summary>
        /// Obtiene el valor establecido como firma para nuevos mensajes
        /// </summary>
        public string NewSignature { get; internal set; }


        /// <summary>
        /// Obtiene el valor establecido como firma para los mensajes que 
        /// se responden
        /// </summary>
        public string ReplySignature { get; internal set; }
             
    }
}

A partir de ahora, la imaginación al poder. Además de los métodos que se nos pueden ocurrir en un primer momento, la verdad es que, pensando un poco en detalle, las posibilidades son amplias. Incluyamos algún ejemplo:

  • Obtener una lista con la información de todas las cuentas
  • Obtener información de una cuenta concreta (por Id o por cuenta de correo)
  • Obtener información de la cuenta de correo predeterminada
  • Cambiar la firma de una cuenta concreta (no sólo la cuenta predeterminada)

Partiendo de los puntos anteriores desarrollo una clase que permita realizar los trabajos mencionados, aunque me imagino que, en el momento que tenga un poco de tiempo acabaré creando alguna aplicación en WinForms que permita realizar estos cambios de un modo más amigable. De momento, con la librería actual se pueden realizar muchos cambios desde, por ejemplo, un inicio de sesión en una red.

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Win32;


namespace jnSoftware.Outlook.Accounts
{

    /// <summary>
    /// Gestión de cuentas de correo de Outlook
    /// </summary>
    public class MailAccounts
    {


        private Dictionary<string, MailAccount> cuentasOutlook;

        // Las cuentas de correo dependen directamente de HKCU
        private RegistryKey rkRaiz
        { get { return Registry.CurrentUser; } }

        // De la rama siguiente dependen todas las cuentas de correo definidas
        // en Outlook
        private string ramaCuentas =
               "Software\\Microsoft\\Windows NT"
                + "\\CurrentVersion\\Windows Messaging Subsystem"
                + "\\Profiles\\Outlook\\9375CFF0413111d3B88A00104B2A6676";



        /// <summary>
        /// Obtiene las cuentas de correo de Outlook definidas en el registro
        /// </summary>
        /// <returns></returns>
        public List<MailAccount> GetAccounts()
        {
            readInfoAccounts();
            return new List<MailAccount>(cuentasOutlook.Values);
        }


        /// <summary>
        /// Obtiene información de una cuenta de correo determinada
        /// </summary>
        /// <param name="accountID">Identificador de la cuenta de correo. Se corresponde 
        /// con el nombre de la rama del registro</param>
        /// <exception cref="ArgumentOutOfRangeException">Se produce la excepción cuando no 
        /// se encuentra la cuenta especificada en <paramref name="accountID"/></exception>
        public MailAccount GetAccount(string accountID)
        {
            readInfoAccounts();
            try
            {
                return cuentasOutlook[accountID];
            }
            catch (KeyNotFoundException ex)
            {
                throw new ArgumentOutOfRangeException("No existe la cuenta solicitada", ex);
            }
            catch { throw; }
        }


        /// <summary>
        /// Obtiene información de la cuenta establecida por defecto
        /// </summary>
        public MailAccount GetDefaultAccount()
        {
            string[] cuentas = getOutlookIdAccounts();
            return GetAccount(cuentas[0]);
        }


        /// <summary>
        /// Obtiene información de una cuenta de correo en base a su dirección electrónica
        /// </summary>
        /// <param name="email">Cuenta de correo de la que se quiere recuperar la información</param>
        /// <exception cref="ArgumentOutOfRangeException">Se produce cuando  no se encuentra la 
        /// cuenta de correo especificada en <paramref name="email"/>.</exception>
        public MailAccount GetAcountByEmail(string email)
        {
            readInfoAccounts();

            MailAccount retVal = null;
            // Como en la Uni, desarrollamos un algoritmo de búsqueda
            foreach (MailAccount cuenta in cuentasOutlook.Values)
            {
                if (cuenta.Email == email)
                {
                    retVal = cuenta;
                    break;
                }
            }
            if (retVal != null)
            { return retVal; }
            else { throw new ArgumentOutOfRangeException("Cuenta de correo no encontrada"); }
        }


        /// <param name="email">Cuenta de correo de la que se quiere establecer la firma</param>
        /// <param name="newSignature">Nombre de la firma que quiere establecerse en la cuenta de 
        /// correo para mensajes nuevos</param>
        /// <param name="replySignature">Nombre de la firma que quiere establecerse en la cuenta 
        /// de correo para mensajes respondidos</param>
        public void SetSignature(string email, string newSignature, string replySignature)
        {
            MailAccount cuenta = GetAcountByEmail(email);

            // TODO : Queda pendiente una comprobación para que existan las firmas 
            // en la carpeta de firmas.

            RegistryKey rkCuenta = rkRaiz.OpenSubKey(ramaCuentas + "\\" + cuenta.AccountID, true);
            rkCuenta.SetValue("New Signature", GetFirmaCodificada(newSignature));
            rkCuenta.SetValue("Reply-Forward Signature", GetFirmaCodificada(replySignature));
        }


        /// <summary>
        /// Realiza la lectura de las cuentas definidas en el registro
        /// </summary>
        private void readInfoAccounts()
        {
            cuentasOutlook = new Dictionary<string, MailAccount>();
            string[] cuentas = getOutlookIdAccounts();
            for (int i = 0; i < cuentas.Length; i++)
            {
                MailAccount m = getInfoAccount(cuentas[i]);
                m.OrderList = i;
                cuentasOutlook.Add(m.AccountID, m);
            }
        }


        /// <summary>
        /// Recupera la información de una cuenta determinada.
        /// </summary>
        /// <param name="accountID"></param>
        /// <returns></returns>
        private MailAccount getInfoAccount(string accountID)
        {
            MailAccount m = new MailAccount();
            m.AccountID = accountID;

            RegistryKey rkCuentas = rkRaiz.OpenSubKey(ramaCuentas + "\\" + accountID, false);
            m.Email = binaryToString(rkCuentas.GetValue("Email"));
            m.NewSignature = binaryToString(rkCuentas.GetValue("New Signature"));
            m.ReplySignature = binaryToString(rkCuentas.GetValue("Reply-Forward Signature"));
            m.AccountName = binaryToString(rkCuentas.GetValue("Account Name"));

            return m;
        }


        /// <summary>
        /// Obtiene una lista con las claves de las cuentas de Outlook
        /// </summary>
        /// <returns></returns>
        private string[] getOutlookIdAccounts()
        {
            List<string> listaCuentas = new List<string>();
            RegistryKey rkCuentas = rkRaiz.OpenSubKey(ramaCuentas, false);

            // Lectura del valor que contiene las cuentas de Outlook
            object oCuentas = rkCuentas.GetValue("{ED475418-B0D6-11D2-8C3B-00104B2A6676}");
            if (oCuentas != null && oCuentas.GetType().Equals(typeof(byte[])))
            {
                byte[] cuentas = (byte[])oCuentas;
                for (int i = 0; i < cuentas.Length; i += 4)
                {
                    // Traspaso de Little Endian a Big Endian
                    StringBuilder sb = new StringBuilder();
                    sb.Append(((byte)(cuentas.GetValue(i + 3))).ToString("X2").ToLower());
                    sb.Append(((byte)(cuentas.GetValue(i + 2))).ToString("X2").ToLower());
                    sb.Append(((byte)(cuentas.GetValue(i + 1))).ToString("X2").ToLower());
                    sb.Append(((byte)(cuentas.GetValue(i))).ToString("X2").ToLower());

                    listaCuentas.Add(sb.ToString());
                }
            }
            return listaCuentas.ToArray();
        }


        /// <summary>
        /// Decodifica una cadena del registro en una cadena ASCII.
        /// </summary>
        /// <param name="cadena">Cadena del registro. Devuelve string.empty si la 
        /// cadena estuviese vacía.</param>
        /// <returns></returns>
        private string binaryToString(object cadena)
        {
            StringBuilder sb = new StringBuilder();
            if (cadena != null)
            {
                if (cadena.GetType().Equals(typeof(byte[])))
                {
                    byte[] entrada = (byte[])cadena;
                    ASCIIEncoding ascii = new ASCIIEncoding();

                    // Bucle para eliminar los bytes vacíos que se colocan en el registro
                    for (int i = 0; i < entrada.Length; i++)
                    {
                        if (entrada[i] != 0)
                        {
                            sb.Append(ascii.GetString(entrada, i, 1));
                        }
                        i++;
                    }
                }
            }
            return sb.ToString();
        }


        /// <summary>
        /// Codifica el nombre de la firma en un array de Bytes
        /// </summary>
        /// <returns>La codificación se hace tal cómo lo hace Outlook</returns>
        private static byte[] GetFirmaCodificada(string nombreFirma)
        {
            // Codificación del nombre de la firma
            ASCIIEncoding ascii = new ASCIIEncoding();
            byte[] firmaSinCeros = ascii.GetBytes(nombreFirma);

            // La firma de Outlook contiene un byte vacío por cada byte del nombre,
            // así que incluyo un List para no complicarme la vida y añadirle los 
            // bytes vacíos.
            List<byte> firmaConCeros = new List<byte>();

            // Se añaden los ceros a la firma
            foreach (byte b in firmaSinCeros)
            {
                firmaConCeros.Add(b);
                firmaConCeros.Add(new byte());
            }

            // No sé la razón, pero la firma le añade dos bytes más
            firmaConCeros.Add(new byte());
            firmaConCeros.Add(new byte());

            return firmaConCeros.ToArray();
        }


    }       // class
}           // namespace

 

En esta ocasión no dejo las librerías listas para la descarga puesto que así intento obligarme a crear una tercera entrega dedicada a Outlook en la que intentaré que se puedan establecer algunos cambios desde una aplicación WinForms.

Posted in C#, Outlook | 1 Comment »

Añadir firma en Outlook programáticamente

Posted by alskare en 09/11/2009

La inserción de elementos que se añadan al final de nuestros mensajes en Outlook es una tarea realmente sencilla, puesto que bastará con acudir, desde el propio Outlook al menú Herramientas, Opciones, Formato de correo y “trastear” un poco con el apartado de Firma.

No obstante, en una de las empresas en las que trabajo se me ha encargado la tarea de configurar a todos los usuarios una firma estándar para todos los mensajes salientes de Outlook. Debo advertir que en esta empresa no existe ningún tipo de servidor del tipo Exchange, es más, los usuarios van conectando de modo automático a una serie de servidores POP3/IMAP de los que van descargando todos los mensajes. Con estas condiciones, sólo me quedan un par de caminos a seguir: o consigo realizar algún tipo de “distribución” de las firmas con alguna directiva de sistema o me tocará añadir, a mano, las firmas de todos los usuarios. No es que sean muchos usuarios, pero a la veintena sí que llegan y, la verdad, ante cualquier cambio, prefiero encontrar la manera más cómoda de hacerlo.

Si a estos prolegómenos le añadimos que algún usuario entra al sistema bien sea mediante Terminal Server o bien mediante la red de la oficina, hecho que provoca que se desactive la firma aunque esté, casi reconozco que no me queda otro remedio que intentar encontrar algún tipo de solución “programática” para el cambio de la firma. Nunca he sabido la causa de la desactivación de la firma, aunque en algún foro he leído que puede ser consecuencia de tener Word como editor del correo de Outlook, pero cualquiera les quita el corrector ortográfico a la gente… (si con él puesto ya te escriben hortografía, ni me imagino cómo sería sin él).

Uno de las primera cosas que más me llama la atención con el tema de las firmas es que, como Outlook permite crear mensajes en tres tipos de formato distintos, cada formato podría tener una firma diferente. Todo depende de qué opción tengamos seleccionada en Herramientas, Opciones, Formato de correo: HTML, Texto enriquecido (formato RTF) o Texto sin formato (ASCII). Por tanto empiezo creando tres ficheros con el mismo nombre, adecuando el formato a cada una de las tres opciones:

  • MiFirma.htm
  • MiFirma.rft
  • MiFirma.txt

Ubicación de las firmas

Con alguna que otra “trampilla” logro descubrir que Outlook tiene un lugar especial en el que almacena las firmas y que éste lugar depende de la versión de Outlook, así, resumo las carpetas de almacenaje en la tabla siguiente:

Versión Carpeta
O2003 \Documents and Settings\%USERNAME%Datos de programa\Microsoft\Signatures
O2007 \Documents and Settings\%USERNAME%Datos de programa\Microsoft\Firmas

¡Cómo me divierten este tipo de cambios!. Ahora resulta que tendremos que mirar la versión del usuario antes de empezar a hacer nada… Bueno, vamos a empezar a copiar los ficheros en dichas carpetas.

Un pequeño apunte para usuarios de Outlook 2003 que quieran añadir imágenes en la firma. Por un lado, las imágenes podrían estar almacenadas en una carpeta /images, justo a continuación de las rutas mencionadas en la tabla anterior y, para hacer insertar dichas imágenes, bastará con referenciarlas como src=”images/nombreImagen.jpg”, Además, deberemos acudir a Herramientas, Opciones, Formato de Correo, Formato de Internet y activar la opción Si un mensaje HTML contiene imágenes ubicadas en Internet, envíe una copia de las mismas en vez de la referencia de su ubicación.

Establecimiento de la firma

Este es el apartado más peliagudo de todo el proceso puesto que, una vez copiados los ficheros de las firmas, de forma manual sería sencillo seguir los pasos indicados al principio (Herramientas, Opciones, Formato de Correo y seleccionar como firma la denominada MiFirma), no obstante, éste sería el paso que querría evitar. Al fin y al cabo, una copia de ficheros puedo hacerla por medio de un simple fichero batch que se ejecute una vez que entre el usuario, gracias a las GPO, pero me queda averiguar cómo podría activar esta firma a los usuarios que quiera.

En las GPO que uso, por lo menos en la versión 2003 no está incluye ningún tipo de opción que permita definir la firma que tiene activa el usuario. De alguna búsqueda extraigo algún fragmento para las directivas que, según los autores, permiten establecer esta opción, pero las pruebas que hago no son fructíferas y no sólo no me establecen ningún tipo de firma sino que desactivan la opción de poder cambiar de firma, así que me decido a buscar algún otro tipo de solución.

Vamos a jugar un poco con el registro del sistema para llevarnos una desagradable sorpresa. Las cuentas que normalmente tenemos configuradas en Outlook están “almacenadas” en la rama siguiente:

HKEY_CURRENT_USER
  \Software
    \Microsoft
      \Windows NT
        \CurrentVersion
          \Windows Messaging Subsystem
            \Profiles
              \Outlook
                \9375CFF0413111d3B88A00104B2A6676

Y, para desespero, las “reglas del juego” son las siguientes: En la clave mencionada existe una clave, concretamente la clave {ED475418-B0D6-11D2-8C3B-00104B2A6676}, que tiene un tipo de datos REG_BINARY. El primer byte del valor de esta cadena incluye un número (en mi caso, por ejemplo, es el 0b). Este dato nos informará de la cuenta que hay por defecto y que será una de las ramas de la clave del cuadro anterior. Una de las cosas buenas que le veo es que, tanto para O2003 como para O2007, es la misma rama.

 image

Así, una vez hemos podido encontrar la cuenta predeterminada en Outlook, es fácil ver existen dos claves que se refieren a la firma. Estas dos claves, por supuesto, también son valores REG_BINARY y son las siguientes:

  • New Signature
  • Reply-Forward Signature

Un poco de código C#

Debo reconocer que este punto lo escribo días más tarde que el resto del post; la razón es muy sencilla, en algún sitio he visto algún script de VisualScript que permitía el cambio requerido en el registro para el tema de las firmas, pero a la hora de intentar implementarlo, siempre me he encontrado con problemas de seguridad al escribir en el registro. Entre esto y que no soy muy ducho con el tema de VisualScript, he optado por crear una pequeña utilidad en C# que me escriba las claves que necesito.

El objetivo de la aplicación, que posteriormente ejecutaré desde las GPO de Windows Server tendrá que ser una aplicación de consola que se ejecute con un parámetro, concretamente, el nombre de la firma que se quiere “predeterminar” en Outlook. Algo así cómo :

CambiaFirma.exe MiFirma

Por tanto, echo mano de una clase que desconocía hasta el momento, pero que me ha gustado bastante, la clase Microsoft.Win32.RegistryKey, puesto que resuelve casi todos los problemas de escritura en el registro.

Muestro la clase tal cual, incluyendo una pantalla de consola que muestra el error en caso de que exista. Imagino que en la implementación final la quitaré para que, en caso de problemas no aparezca la pantallita.

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Win32;


namespace CambiaFirma
{

    /// <summary>
    /// Ejecución del programa de cambio de firma
    /// </summary>
    class CambiarFirma
    {


        /// <summary>
        /// Inicio de la aplicación. Comprobación de argumento y gestión 
        /// de la firma
        /// </summary>
        /// <param name="args">Deberá incluir un parámetro: NombreFirma</param>
        static void Main(string[] args)
        {
            try
            {
                // Lectura de parámetros para obtener el nombre de la firma
                if (args.Length != 1)
                    throw new ArgumentException("Debe añadirse el nombre de la firma como parámetro");
                string nombreFirma = args[0];

                // Gestión de firma
                GestionFirma gf = new GestionFirma(nombreFirma);
                gf.EscribeFirma();
            }
            catch (Exception ex)
            {
                MuestraError(ex);
            }
        }


        /// <summary>
        /// Muestra un error por pantalla de consola
        /// </summary>
        /// <param name="ex"></param>
        private static void MuestraError(Exception ex)
        {
            Console.Clear();
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine("ERROR EN CREACIÓN DE FIRMA");
            Console.WriteLine();
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine(ex.Message);
            Console.ForegroundColor = ConsoleColor.White;

            for (int i = Console.CursorTop; i < Console.WindowHeight - 1; i++)
            {
                Console.WriteLine();
            }
            Console.Write("Pulsa una tecla para continuar ... : ");
            Console.ReadKey(true);
        }
    }



    /// <summary>
    /// Clase que añade una firma a la cuenta por defecto de Outlook
    /// </summary>
    /// <remarks>Provado en Outlook 2003 y Outlook 2007</remarks>
    class GestionFirma
    {
        private string NombreFirma { get; set; }

        /// <summary>
        /// Constructor de la clase.
        /// </summary>
        /// <param name="nombreFirma">
        /// Texto que representa la firma que se insertará en Outlook
        /// </param>
        public GestionFirma(string nombreFirma)
        {
            NombreFirma = nombreFirma;
        }

        /// <summary>
        /// Cambia el registro del sistema de manera que la firma quedará activada
        /// en Outlook
        /// </summary>
        public void EscribeFirma()
        {
            RegistryKey r = GetRamaCuentaPredeterminadaOutlook();

            byte[] firma = GetFirmaCodificada(NombreFirma);

            // Escritura de los valores
            r.SetValue("New Signature", firma);
            r.SetValue("Reply-Forward Signature", firma);
        }

        /// <summary>
        /// Obtiene la rama del registro correspondiente a la cuenta predeterminada 
        /// de Outlook
        /// </summary>
        /// <returns></returns>
        private RegistryKey GetRamaCuentaPredeterminadaOutlook()
        {
            // Lectura de la rama \\HKCU
            RegistryKey rkRaiz = Registry.CurrentUser;

            // Lectura de la rama en al que se almacena el correo
            RegistryKey rkCorreo =
                rkRaiz.OpenSubKey(
                        "Software\\Microsoft\\Windows NT"
                        + "\\CurrentVersion\\Windows Messaging Subsystem"
                        + "\\Profiles\\Outlook\\9375CFF0413111d3B88A00104B2A6676");

            // Se obtiene el valor de la clave que contiene la cuenta predeterminada
            byte[] cuentaDefecto = (byte[])rkCorreo.GetValue(@"{ED475418-B0D6-11D2-8C3B-00104B2A6676}");

            // Se obtiene el valor de la subclave que contiene la cuenta predeterminada
            string claveCuentaDefecto = "000000" + ((byte)(cuentaDefecto.GetValue(0))).ToString("X2").ToLower();

            // Se abre la rama con la cuenta predeterminada con permisos de escritura
            RegistryKey rkCuentaDefecto = rkCorreo.OpenSubKey(claveCuentaDefecto, true);

            return rkCuentaDefecto;
        }

        /// <summary>
        /// Codifica el nombre de la firma en un array de Bytes
        /// </summary>
        /// <returns>La codificación se hace tal cómo lo hace Outlook</returns>
        private static byte[] GetFirmaCodificada(string nombreFirma)
        {
            // Codificación del nombre de la firma
            ASCIIEncoding ascii = new ASCIIEncoding();
            byte[] firmaSinCeros = ascii.GetBytes(nombreFirma);

            // La firma de Outlook contiene un byte vacío por cada byte del nombre,
            // así que incluyo un List para no complicarme la vida y añadirle los 
            // bytes vacíos.
            List<byte> firmaConCeros = new List<byte>();

            // Se añaden los ceros a la firma
            foreach (byte b in firmaSinCeros)
            {
                firmaConCeros.Add(b);
                firmaConCeros.Add(new byte());
            }

            // No sé la razón, pero la firma le añade dos bytes más
            firmaConCeros.Add(new byte());
            firmaConCeros.Add(new byte());

            return firmaConCeros.ToArray();
        }
    }

}

Ahora, con el programita éste ya puedo ejecutarlo de tal manera que, cada vez que un usuario inicie la sesión, se le establezca la firma deseada en la cuenta predeterminada de Outlook.

Confío que no me hagan establecer una firma para cada una de las cuentas que tengan configuradas en Outlook, pero con los jefes “tan listos” de las empresas, nunca se sabe…”Si total, como es algo fácil establecer la firma… ¿Ves?, sólo hay que hacer Herramientas, Opciones, Formato de correo

Como siempre, dejo enlace para descarga por si alguien no tiene el compilador o, sencillamente, no tiene ganas de hacer un Copy & Paste.

Descarga del programa: aquí

Posted in Outlook | 6 Comments »

C#. Generador de Passwords aleatorios

Posted by alskare en 13/10/2009

Estoy más que seguro que cualquiera de nosotros hemos sido usuarios en cualquier ocasión de algún tipo de herramienta similar a la que intento crear en estos momentos. De hecho, con una búsqueda bastante genérica en Google surgen multitud de rutinas en cualquier lenguaje. No obstante, como soy de aquellas personas que prefiere crear sus propias rutinas antes de hacer un “Copia y Pega” de lo que han hecho otros, me dispongo a la creación de una utilidad que me permita obtener contraseñas de una manera aleatoria.

¿Qué necesito?

En principio, sólo necesito una rutina que me permita generar un password de 8 caracteres, y que incluya, al menos, un número, un carácter no alfanumérico y algún carácter en mayúscula. Podemos apreciar enseguida que, aunque la necesidad inminente es bastante sencilla, lo mejor es poder parametrizar estos elementos a los que podemos llamar “Caracteres especiales”:

  • Números
  • Mayúsculas
  • Símbolos

Debo reconocer que, a la hora de parametrizar valores, soy un amante de los porcentajes y, en este principio, baso muchas de las rutinas creadas. Es decir, si genero una rutina que me permita crear una contraseña de 10 caracteres, prefiero decir que tenga un 20% de números, un 20% de mayúsculas y otro 20% de símbolos para que la contraseña tenga: 2 números + 2 letras mayúsculas + 2 símbolos + 4 caracteres normales en minúscula. De esta manera, si tengo que ampliar la longitud de la contraseña a 20 caracteres, automáticamente cambiará el número de caracteres especiales, manteniendo la proporción.

Otro de los puntos a tener en cuenta es que los caracteres que pueden emplearse no siempre serán válidos en todos los entornos, por tanto, aunque no me he preocupado de parametrizar este punto, sí que he tenido la precaución de “separarlo” por si alguien tiene que añadir/eliminar algún carácter no compatible, por ejemplo, con un determinado servidor de correo.

string caracteres = "abcdefghijklmnopqrstuvwxyz";
string numeros = "0123456789";
string simbolos = "%$#@+-=&";

¿Cómo enfoco el resultado?

En mi caso, con contraseñas de 8 caracteres suelo pasar. De hecho, debo reconocer que la necesidad de la creación de una rutina que cree passwords automáticamente viene dada por una aplicación Web que tengo que crear en la que, cuando un usuario pierde una contraseña, se le enviará por e-correo y, la primera vez que entre, tendrá que cambiarla, así que prima más la “estética” de la contraseña que la seguridad. Lo que pasa es que, a la gente le gusta más ver símbolos raros que cualquier nombre.

Pues eso, que la idea inicial es la de crear un constructor estándar con 8 caracteres, de los cuales tendrá un 20% de caracteres especiales. Otro constructor que permita crear una contraseña con una longitud variable y otro que permita definir todo:

public GeneradorPassword()
{ }

public GeneradorPassword(int longitudCaracteres)
{ }

public GeneradorPassword(int longitudCaracteres, 
                         int porcentajeMayusculas, 
                         int porcentajeSimbolos, 
                         int porcentajeNumeros)
{ }

En cuanto al sistema de crear la rutina, no me complico tampoco la vida:

  1. Creo una cadena con la longitud de la contraseña todo en minúscula
  2. Obtengo el número de caracteres especiales a añadir
  3. Obtengo una posición aleatoria para colocar los caracteres especiales
  4. Reemplazo los caracteres especiales en las posiciones obtenidas.

El resultado

Dejo la rutina tal cuál me ha quedado:

using System;
using System.Collections.Generic;
using System.Text;

namespace jnSoftware.Utiles
{
    /// <summary>
    /// Clase que permite la generación de una contraseña. 
    /// La contraseña contiene un número de caracteres fijos y permite especificar el porcentaje
    /// de caracteres en mayúsculas y símbolos que se quieren obtener
    /// </summary>
    public class GeneradorPassword
    {


        /// <summary>
        /// Enumeración que permite conocer el tipo de juego de carácteres a emplear
        /// para cada carácter
        /// </summary>
        private enum TipoCaracterEnum { Minuscula, Mayuscula, Simbolo, Numero }

        #region Campos

        private int porcentajeMayusculas;
        private int porcentajeSimbolos;
        private int porcentajeNumeros;
        Random semilla;

        // Caracteres que pueden emplearse en la contraseña
        string caracteres = "abcdefghijklmnopqrstuvwxyz";
        string numeros = "0123456789";
        string simbolos = "%$#@+-=&";

        // Cadena que contiene el password generado
        private StringBuilder password;

        #endregion



        #region Propiedades

        /// <summary>
        /// Obtiene o establece la longitud en carácteres de la contraseña a obtener
        /// </summary>
        public int LongitudPassword { get; set; }

        /// <summary>
        /// Obtiene o establece el porcentaje de carácteres en mayúsculas que 
        /// contendrá la contraseña
        /// </summary>
        /// <exception cref="ArgumentOutOfRangeException">Se produce al intentar introducir
        /// un valor que no coincida con un porcentaje</exception>
        public int PorcentajeMayusculas
        {
            get { return porcentajeMayusculas; }
            set
            {
                if (value < 0 || value > 100)
                    throw new ArgumentOutOfRangeException("El porcentaje es un número entre 0 y 100");
                porcentajeMayusculas = value;
            }
        }


        /// <summary>
        /// Obtiene o establece el porcentaje de símbolos que contendrá la contraseña
        /// </summary>
        /// <exception cref="ArgumentOutOfRangeException">Se produce al intentar introducir
        /// un valor que no coincida con un porcentaje</exception>
        public int PorcentajeSimbolos
        {
            get { return porcentajeSimbolos; }
            set
            {
                if (value < 0 || value > 100)
                    throw new ArgumentOutOfRangeException("El porcentaje es un número entre 0 y 100");
                porcentajeSimbolos = value;
            }
        }

        /// <summary>
        /// Obtiene o establece el número de caracteres numéricos que contendrá la contraseña
        /// </summary>
        /// <exception cref="ArgumentOutOfRangeException">Se produce al intentar introducir
        /// un valor que no coincida con un porcentaje</exception>
        public int PorcentajeNumeros
        {
            get { return porcentajeNumeros; }
            set
            {
                if (value < 0 || value > 100)
                    throw new ArgumentOutOfRangeException("El porcentaje es un número entre 0 y 100");
                porcentajeNumeros = value;
            }
        }

        #endregion


        #region Constructores
        /// <summary>
        /// Constructor. La contraseña tendrá 8 caracteres, incluyendo una letra mayúscula, 
        /// un número y un símbolo
        /// </summary>
        public GeneradorPassword()
            : this(8)
        { }


        /// <summary>
        /// Constructor. La contraseña tendrá un 20% de caracteres en mayúsculas y otro tanto de 
        /// símbolos
        /// </summary>
        /// <param name="longitudCaracteres">Longitud en carácteres de la contraseña a obtener</param>
        /// <exception cref="ArgumentOutOfRangeException">Se produce al intentar introducir
        /// un porcentaje de caracteres especiales mayor de 100</exception>
        public GeneradorPassword(int longitudCaracteres)
            : this(longitudCaracteres, 20, 20, 20)
        { }


        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="longitudCaracteres">Longitud en carácteres de la contraseña a obtener</param>
        /// <param name="porcentajeMayusculas">Porcentaje a aplicar de caracteres en mayúscula</param>
        /// <param name="porcentajeSimbolos">Porcenta a aplicar de símbolos</param>
        /// <param name="porcentajeNumeros">Porcentaje de caracteres numéricos</param>
        /// <exception cref="ArgumentOutOfRangeException">Se produce al intentar introducir
        /// un porcentaje de caracteres especiales mayor de 100</exception>
        public GeneradorPassword(int longitudCaracteres, int porcentajeMayusculas, int porcentajeSimbolos, int porcentajeNumeros)
        {
            LongitudPassword = longitudCaracteres;
            PorcentajeMayusculas = porcentajeMayusculas;
            PorcentajeSimbolos = porcentajeSimbolos;
            PorcentajeNumeros = porcentajeNumeros;

            if (PorcentajeMayusculas + porcentajeSimbolos + PorcentajeNumeros > 100)
                throw new ArgumentOutOfRangeException(
                "La suma de los porcentajes de caracteres especiales no puede superar el " +
                "100%, es decir, no puede ser superior a la longitud de la contraseña");
            semilla = new Random(DateTime.Now.Millisecond);
        }

        #endregion


        #region Métodos públicos

        /// <summary>
        /// Obtiene el password
        /// </summary>
        /// <returns></returns>
        public string GetNewPassword()
        {
            GeneraPassword();
            return password.ToString();
        }


        /// <summary>
        /// Permite establecer el número de caracteres especiales que se quieren obtener
        /// </summary>
        /// <param name="numeroCaracteresMayuscula">Número de caracteres en mayúscula</param>
        /// <param name="numeroCaracteresNumericos">Número de caracteres numéricos</param>
        /// <param name="numeroCaracteresSimbolos">Número de caracteres de símbolos</param>
        public void SetCaracteresEspeciales(
            int numeroCaracteresMayuscula
            , int numeroCaracteresNumericos
            , int numeroCaracteresSimbolos)
        {
            // Comprobación de errores
            if (numeroCaracteresMayuscula
                    + numeroCaracteresNumericos
                    + numeroCaracteresSimbolos > LongitudPassword)
                throw new ArgumentOutOfRangeException(
                    "El número de caracteres especiales no puede superar la longitud del password");

            PorcentajeMayusculas = numeroCaracteresMayuscula * 100 / LongitudPassword;
            PorcentajeNumeros = numeroCaracteresNumericos * 100 / LongitudPassword;
            PorcentajeSimbolos = numeroCaracteresSimbolos * 100 / LongitudPassword;
        }



        /// <summary>
        /// Constructor. La contraseña tendrá 8 caracteres, incluyendo una letra mayúscula, 
        /// un número y un símbolo
        /// </summary>
        public static string GetPassword()
        {
            // Se crea un método estático para facilitar el uso
            GeneradorPassword gp = new GeneradorPassword();
            return gp.GetNewPassword();
        }


        #endregion


        #region Métodos de cálculo

        /// <summary>
        /// Método que genera el password. Primero crea una cadena de caracteres 
        /// en minúscula y va sustituyendo los caracteres especiales
        /// </summary>
        private void GeneraPassword()
        {
            // Se genera una cadena de caracteres en minúscula con la longitud del 
            // password seleccionado
            password = new StringBuilder(LongitudPassword);
            for (int i = 0; i < LongitudPassword; i++)
            {
                password.Append(GetCaracterAleatorio(TipoCaracterEnum.Minuscula));
            }

            // Se obtiene el número de caracteres especiales (Mayúsculas y caracteres) 
            int numMayusculas = (int)(LongitudPassword * (PorcentajeMayusculas / 100d));
            int numSimbolos = (int)(LongitudPassword * (PorcentajeSimbolos / 100d));
            int numNumeros = (int)(LongitudPassword * (PorcentajeNumeros / 100d));

            // Se obtienen las posiciones en las que irán los caracteres especiales
            int[] caracteresEspeciales =
                    GetPosicionesCaracteresEspeciales(numMayusculas + numSimbolos + numNumeros);
            int posicionInicial = 0;
            int posicionFinal = 0;

            // Se reemplazan las mayúsculas
            posicionFinal += numMayusculas;
            ReemplazaCaracteresEspeciales(caracteresEspeciales,
                 posicionInicial, posicionFinal, TipoCaracterEnum.Mayuscula);

            // Se reemplazan los símbolos
            posicionInicial = posicionFinal;
            posicionFinal += numSimbolos;
            ReemplazaCaracteresEspeciales(caracteresEspeciales,
                 posicionInicial, posicionFinal, TipoCaracterEnum.Simbolo);

            // Se reemplazan los Números
            posicionInicial = posicionFinal;
            posicionFinal += numNumeros;
            ReemplazaCaracteresEspeciales(caracteresEspeciales,
                 posicionInicial, posicionFinal, TipoCaracterEnum.Numero);
        }



        /// <summary>
        /// Reemplaza un caracter especial en la cadena Password
        /// </summary>
        private void ReemplazaCaracteresEspeciales(
                                        int[] posiciones
                                        , int posicionInicial
                                        , int posicionFinal
                                        , TipoCaracterEnum tipoCaracter)
        {
            for (int i = posicionInicial; i < posicionFinal; i++)
            {
                password[posiciones[i]] = GetCaracterAleatorio(tipoCaracter);
            }
        }



        /// <summary>
        /// Obtiene un array con las posiciones en las que deberán colocarse los caracteres
        /// especiales (Mayúsculas o Símbolos). Es importante que no se repitan los números
        /// de posición para poder mantener el porcentaje de dichos carácteres
        /// </summary>
        /// <param name="numeroPosiciones">Valor que representa el número de posiciones
        /// que deberán crearse sin repetir</param>
        private int[] GetPosicionesCaracteresEspeciales(int numeroPosiciones)
        {
            List<int> lista = new List<int>();
            while (lista.Count < numeroPosiciones)
            {
                int posicion = semilla.Next(0, LongitudPassword);
                if (!lista.Contains(posicion))
                {
                    lista.Add(posicion);
                }
            }
            return lista.ToArray();
        }


        /// <summary>
        /// Obtiene un carácter aleatorio en base a la "matriz" del tipo de caracteres
        /// </summary>
        private char GetCaracterAleatorio(TipoCaracterEnum tipoCaracter)
        {
            string juegoCaracteres;
            switch (tipoCaracter)
            {
                case TipoCaracterEnum.Mayuscula:
                    juegoCaracteres = caracteres.ToUpper();
                    break;
                case TipoCaracterEnum.Minuscula:
                    juegoCaracteres = caracteres.ToLower();
                    break;
                case TipoCaracterEnum.Numero:
                    juegoCaracteres = numeros;
                    break;
                default:
                    juegoCaracteres = simbolos;
                    break;
            }

            // índice máximo de la matriz char de caracteres
            int longitudJuegoCaracteres = juegoCaracteres.Length;

            // Obtención de un número aletorio para obtener la posición del carácter
            int numeroAleatorio = semilla.Next(0, longitudJuegoCaracteres);

            // Se devuelve una posición obtenida aleatoriamente
            return juegoCaracteres[numeroAleatorio];
        }

        #endregion

    }
}

 

La rutina puede probarse gracias a los amigos de velasco.biz: Probar rutina

Posted in C#, Helpers, NetFramework | 3 Comments »