August 2011 - Artículos
Me he encontrado con un problema en SQL Server, concretamente y para ser concretos en SSMS (SQL Server Management Studio) que quiero compartir por si a alguno le pasa para que sepa como lo he resuelto yo.
Son de esas cosas que aunque uno tiene presente, a veces olvida. El caso es que siempre aparecen cuando menos las deseas, pero es lo que tiene que en este mundo imperfecto haga siempre su aparición el señor Murphy.
El caso es que en mi equipo de 64 bits tengo una instancia de SQL Server 2008 y otra de SQL Server 2008 R2.
En el equipo con el que me he encontrado el problema tenía además de estas dos instancias instaladas, el SSMS de 2005 (cosas de la vida).
Al abrir con el SSMS 2005 cualquiera de las dos instancias de SQL Server 2008, estas se abrían perfectamente.
Si quería crear una base de datos,... perfecto.
Si quería crear una tabla con un script en la base de datos,... perfecto.
Ahora bien, para crear una tabla con el típico botón derecho, crear tabla... etc., tururú.
El error que muestra SSMS 2005 es:
Unspecified error (MS Visual Database Tools)
Error no especificado (MS Visual Database Tools)
Bien bien, parece ser que no le mola SSMS 2005 (algo que puede entenderse y aceptarse como reglas universales de juego cuando trabajamos con diferentes versiones de SQL Server), así que descargo SSMS 2008 64 bits y lo instalo, y así todo arreglado... o eso debería ser.
Descargo SSMS 2008 64 bits (176 Mb), lo instalo y... ¡zás!, error en el último paso de chequeo del sistema justo antes de empezar la instalación.
Ahora me dice "nosequé" de las Tools de SQL Server 2005 (Sql2005SsmsExpressFacet - La característica SQL Server 2005 Express Tools está instalada. Para continuar, quítela)... pero... ¡si sólo tengo dos instancias y las dos de SQL Server 2008!.
Pues eso, que el problema (y la solución) está en el SSMS de 2005 que tenemos instalado y en concreto en el registro de Windows, ya que al paquete de instalación de SSMS 2008 no le gusta "ver esas cosas".
Al ser un entorno de 64 bits, deberemos ir a la ruta:
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Microsoft SQL Server\90\Tools\
Aquí localizaremos la etiqueta ShellSEM.
Basta con renombrar la etiqueta a lo que queramos (por ejemplo ShellSEM_OLD).
Reintentaremos la instalación de SSMS 2008 y esta vez no nos dará ningún error ni problema. Iniciaremos por lo tanto el proceso de instalación y adelante.
Bastará entonces con volver a renombrar ShellSEM_OLD por ShellSEM y así podremos seguir utilizando SSMS 2005 si quisiéramos, ya que de otra forma no se arrancará.
Espero que este problema y solución le pueda ayudar a más de uno.
Saludos.
Introducción:
A estas alturas creo que no descubro nada si indico que para mostrar un gif animado en una aplicación Windows por ejemplo, basta con agregar el gif animado al control Image.
De esta manera, el gif animado se mostrará en toda su "expresión".
Ahora bien,... imaginemos que lo que queremos es extraer el contenido (los frames) de un gif animado.
¿Cómo lo haríamos?.
Eso es lo que vamos a ver en esta entrada, pero antes, un poco de chicha (si te aburro, puedes ir directamente al código :-D ).
Conociendo un poco más un gif animado:
Un gif animado no es otra cosa que un repositorio compuesto por un conjunto de imágenes gif en orden secuencial.
El formato Gif tiene su origen en el año 1987 (ver referencias para concocer más acerca de Gif), y la bondad de los gifs animados es que gracias a ellos podíamos utilizar en Internet lo que se denominarían banners, término que aún hoy se usa. El formato gif animado sufrió diferentes modificaciones, de hecho se conocen dos formatos, 87A y 89A. El segundo acepta además transparencias.
Lo más interesante de un gif animado es que además de poder contener una secuencia de imágenes gif en un orden determinado, podíamos indicar una frecuencia, delay y otras propiedades propias para cada frame, de tal modo que si queríamos que un frame de la secuencia durara 2 segundos por ejemplo, lo podíamos realizar sin complicaciones.
No me quiero extender más sobre los gifs animados, para eso hay mucha información en Internet.
En referencias os indico un enlace con información adicional.
Posteriormente surgiría el formato png que tenía como objetivo cubrir dos aspectos inherentes a los gifs y que desquiciaban a los diseñadores y empresas.
Por un lado, utilizar un algoritmo de compresión sin pérdida aparente (el algoritmo LZW de gif sí tenía este problema de pérdida de calidad, además de otras limitaciones), y por otro lado, evitar los problemas de patentes de Gif por el uso de LZW (algoritmo de compresión patentado por Unisys). Al ver que las limitaciones de gif eran superadas ampliamente por png, la carrera que se inició en 1995 por adoptar este formato ha sido fulgurante.
No obstante, esto no quita que los gifs animados se hayan dejado de utilizar.
Uso de gifs animados en VB 32 bits:
Recordando tiempos pasados... algo de nostalgia.
¿Cómo usábamos gifs animados en VB?.
Me acuerdo cuando delante de Visual Basic 4 32 bits, Visual Basic 5.0 y Visual Basic 6.0 (no de .NET), teníamos en nuestras empresas la necesidad de agregar gifs animados a nuestras aplicaciones.
Por aquel entonces tenía yo mi web en Internet sobre VB y tuve la oportunidad de conocer a una persona que juntos planteamos el problema de los gifs animados y como resolver este problema junto con la problemática de las licencias.
Como en este mundo el miedo es muy nuestro y teníamos auténtico pavor al tema de quebrar una licencia y desconocíamos si podíamos transgredir ese aspecto o no, me pude hacer con un código en C++ de lectura de gif y la estructura del formato de un gif animado (de esto hace ya un montón de años).
Al principio intenté hacer todo en VB, pero no dí con ello, y mis conocimientos de C++ siempre han sido limitados. Con C++ apenas he hecho una aplicación de Windows 3.1, algún pequeño control VBX y OCX, y alguna pequeñísima cosa más, pero poco más... y desde entonces C++ lo he tenido abandonado.
El caso es que pese a esta pequeña frustación, pude contactar con una persona que conocía más que yo C++ y que tenía la misma necesidad que un servidor y que mucha gente en Internet (poder leer un gif animado desde una aplicación de VB), así que a esa persona le pasé todo lo que tenía yo hecho sobre este tema, y concluyó mejor que yo lo que quería abordar.
Creó un control OCX que se hizo muy famoso y cuyo nombre era Marchoso.ocx (seguro que a más de uno le suena).
Ese control lo puse en mis páginas Web (Mundo Visual - Visual Basic inicialmente, y PortalVB.com posteriormente) y corrió como la pólvora por Internet.
Aún hoy día es fácil conseguirlo.
Creo que nunca ha sabido nadie cómo apareció este control ni de donde... y es que el miedo al tema de licenciamiento como decía anteriormente era muy grande.
Han pasado bastante años desde aquello, así que porqué no desvelar ahora esta pequeña historia. Así, alguno que se preguntara de donde venía Marchoso.ocx puede conocer algo más de la aparición del primer control OCX de VB para la lectura de gifs animados. Su creador salvo que quiera hacerlo público no lo revelaré yo, seguirá en el anonimato mientras quiera. :-)
Conociendo un poco más a Image:
Para poder obtener fácilmente los frames que forman parte de un gif animado, debemos hacer uso del control Image.
El namespace System.Drawing será el utilizado para trabajar con el control Image, sin embargo, también haremos uso de otro namespace, System.Drawing.Imaging.
Y dentro de System.Drawing.Imaging, utilizaremos la clase FrameDimension.
FrameDimension es una clase que no se puede heredar, pero que nos va a permitir interactuar con el control Image para obtener los diferentes frames que constituyen la imagen gif animada.
Gracias a estos dos namespaces, estaremos en disposición de extraer todos y cada uno de los frames que forman parte de un gif animado.
El código:
El siguiente ejemplo muestra de forma práctica como trabajar con el control Image y System.Drawing.Imaging.FrameDimension, y extraer las imágenes o frames que forman parte de un gif animado.
La aplicación de Windows contiene tres controles button y un control pictureBox.
El código de demostración de lo que hemos comentado es el que se indica a continuación:
1: using System;
2: using System.Collections.Generic;
3: using System.Drawing;
4: using System.Windows.Forms;
5:
6: namespace AnimatedGif
7: {
8:
9: public partial class MainForm : Form
10: {
11:
12: public MainForm()
13: {
14: InitializeComponent();
15: } // MainForm
16:
17:
18: private void MainForm_Load(object sender, EventArgs e)
19: {
20: // Indicamos la ruta y la imagen animada.
21: this.Path = @"images\st_patricks_day.gif";
22: } // MainForm_Load
23:
24: /// <summary>
25: /// Propiedad con la posición máxima (frames) de la imagen.
26: /// </summary>
27: public int MaximunPosition { get; set; }
28:
29: /// <summary>
30: /// Propiedad con la posición de la imagen actual.
31: /// </summary>
32: public int Position { get; set; }
33:
34: /// <summary>
35: /// Ruta en la que se encuentra la imagen gif animada con la que
36: /// trabajaremos.
37: /// </summary>
38: private string Path { get; set; }
39:
40: /// <summary>
41: /// Colección de imágenes que corresponde con los frames del gif
42: /// animado.
43: /// </summary>
44: private List<Image> GifImagesCollection { get; set; }
45:
46: private void btnProcess_Click(object sender, EventArgs e)
47: {
48: // Indicamos la posición inicial (Frame 1).
49: this.Position = 0;
50: // Inicializamos la colección de imágenes o frames de un Gif animado.
51: this.GifImagesCollection = new List<Image>();
52: // Recuperamos y cargamos en memoria la imagen animada.
53: Image image = Image.FromFile(this.Path);
54: // Obtenemos las dimensiones o frames de la imagen.
55: System.Drawing.Imaging.FrameDimension frameDimension = new System.Drawing.Imaging.FrameDimension(image.FrameDimensionsList[0]);
56: int frames = image.GetFrameCount(frameDimension);
57: // Recorremos los frames e insertamos en la colección cada una
58: // de las imágenes.
59: for (int i = 0; i < frames; i++)
60: {
61: // Activamos el frame concreto.
62: image.SelectActiveFrame(frameDimension, i);
63: // Agregamos la imagen a la colección de imágenes.
64: this.GifImagesCollection.Add(new Bitmap(image));
65: }
66: // Indicamos el número máximo de frames.
67: this.MaximunPosition = frames;
68: // Mostramos la primera imagen.
69: ShowImage();
70: } // btnProcess_Click
71:
72: private void btnBack_Click(object sender, EventArgs e)
73: {
74: // Cambiamos la posición de la imagen.
75: this.Position -= 1;
76: // Mostramos la imagen que le corresponde.
77: ShowImage();
78: } // btnBack_Click
79:
80: private void btnNext_Click(object sender, EventArgs e)
81: {
82: // Cambiamos la posición de la imagen.
83: this.Position += 1;
84: // Mostramos la imagen que le corresponde.
85: ShowImage();
86: } // btnNext_Click
87:
88: private void ShowImage()
89: {
90: // Cambiamos la imagen por la que le corresponde.
91: this.pbImage.Image = this.GifImagesCollection[this.Position];
92: // Habilitamos y deshabilitamos los controles para mostrar
93: // la imagen siguiente o anterior.
94: this.btnBack.Enabled = (this.Position == 0 ? false : true);
95: this.btnNext.Enabled = (this.Position == this.MaximunPosition - 1 ? false : true);
96: } // ShowImage
97:
98: } // MainForm
99:
100: } // AnimatedGif
Referencias:
Gif (Graphics Interchange Format).
Gif, Png, problemáticas y soluciones.
Png (Portable Network Graphics).
Control marchoso.ocx para VB (no .NET).
Información sobre FrameDimension (System.Drawing.Imaging).
Introducción:
Siguiendo la línea de mi última entrada en la que hablaba de alguna de esas novedades de .NET Framework 4.0 que quizás hayan pasado desapercibidas, voy a hablar en esta ocasión de Is64BitOperatingSystem y Is64BitProcess, dos propiedades muy simples en comprensión y uso que pertenecen ambas a la clase Environment.
Clase Environment:
La clase Environment que puede ser localizada en el namespace System y concretamente en la librería mscorlib.dll, ha sido ampliada en .NET Framework 4.0 con dos nuevas propiedades, Is64BitOperatingSystem y Is64BitProcess.
Is64BitOperatingSystem y Is64BitProcess:
Estas dos propiedades estáticas se utilizan como no puede ser de otro forma, de manera directa.
La misión de Is64BitOperatingSystem es saber si el sistema operativo sobre el que se está ejecutando la aplicación es de 64 bits o no.
Por lo tanto, devolverá un valor System.Boolean a true si es un sistema operativo de 64 bits y false en caso contrario.
Su llamada es del tipo: Environment.Is64BitOperatingSystem.
La misión de Is64BitProcess por su parte, es saber si el proceso que se está ejecutando es de 64 bits o no.
Al igual que con Is64BitOperatingSystem, devolverá un valor System.Boolean a true si es un proceso de 64 bits y false en caso contrario.
Su llamada es del tipo: Environment.Is64BitProcess.
Como podemos apreciar, estas dos propiedades no representan complejidad, y su explicación de uso y funcionamiento es realmente simple.
Introducción:
Hoy voy a hablar de un namespace que fué introducido en .NET Framework 4.0 y del cual no he oído hablar mucho, me refiero a System.Collections.Concurrent.
Antes de hablar de este namespace imaginemos la siguiente situación:
Tenemos una caja dentro de la cual vamos colocando diferentes elementos uno detrás de otro, si bien, el orden en el que llegan esos elementos no es en este caso lo más importante para nosotros.
Ahora imaginemos varios procesos concurrentes accediendo a esa caja para obtener un elemento (y sólo uno), sacarlo de la caja y trabajar y operar con él.
Cuando uno de esos procesos finaliza, acudiría nuevamente a la caja en busca de más elementos obteniendo nuevamente un único elemento, realizando la misma operación (obtención del elemento y trabajo con él).
System.Collections.Concurrent:
Antes de que Microsoft introdujera en .NET Framework 4.0 las colecciones de las que voy a hablar y las cuales resolverían este problema, yo me hice mis propias clases (anti-colisión) que utilizaba a modo de listener una colección genérica que permitía consultar sus elementos y extraerlos de la colección, todo esto desde varios procesos o hebras concurrentes.
Aunque mis clases funcionan aún hoy perfectamente, no es menos cierto que si tenemos esto ya en .NET Framework, ¿para qué complicarse la vida?.
Así que con esta idea quiero hacer mención a System.Collections.Concurrent en esta entrada, para que tengamos en cuenta el uso de este namespace y su posible reutilización.
Hablando de System.Collections.Concurrent, diremos que contiene un conjunto de interesantes clases que nos permitirán implementar diferentes tipos de colecciones de una forma segura evitando colisiones.
Entre las clases que encontraremos en System.Collections.Concurrent haré una mención especial a la clase ConcurrentBag<T> que como veréis es genérica e implementa esta funcionalidad de forma rápida y sencilla.
No obstante, ConcurrentBag<T> tiene varias particularidades.
se trata de una clase en modo LIFO.
Si queremos trabajar con datos en modo FIFO, deberemos pensar en trabajar con ConcurrentQueue<T>, que en mi caso coincide más con las clases que me construí antes de .NET Framework 4.0 para trabajar con colas de mensaje y de datos concurrentemente.
Otra clase interesante es ConcurrectStack<T> que permite trabajar con los datos en modo LIFO también, si bien en este caso nos permite obtener no un elemento por llamada, sino incluso más en caso de ser necesario.
Pero dentro de System.Collections.Concurrent también podemos encontrar una clase algo más especial.
BlockingCollection<T> nos permite introducir características de bloqueo para nuestras colecciones.
Sin embargo, y pese a que el uso de todas las colecciones es muy similar y sencillo, vamos a fijarnos en ConcurrentBag<T> para mostrar su funcionamiento y entenderlo bien (es muy sencillo la verdad).
La demostración de ConcurrentBag<T>:
Para mostrar el uso de esta clase, he decidido crear un sencillo ejemplo de código en C# que realiza las siguientes tareas:
- Creamos una clase Person con una propiedad (Name).
- Cargamos una colección de objetos Person (25 en concreto).
- Cada objeto Person formará parte de una colección de tipo ConcurrentBag<Person>.
- Creamos 3 controles Timer con un intervalo de 500 ms, 750 ms y 1000 ms que se lanzarán cuando le toque a cada uno.
- Cada timer obtendrá un elemento de ConcurrentBag y mostrará su contenido en un control textBox.
Nuestra aplicación de demostración en tiempo de ejecución es la que se indica en la siguiente imagen:

El objetivo de este ejemplo es únicamente demostrar el funcionamiento de ConcurrentBag<T>, por lo que obviado otras particularidades que tendrían que tenerse en cuenta en entornos empresariales y de producción.
Como podremos observar, agregar un elemento a la colección se realiza con el método Add.
Mientras que obtener un elemento de la colección se realiza con el método TryTake. TryTake tiene la particularidad de que además de obtener un elemento de la lista o colección de elementos, lo elimina de la misma.
Si quisiéramos obtener un elemento de la colección pero no eliminarlo, deberíamos utilizar TryPeek.
Ahora bien, en el caso del ejemplo TryPeek haría que el lanzamiento de los tres eventos timer siempre obtuviera el último elemento, algo que en esta demostración no es lo buscado.
Volviendo a TryTake, lo bueno de esta función es que nos devuelve true en caso de tener datos y false en caso contrario.
No obstante, en este ejemplo obtengo directamente el elemento de tipo Person.
He reducido el ejemplo a la mínima expresión y he asumido que no hay elementos de tipo null en la colección, así que si el elemento devuelto por TryTake es null es que no hay más elementos en la colección.
Pero recordad que lo ideal es preguntar por su valor (true/false) para saber si hay más elementos en la colección.
También tenemos no obstante la propiedad Count que siempre es una buena aliada, y con esto doy la pista también para resolver el posible problema de un elemento de la colección a null (todo esto desde el punto de vista del ejemplo de demostración... el código en sí aclarará más esto que comento).
Y ahora el código de esta pequeña demostración del uso de System.Collections.Concurrent.ConcurrentBag<T>.
Código fuente:
Person.cs
1: namespace ConcurrentBagSample
2: {
3:
4: public class Person
5: {
6:
7: /// <summary>
8: /// Inicializa una nueva instancia de la clase <see cref="T:Person"/>.
9: /// </summary>
10: /// <remarks>
11: /// Constructor de la clase Person.
12: /// </remarks>
13: public Person(string name)
14: {
15: this.Name = name;
16: } // Person Constructor
17:
18: public string Name { get; set; }
19:
20: } // Person
21:
22: } // ConcurrentBagSample
MainForm.cs
1: using System;
2: using System.Collections.Generic;
3: using System.Text;
4: using System.Windows.Forms;
5:
6: namespace ConcurrentBagSample
7: {
8:
9: public partial class MainForm : Form
10: {
11:
12: public MainForm()
13: {
14: InitializeComponent();
15: } // MainForm
16:
17:
18: private void MainForm_Load(object sender, EventArgs e)
19: {
20: // Indicamos el número de elementos que cargaremos.
21: this.NumberOfElements = 25;
22: // Indicamos en el control de progreso el valor máximo que
23: // corresponde con el de los elementos.
24: this.progressBar1.Maximum = this.NumberOfElements;
25: this.progressBar1.Value = 0;
26: // Inicializamos la colección de elementos concurrentes.
27: this.PersonConcurrentCollection = new System.Collections.Concurrent.ConcurrentBag<Person>();
28: // Cargamos los datos con los que trabajaremos.
29: LoadData();
30: } // MainForm_Load
31:
32:
33: public System.Collections.Concurrent.ConcurrentBag<Person> PersonConcurrentCollection { get; set; }
34:
35: public int NumberOfElements { get; set; }
36:
37: private void LoadData()
38: {
39: // Cargamos un conjunto de datos a modo de simulación.
40: // Estos datos nos permitirán comprender mejor el funcionamiento
41: // de ConcurrentBag.
42: for (int i = 0; i < this.NumberOfElements; i++)
43: {
44: PersonConcurrentCollection.Add(new Person(string.Format("Person {0}", i)));
45: }
46: } // LoadData
47:
48:
49: private void button1_Click(object sender, EventArgs e)
50: {
51: // Mostramos el número de elementos de la colección...
52: this.label4.Text = string.Format("Initial elements: ({0})", this.PersonConcurrentCollection.Count.ToString());
53: // Comenzamos a recuperar datos...
54: this.timer1.Enabled = true;
55: this.timer2.Enabled = true;
56: this.timer3.Enabled = true;
57: } // button1_Click
58:
59:
60: private void timer1_Tick(object sender, EventArgs e)
61: {
62: Person person = null;
63: GetData(ref person);
64: if (person != null)
65: {
66: this.textBox1.Text += person.Name + Environment.NewLine;
67: }
68: else
69: {
70: this.timer1.Enabled = false;
71: }
72: // Mostramos el avance del proceso.
73: ShowAdvance();
74: } // timer1_Tick
75:
76:
77: private void timer2_Tick(object sender, EventArgs e)
78: {
79: Person person = null;
80: GetData(ref person);
81: if (person != null)
82: {
83: this.textBox2.Text += person.Name + Environment.NewLine;
84: }
85: else
86: {
87: this.timer2.Enabled = false;
88: }
89: // Mostramos el avance del proceso.
90: ShowAdvance();
91: } // timer2_Tick
92:
93:
94: private void timer3_Tick(object sender, EventArgs e)
95: {
96: Person person = null;
97: GetData(ref person);
98: if (person != null)
99: {
100: this.textBox3.Text += person.Name + Environment.NewLine;
101: }
102: else
103: {
104: this.timer3.Enabled = false;
105: }
106: // Mostramos el avance del proceso.
107: ShowAdvance();
108: } // timer3_Tick
109:
110:
111: private void GetData(ref Person person)
112: {
113: // Obtenemos un elemento de la colección, eliminándola de
114: // la colección en sí.
115: this.PersonConcurrentCollection.TryTake(out person);
116: } // GetData
117:
118:
119: private void ShowAdvance()
120: {
121: // Indicamos el avance en la barra de progreso.
122: this.progressBar1.Value = this.NumberOfElements - this.PersonConcurrentCollection.Count;
123: // Mostramos el número de elementos de la colección...
124: this.label5.Text = string.Format("Elements to read: ({0})", this.PersonConcurrentCollection.Count.ToString());
125: } // ShowAdvance
126:
127: } // MainForm
128:
129: } // ConcurrentBagSample
Espero que le sirva a más de uno y que le haga comprender la utilidad de lo que contiene System.Collections.Concurrent.
Referencias:
.NET Framework 4.0 :: System.Collections.Concurrent