Mostrando las entradas con la etiqueta Clipboard. Mostrar todas las entradas
Mostrando las entradas con la etiqueta Clipboard. Mostrar todas las entradas

martes, 21 de octubre de 2014

El Demo del Día: Capturar y Reproducir Pantallas en WinForms

Capturar y Reproducir Pantallas en WinForms

En este post veremos como crear una aplicación que permita lanzar cualquier aplicación o programa (archivo exe) y como capturamos la pantalla (en realidad las ventanas) de dicha aplicación hasta que esta finalice, ya sea en forma normal (por el cierre del usuario) o por una excepción No controlada por la aplicación.

Requerimiento

A veces no tenemos el código fuente de un programa que presenta problemas en su funcionamiento y no se sabe porque se cae, ya que No tiene Log de errores o una forma de saber porque se cerró.

Solución

Para resolver el problema vamos a construir una aplicación en Windows Forms que permita lanzar la aplicación o programa, luego capturar sus pantallas y finalmente mostrar la secuencia de pantallas como si fuera un video, aunque no lo sea (por el momento).

La técnica consiste en crear un objeto Pantalla con un par de propiedades: Fecha y Hora de Captura e Imagen a Capturar, luego lanzaremos la aplicación con Process.Start y habilitaremos un Timer que cada segundo verifique si el proceso esta en ejecución y si lo esta enviaremos la tecla capturar ventana y recuperaremos del Clipboard la imagen guardándola en el objeto y agregándola a una lista de objetos.

Si se salio de la aplicación analizada, se serializará en forma binaria y se guardará la lista de objetos en un archivo llamado "PrintScreen.dll" e inmediatamente se habilitará otro Timer que permite mostrar cada imagen de la lista de imagen.

Crear la Aplicación Windows en Windows Forms

Crear un Nuevo Proyecto "Aplicación de Windows Forms" en C# llamado: "CapturaPantalla" y agregar la clase "bePantalla" como sigue:

using System;
using System.Collections.Generic;
using System.Drawing;

namespace CapturaPantalla
{
    [Serializable]
    public class bePantalla
    {
        public DateTime FechaHora { get; set; }
        public Image Imagen { get; set; }
    }
}

Nota: Se usa el atributo Serializable sobre la clase bePantalla porque vamos a guardarlo en un archivo binario serializando.

Cambiar de nombre al formulario por: "frmCaptura" y arratrar los siguientes controles:
- Panel: pnlCaptura
- Label: lblTitulo con el Text="Selecciona el Programa a Capturar la Pantalla"
- TextBox: txtArchivo
- Button: btnIniciar con Text="Iniciar"
- Timer: tmrCaptura con Interval=1000
- Timer: tmrReproduce con Interval=1000

El diseño de la pantalla debe quedar como se muestra a continuación:


Escribir el siguiente código en el formulario "frmCaptura":

using System;
using System.Collections.Generic; //List
using System.Windows.Forms; //Form, SendKeys, Clipboard, etc
using System.Drawing; //Image, Bitmap, Size
using System.Diagnostics; //Process
using System.IO; //FileStream, FileMode, FileAccess, FileShare
using System.Runtime.Serialization.Formatters.Binary; //BinaryFormatter

namespace CapturaPantalla
{
    public partial class frmCaptura : Form
    {
        private Process proceso;
        private List<bePantalla> lbePantalla;
        private int c;

        public frmCaptura()
        {
            InitializeComponent();
        }

        private void mostrarDialogoAbrir(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Title = "Selecciona un archivos de programa";
            ofd.Filter = "Archivos de programa|*.exe";
            if (ofd.ShowDialog().Equals(DialogResult.OK))
            {
                txtArchivo.Text = ofd.FileName;
            }
        }

        private void iniciarCaptura(object sender, EventArgs e)
        {
            this.Visible = false;
            pnlCaptura.Visible = false;
            lbePantalla = new List<bePantalla>();
            proceso = Process.Start(txtArchivo.Text);
            tmrCaptura.Enabled = true;
            tmrCaptura.Start();
        }

        private void capturarPantalla(object sender, EventArgs e)
        {
            if (proceso != null && !proceso.HasExited)
            {
                SendKeys.SendWait("%{PRTSC}");
                Image img = Clipboard.GetImage();
                if (img != null)
                {
                    bePantalla obePantalla = new bePantalla();
                    obePantalla.FechaHora = DateTime.Now;
                    obePantalla.Imagen = img;
                    lbePantalla.Add(obePantalla);
                }
            }
            else
            {
                this.Visible = true;
                tmrCaptura.Stop();
                tmrCaptura.Enabled = false;
                using(FileStream fs=new FileStream("PrintScreen.dll",
                    FileMode.Create, FileAccess.Write,FileShare.Write))
                {
                    BinaryFormatter bf = new BinaryFormatter();
                    bf.Serialize(fs, lbePantalla);
                }
                tmrReproduce.Enabled = true;
                tmrReproduce.Start();
                c = 0;
            }
        }

        private void reproducirCaptura(object sender, EventArgs e)
        {
            if (lbePantalla.Count>0)
            {
                if (c < lbePantalla.Count)
                {
                    this.Text = String.Format("{0} de {1} - {2}",c+1,
                        lbePantalla.Count,lbePantalla[c].FechaHora);
                    this.BackgroundImage = new Bitmap(lbePantalla[c].Imagen,
                        new Size(this.ClientRectangle.Width,this.ClientRectangle.Height));
                    c++;
                }
                else c = 0;
            }
        }
    }
}

Nota: La asociación de funciones controladoras de eventos es la siguiente:
- mostrarDialogoAbrir: Evento Click del botón "btnAbrir"
- iniciarCaptura: Evento Click del botón "btnIniciar"
- capturarPantalla: Evento Tick del timer "tmrCaptura"
- reproducirCaptura: Evento Tick del timer "tmrReproduce"

Ejecutar y Probar la Aplicación Windows Forms

Grabar la aplicación, ejecutarla y se mostrará la siguiente ventana:


Seleccionar un archivo de programa a ejecutar y capturar sus pantallas, por ejemplo seleccione el archivo llamado: "AppCapturar.exe" que viene en la carpeta bin\debug del Demo y se mostrará la ventana con dicha aplicación.

Nota: En ese momento el panel con el archivo seleccionado se ha ocultado para después mostrar en toda la ventana o formulario las pantallas capturadas.

Esta aplicación permite ingresar los datos del Empleado: Código (string), Nombre (string) y Sueldo (decimal) en un objeto, pero No hay una validación para el tipo de datos.

Ingresar unos cuantos empleados, como se muestra en la figura:


Después de ingresar correctamente varios, probar ingresar textos en el sueldo y al grabar la aplicación se caerá e inmediatamente finalizará regresando a la aplicación anterior (CapturaPantalla).

Se creará un archivo llamado "PrintScreen.dll" con la lista de objetos (pantallas) serializadas y se mostrará el contenido de la lista en el mismo formulario como si fuese un video, tal como se muestra a continuación:


Nota: La imagen de la pantalla capturada se ha ajustado al tamaño del formulario o ventana.

Comentario

Si bien es cierto, existen muchos programas que capturan pantalla, lo graban en un video y lo reproducen, en este demo, hemos visto que es relativamente sencillo capturar y reproducir mediante poco código.

En nuestro caso hemos usado los métodos "SendKeys.SendWait" para capturar la ventana activa, "Clipboard.GetImage" para recuperar de la memoria la imagen y una Lista de Objetos para almacenar las pantallas y luego reproducirlas.

En un post futuro la secuencia de imágenes o pantallas capturadas la grabaremos en un formato de video, como un archivo AVI, MPEG o MP4. Inténtelo desde ahora y diviértanse en el intento.

Descarga:
Demo22_CapturaReproducePantalla

jueves, 18 de septiembre de 2014

El Demo del Día: Copiar Datos de Excel en el DataGridView de WinForms

Copiar Datos de Excel en el DataGridView de WinForms

Después de un tiempo, volvemos a publicar un nuevo Demo, esta vez a pedido de un alumno, que desea copiar que su aplicación tenga la funcionalidad de copiar datos de la Grilla (DataGridView) a Excel (esto es automático, no es necesario programar nada) y desde Excel hacia la aplicación (esto si requiere de programar usando el objeto Clipboard de WinForms).

Como siempre nuestro desarrollo lo haremos con Listas de Objetos (como debe ser para ahorrar memoria y no con DataSet), ademas usaremos Reflection para convertir los datos que vienen como cadenas desde Excel al tipo de datos adecuado del objeto.

Crear una Aplicación Windows Forms en C#

Crear un nuevo proyecto de tipo Windows Forms y llamarle "CopiarExcel". Cambiarle de nombre al formulario por el de "frmProducto".

Crear la clase: "beProducto" para la lista de objetos

using System;
namespace CopiarExcel
{
    public class beProducto
    {
        public int IdProducto { get; set; }
        public string Nombre { get; set; }
        public decimal Precio { get; set; }
        public short Stock { get; set; }
    }
}

Agregar un control DataGridView sobre el formulario, cambiarle al nombre de "dgvProducto" y acoplarlo para que se vea similar a la siguiente figura:


Escribir el siguiente código en el formulario:

using System.IO;
using System.Reflection;

namespace CopiarExcel
{
    public partial class frmProducto : Form
    {
        private List<beProducto> lbeProducto;

        public frmProducto()
        {
            InitializeComponent();
        }

        private void cargarProductos(object sender, EventArgs e)
        {
            lbeProducto = new List<beProducto>();
            Random oRandom = new Random();
            beProducto obeProducto;
            for(int i=1;i<=10;i++)
            {
                obeProducto = new beProducto();
                obeProducto.IdProducto = i;
                obeProducto.Nombre = String.Format("Producto {0}", i);
                obeProducto.Precio = oRandom.Next(100)+10;
                obeProducto.Stock = (short)(oRandom.Next(10) + 1);
                lbeProducto.Add(obeProducto);
            }
            dgvProducto.DataSource = lbeProducto;
        }

        private void pegarExcel(object sender, KeyEventArgs e)
        {
            if (e.Control && e.KeyCode == Keys.V)
            {
                string formato = DataFormats.CommaSeparatedValue;
                object contenido = Clipboard.GetData(formato);
                if (contenido != null && contenido is MemoryStream)
                {
                    byte[] buffer;
                    using (MemoryStream ms = (MemoryStream)contenido) buffer = ms.ToArray();
                    string lista = Encoding.UTF8.GetString(buffer).Replace("\r\n", ",");
                    string[] data = lista.Split(',');
                    PropertyInfo[] propiedades = lbeProducto[0].GetType().GetProperties();
                    if ((data.Length - 1) % propiedades.Length == 0)
                    {
                        beProducto obeProducto = null;
                        PropertyInfo propiedad;
                        int c = 0;
                        Type tipo = null;
                        while (c < data.Length - 1)
                        {
                            for (int i = 0; i < propiedades.Length; i++)
                            {
                                if (i == 0) obeProducto = new beProducto();
                                propiedad = obeProducto.GetType().GetProperty(propiedades[i].Name);
                                tipo = propiedad.PropertyType;
                                propiedad.SetValue(obeProducto, Convert.ChangeType(data[c], tipo));
                                if (i == propiedades.Length - 1) lbeProducto.Add(obeProducto);
                                c++;
                            }
                        }
                        dgvProducto.DataSource = null;
                        dgvProducto.DataSource = lbeProducto;
                    }
                    else MessageBox.Show
                    ("Numero de columnas a copiar del Excel y la Grilla deben ser iguales");
                }
                else MessageBox.Show("No hay un rango de celdas a copiar");
            }
        }
    }
}

Nota: Para copiar desde Excel a la grilla se debe pulsar la tecla Ctrl + V y la cantidad de columnas debe ser la misma que la de la grilla (en realidad la de las propiedades de la clase beProducto o la que fuera).

Probar la Aplicación Creada

Grabar el proyecto y ejecutarlo con F5. Se mostrará la siguiente ventana con los datos de los productos creados en el evento Load del formulario:


Crear un archivo de Excel con la misma estructura de los Productos, tal como se muestra a continuación:


Copiar (Ctrl + C) el rango de celdas que se desea llevar a la aplicación (sin incluir las cabeceras) y en cualquier celda de la grilla Pegar (Ctrl + V), y se copiaran los registros seleccionados en Excel, tal como se muestra a continuación:


Si desean se puede ir copiando por partes (filas) pero si es necesario seleccionar todas las columnas, ya que el objeto necesita de valores.

Nota: El procedimiento contrario, es decir desde la Aplicación (DataGridView) a Excel es automatico, solo hay que marcar el rango de celdas del DataGridView, Ctrl + C y luego en Excel Ctrl + V.

Comentario Final

En este post hemos visto como agregar la funcionalidad de Copiar Datos Externos a la aplicación, en este caso desde Excel, pero el procedimiento sera similar desde cualquier origen. Para ello hemos usado el objeto ClipBoard y para trabajar con objetos hemos usado Reflection, lo que quiere decir que el demo lo puedes probar con cualquier objeto que definan.

Saludos a todos los visitantes asiduos del Blog y ya saben que pueden hacer sus pedidos en los comentarios.

Descarga: