Sql Server 2008 Management Studio

Últimamente toco poco el MS Sql Server, puesto que casi todas las empresas con las que colaboro parece que han coincidido en usar MySql en sus desarrollos. No es mi trabajo decidir sobre qué servidor se trabajará así que sólo me limito a intentar crear las aplicaciones de la manera más genérica posible (mejor dicho, que sé hacer) con la finalidad de poder cambiar en el futuro sin mucho trastorno.

Bueno, el caso es que no hace mucho se me ocurrió instalar Visual Studio 2010 en mi portátil y me actualizó SQL Server, de la versión 2005 a  la 2008 (versión Express, que tampoco necesito más). Así, me pongo el otro día a modificar una base y ¡¡Sorpresa!!. Me da problemas de permisos para poder modificar la tabla desde las herramientas del SQL SMS. ¡Pues mira qué bien!., me deja modificar elementos si trabajo desde SQL, también me deja modificar lo que quiera si lo hago desde VisualStudio (desde la versión 2005 y desde la 2010), pero no me deja usar las herramientas a las que estoy acostumbrado (Vale, reconozco que un poco de cabezonería sí que hay, que podría haber hecho lo mismo desde VisualStudio, pero si no nos pusiéramos así de vez en cuando, no sería tan divertido).

¡Vamos al grano!. Con VisualStudio se añade un “Centro de Instalación” de Sql Server el cual, como es de esperar, no sé usar, así que opto por ir a la mía y descargarme la aplicación (unos quinientos y pico megas) desde la página oficial.

Una vez descargado, lo ejecuto y la primera sorpresa es que me devuelve un error de compatibilidad, así que me lío y comienzo a descargar el SP1 de Sql Server 2008.

image

 

Como una vez descargado e instalado el SP1 de SqlServer continúo con la misma pantalla de compatibilidad, oso pulsar sobre el botón Ejecutar programa, que me lleva al mismo sitio en el que había estado al ejecutar el “Centro de Instalación”.

– ¡Mal vamos, Jesús!. Vigila, que si cuentas esto, luego se ríen de ti.

Esta es la pantalla que aparece:

image

 

Yo, que de SQL Server tampoco sé mucho, pero como ya he tenido algún que otro problema con instancias, se me ocurre intentar hacer una actualización desde SQL Server 2000 o SQL Server 2005 para evitar que se me creen nuevas instancias o desaparezcan alguna de las existentes. De este modo, me lío a ejecutar la típica instalación “Siguiente-Siguiente” que conlleva unas cuantas verificaciones para comprobar que el sistema es adecuado, los términos de licencia y poca cosa más. Justo en el momento en el que esperaba encontrar la típica pantalla en la que me permitiese seleccionar las herramientas que quiero instalar/desinstalar, me aparece otra en la que no me da la posibilidad de modificar ninguna opción y de la cual no me deja pasar.

 

image

 

Por cierto, al darle esta vez a “Siguiente”, el error con el que me encuentro es el siguiente:

 

image

 

¡Joroba!

Solución: La opción correcta es Nueva instalación independiente de SQL Server o agregar características a una instalación existente. ¿Por qué nos empeñamos siempre en leer sólo los primeros caracteres de las oraciones?. Este despiste me costó casi una tarde completamente perdida. Aunque ya empiezo a ilusionarme, aquí no acaba mi peregrinación, puesto que, desde la opción de agregar características a una instalación existente, una de las pantallas en la que no dudo la opción es la siguiente:

image

 

Como digo, sin dudar, selecciono “Agregar características a una instancia existente de SQL Server 2008”. ¡Buf!. Llego, casi igual que antes a una pantalla en la que no me deja modificar nada y que, al igual que anteriormente, me devuelve el mismo mensaje de errores de validación.

image

 

¡Ya no puedo más!. Tengo que echar mano de San Google y comprobar que he seleccionado mal la opción de actualizar en vez de seleccionar una nueva instalación.

image

 

No sé, yo ya me hago viejo para estas cosas y ya estaba acostumbradito a tener una pantalla de instalación en la que si quiero agregar nuevas opciones sólo tengo que activar una casilla de verificación y la inversa para desinstalar…

 

Por cierto, la fuente que me salvó de seguir enredado está en http://www.asql.biz/Articoli/SQLX08/Art3_1.aspx.

Crear un protector de pantalla con C#

Hace ya algún tiempo, en una de las empresas en las que colaboro, se me ocurrió la idea de hacer un protector de pantalla un poco personalizado. Como el tema del diseño no es que sea mi fuerte, opté por hacer algo sencillito y que, entre otras cosas, intentara tener la aceptación de los usuarios. Así, me marqué los siguiente objetivos:

  • Fondo negro ( ¿he dicho que el diseño no es lo mío? )
  • Movimiento
  • Sencillez

Es curioso el efecto que puede llegar a hacer un par de logotipos moviéndose por la pantalla de nuestro ordenador…así que nos ponemos manos a la obra.

¿ Qué necesito para crear un protector de pantalla?.

Nada. Sólo las herramientas que estamos usando día tras día. Igual ahora que estamos tan metidos todos en los temas de Web, el empezar un proyecto de WinForms nos da un poco de pereza, pero creo que todos hemos empezado por ahí. De hecho, un protector de pantalla tan sólo es un proyecto .EXE al cual, una vez compilado, le cambiaremos la extensión por .SCR. Vale… ya sé que un protector de pantalla debería tener una serie de opciones que el usuario podría cambiar y además se debería poder ver desde la ventana de Vista Previa de los protectores, pero estamos hablando de un protector sencillito y corporativo, de tal manera que, para simplificar, no vamos a dejar que el usuario modifique ninguna opción (Al fin y al cabo, cuando esté acabado, se lo impondremos mediante directiva de sistema, así que no hacen falta muchas opciones).

Empezamos pues, creando un proyecto WinForms, que tendrá un único formulario, al que cambiaremos las propiedades siguientes:

   1: public Form1()
   2: {
   3:     InitializeComponent();
   4:  
   5:     // Fondo negro
   6:     this.BackColor = Color.FromArgb(0, 0, 0);
   7:     // Ejecución maximizada
   8:     this.WindowState = FormWindowState.Maximized;
   9:     // Eliminamos los bordes del formulario
  10:     this.FormBorderStyle = FormBorderStyle.None;
  11:     // El formulario recibirá cualquier evento antes que sus controles
  12:     this.KeyPreview = true;
  13:     // El formulario, siempre en primer plano
  14:     this.TopLevel = true;
  15: }

Configuremos cómo cerrar el formulario

Antes de nada, intentaremos configurar el entorno para que podamos cerrar el protector. A mí siempre me han molestado los protectores de pantalla que se descargan cuando, sencillamente mueves el ratón, aunque sea por error, así que como ahora soy yo el que lo hace, lo haré a mi gusto: para descargar el protector, sólo será válida la tecla de ESC o un clic de ratón. Estas opciones son muy fáciles de modificar al gusto del consumidor.

   1: public Form1()
   2: {
   3:     ... // Código anterior
   4:     this.TopLevel = true;
   5:  
   6:     // Control de eventos
   7:     this.Click += new EventHandler(Form1_Click);
   8:     this.KeyPress += new KeyPressEventHandler(Form1_KeyPress);
   9: }
  10:  
  11: void Form1_Click(object sender, EventArgs e)
  12: { 
  13:     CierraFormulario(); 
  14: }
  15:  
  16: void Form1_KeyPress(object sender, KeyPressEventArgs e)
  17: {
  18:     if (e.KeyChar == (char)Keys.Escape)
  19:         CierraFormulario(); 
  20: }
  21:  
  22: /// <summary>
  23: /// Cierra el formulario
  24: /// </summary>
  25: private void CierraFormulario()
  26: {
  27:     this.Close();
  28: }

Vamos a rellenar el formulario

Un Timer será nuestro mejor aliado a la hora de conseguir el efecto del movimiento cada cierto tiempo, así que lo dejamos preparado para que acceda al método movimientoObjetos() cada segundo.

   1: #region Temporizador
   2:  
   3: Timer temporizador;
   4:  
   5: private void activaTemporizador()
   6: {
   7:     temporizador = new Timer();
   8:     temporizador.Tick += new EventHandler(temporizador_Tick);
   9:  
  10:     temporizador.Interval = 1000;
  11:     temporizador.Enabled = true;
  12: }
  13:  
  14: void temporizador_Tick(object sender, EventArgs e)
  15: {
  16:     movimientoObjetos();
  17: }
  18: #endregion

Ahora nos queda la parte divertida, es decir, cómo distribuir algún que otro objeto por la pantalla y que se vayan moviendo cada segundo, es decir, cada vez que sea llamado el método movimientoObjetos().

Como tenemos muy asumido que el diseño no es lo nuestro y que las coordenadas de la pantalla es una especie de película de ciencia-ficción, vamos a echar mano de un componente del namespace System.Windows.Forms, concretamente el TableLayoutPanel. Este componente tiene un método que nos solventará el problema de ir desplazando cualquier objeto. El método en sí es SetCellPosition, que permite, precisamente eso, colocar un objeto en una posición cualquiera. Lo único que deberemos mirar con anterioridad es conocer si ya existe algo en esa posición y podemos comprobarlo con otro método (GetControlFromPosition) del mismo control.

Así pues, crearemos dentro de nuestro formulario un control TableLayoutPanel que tenga un número determinado de filas y columnas y que estén distribuidos de una manera uniforme. En mi caso, he creado un ejemplo de tabla 10×10 y he añadido algún que otro ornamento (estos controles nos dan la ventaja que poder jugar con las propiedades RowSpan y ColumnSpan de los controles que añadimos en ellos, por si tenemos algún gráfico más grande).

image_thumb[12]

Por tanto, ya podemos hacer un bucle que se recorra todos los controles que hemos ido añadiendo, que aleatoriamente seleccione una celda, mire si está ocupada y, si no es así, que deje el elemento:

   1: /// <summary>
   2: /// Provoca el efecto de movimiento de los objetos en pantalla
   3: /// </summary>
   4: private void movimientoObjetos()
   5: {
   6:     int filas = tabla.RowCount;
   7:     int columnas = tabla.ColumnCount;
   8:  
   9:     Random r = new Random(System.DateTime.Now.Millisecond);
  10:     bool posicionado;
  11:  
  12:     lblReloj.Text = DateTime.Now.ToShortTimeString();
  13:  
  14:     foreach (Control ctr in tabla.Controls)
  15:     {
  16:         posicionado = false;
  17:  
  18:         while (!posicionado)
  19:         {
  20:             int posibleColumna = r.Next(columnas);
  21:             int posibleFila = r.Next(filas);
  22:  
  23:             if (tabla.GetControlFromPosition(posibleColumna, posibleFila) == null)
  24:             {
  25:                 tabla.SetCellPosition(ctr, new TableLayoutPanelCellPosition(posibleColumna, posibleFila));
  26:                 posicionado = true;
  27:             }
  28:         }
  29:     }
  30:     Application.DoEvents();
  31: }

 

Pues, en principio, ya tenemos el esqueleto de nuestro propio Protector de Pantalla. Sé que es sencillito tal cuál está, pero tiene la ventaja de poder personalizarse al gusto sin tocar casi nada de código. En la empresa, por ejemplo, añadí dos o tres logotipos que tienen por ahí del grupo y además añadí una frase aleatoria que introducen los mismos usuarios en un apartado de la intranet. Estas cosas a los jefes suelen gustarles.

¡Vale, muy bien!, pero, ¿qué pasa con el ratón?

Cierto, se me olvidaba un detalle con el que me peleé bastante rato. Si se te ocurre dirigir, desde el Form_Load un disparador al mover el ratón, te encontrarás que no llega a abrirse. Para ello, lo que hice fue lo siguiente: Guardar un punto en el que se almacena la posición del ratón, así se puede comparar con una nueva coordenada en caso de que se desplace.

   1: #region Control de ratón
   2:  
   3: private Point mouseXY;
   4: void tabla_MouseMove(object sender, MouseEventArgs e)
   5: {
   6:     Point puntoActual = new Point(e.X, e.Y);
   7:     if (!mouseXY.IsEmpty)
   8:     {
   9:         if (puntoActual != mouseXY)
  10:             CierraFormulario();
  11:  
  12:     }
  13:     mouseXY = puntoActual;
  14: }
  15: #endregion

 

Ya sólo nos queda compilarlo, cambiar la extensión de .EXE a .SCR y añadirlo a la carpeta %System%Windows para que aparezca entre el resto de protectores.

Dejo la aplicación por si alguien quiere entretenerse y modificarlo a su gusto: enlace

Generador de NIF/NIE

En el blog anterior, http://alskare.wordpress.com, un día se me ocurrió hacer una rutina que permitiese validar NIF, NIE y CIF . Sí, todo a la vez, así evitamos al usuario que tenga que introducir el tipo de documento. Por cierto, un día de estos tengo que ordenar el código, acabar de arreglarlo y dejarlo aquí. Bueno, el caso es que, desde que colgué ese post, uno de los mensajes que he recibido más a menudo hace referencia a ver si conocía algún lugar en el que existiese algún tipo de “generador” de NIFs para hacer pruebas.

Como me he comprometido a escribir un mínimo de un post quincenal, después de la broma de la presentación se me ha ocurrido que este podría ser un inicio como otro cualquiera, así que ahí queda.

La rutina, como puede comprobarse incluye tres métodos públicos que permiten diferenciar entre NIF y NIE (dependiendo de las pruebas se puede hacer uso de uno u otro). Además, he incluido un nuevo método que obtiene una lista de ambos tipos de documentos.

 

   1: using System;
   2: using System.Collections.Generic;
   3:  
   4: namespace JnSoftware.Generadores
   5: {
   6:  
   7:     /// <summary>
   8:     /// Generador de NIF/NIE al azar.
   9:     /// </summary>
  10:     public class NifNies
  11:     {
  12:  
  13:         Random semilla;
  14:         List<string> retVal;
  15:  
  16:         public NifNies()
  17:         {
  18:             semilla = new Random(DateTime.Now.Millisecond);
  19:         }
  20:  
  21:  
  22:         /// <summary>
  23:         /// Obtiene una lista de strings con <paramref name="numDocuments"/> NIFs aleatorios
  24:         /// </summary>
  25:         public List<string> GetNIFs(int numDocuments)
  26:         {
  27:             retVal = new List<string>(numDocuments);
  28:             for (int i = 0; i < numDocuments; i++)
  29:                 retVal.Add(getNif());
  30:             return retVal;
  31:         }
  32:  
  33:  
  34:         /// <summary>
  35:         /// Obtiene una lista de strings con <paramref name="numDocuments"/> NIFs aleatorios
  36:         /// </summary>
  37:         public List<string> GetNIEs(int numDocuments)
  38:         {
  39:             retVal = new List<string>(numDocuments);
  40:             for (int i = 0; i < numDocuments; i++)
  41:                 retVal.Add(getNie());
  42:             return retVal;
  43:         }
  44:  
  45:         
  46:         /// <summary>
  47:         /// Obtiene una lista de strings
  48:         /// </summary>
  49:         /// <param name="numNifs">Número de NIFs aleatorios que contendrá la lista</param>
  50:         /// <param name="numNies">Número de NIEs aleatorios que contendrá la lista</param>
  51:         public List<string> GetDocuments(int numNifs, int numNies)
  52:         {
  53:             // Es curioso. Primero he intentado:
  54:             // retVal = GetNIFs(numNifs);
  55:             // retVal.AddRange(GetNIEs(numNies));
  56:             // Pero sólo devuelve los Nies
  57:  
  58:             List<string> nies = GetNIEs(numNies);
  59:             retVal = GetNIFs(numNifs);
  60:             retVal.AddRange(nies);
  61:             return retVal;
  62:         }
  63:  
  64:  
  65:         /// <summary>
  66:         /// Obtiene un número de DNI
  67:         /// </summary>
  68:         /// <returns>
  69:         /// Un número de DNI consta de 7 dígitos + 1 carácter de control
  70:         /// </returns>
  71:         private string getNif()
  72:         {
  73:             int numDNI = semilla.Next(1000, 100000000);
  74:             return numDNI.ToString("00000000") + letraDNI(numDNI);
  75:         }
  76:  
  77:  
  78:         /// <summary>
  79:         /// Obtiene un número de NIE
  80:         /// </summary>
  81:         /// <returns>
  82:         /// Un NIE consta de un carácter (Y o X) + 6 dígitos + Carácter de control
  83:         /// </returns>
  84:         private string getNie()
  85:         {
  86:             int numDNI = semilla.Next(1000, 10000000);
  87:             return letraNie() + numDNI.ToString("0000000") + letraDNI(numDNI);
  88:         }
  89:  
  90:  
  91:         /// <summary>
  92:         /// Obtiene una letra aleatoria para el NIE
  93:         /// </summary>
  94:         private char letraNie()
  95:         {
  96:             string nie = "XY";
  97:             return nie[semilla.Next(0,2)];
  98:         }
  99:  
 100:  
 101:         /// <summary>
 102:         /// Cálculo de la letra del DNI/NIF
 103:         /// </summary>
 104:         private string letraDNI(int dni)
 105:         {
 106:             int indice = dni % 23;
 107:             return "TRWAGMYFPDXBNJZSQVHLCKET"[indice].ToString();
 108:         }
 109:     }
 110:  
 111: }

 

Como siempre, confío sea de utilidad. A mí me ha servidor bastante cuando he tenido que crear “datos de pruebas” en las bases de datos.

Post de presentación

using System;

namespace JnSoftware.Posts
{
    public class PrimerPost
    {

        /* Campos */
        private string responsable;
        private string mensaje;
        private bool gracias;
        private string blogAnterior;

        /// <summary>
        /// Obtiene o establece el valor que indica si está controlado el entorno
        /// </summary>
        /// <remarks>
        /// Entendamos por control del entorno la posibilidad de subir ficheros, imágenes
        /// y el formateo correcto del código
        /// </remarks>
        public bool EntornoControlado { get; set; }

        /// <summary>
        /// Constructor de la clase
        /// </summary>
        public PrimerPost()
        {
            gracias = true;
            blogAnterior = "http://alskare.wordpress.com";
            responsable = "Rodrigo Corral";
            mensaje = getMessage();
            EntornoControlado = false;
        }

        /// <summary>
        /// Creación del mensaje de presentación
        /// </summary>
        private string getMessage()
        {
            string m = "Antes de nada, me gustaría dar las gracias a {0} por la oportunidad " +
                        "que me ofrece con este nuevo blog.n" +
                        "Para aquellos que no me conozcan, todavía se puede consultar mi antiguo " +
                        "blog en {1}.n" +
                        "Confío estar a la altura de las circunstancias.n";
            return string.Format(m, responsable, blogAnterior);
        }

        /// <summary>
        /// Muestra el mensaje de inicio y agradecimiento.
        /// </summary>
        private void Presentacion()
        {
            if (gracias)
                Console.WriteLine(mensaje);
            if (EntornoControlado)
                Console.WriteLine("Manos a la obra");
        }

        /// <summary>
        /// Ejecución de la aplicación
        /// </summary>
        public static void Main()
        {
            PrimerPost post = new PrimerPost();

            // TODO Leer configuración para controlar subir imágenes y archivos.
            post.EntornoControlado = false; 

            post.Presentacion();
            Console.Write("Proceso finalizado...: ");
            Console.ReadKey(true);
        }
    }
}