PRE-Requisitos en instalaciones con ClickOnce

Para distribuir un desarrollo con dependencias comunes, tipo .NET Framework, Crystal Reports, SQL SERVER Express, etc…, no necesitamos crearnos una instalación compleja con InstallShield. Podemos hacer uso de ClickOnce, que nos permite publicar y mantener las actualizaciones de forma muy sencilla.

ClickOnce001

En mi caso tenía una pequeña solución escrita en C# que utilizaba Crystal Report y SQL Server LocalBD. En las propiedades del propio proyecto C# disponemos de la opción de publicación con ClickOnce. Hasta ahí todo perfecto, pero a la hora de configurar los PRE-Requesititos de mi solución no encontraba en el menú la opción de Crystal. Yo tenía un windows 7 y actualice a windows 8 y después a windows 8.1. ClickOnce utiliza una carpeta de sistema para los re-requisitos, entiendo que será todos los componentes instalados para el desarrollo y algunos que trae por defecto windows. El caso es que todos esos datos están en “C:\Program Files (x86)\Microsoft SDKs\Windows\v8.1A\Bootstrapper\Packages” menos el de Crystal que estaba en “C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bootstrapper\Packages”. Solución evidente, copiamos la carpeta “Crystal Reports for .NET Framework 4.0” de …\v7.0A\.. a …\v8.1A\… y ya podremos seleccionar Crystal como pre-requisito.

ClickOnce002

Pero se nos plantea otro problema. Hay pre-requisitos que dependen de otros y sin embargo se lanzabn antes que los que necesitan. Así que descubrí que se pueden editar unos ficheros incluidos dentro de cada carpeta de pre-requisito llamado Product.xml. En ese fichero podemos decirle el requisito previo a instalar. Por ejemplo, Crystal Report le tuve que decir que instalase previamente .NET 4.

<RelatedProducts>
<DependsOnProduct Code=”.NETFramework,Version=v4.0″ />
<DependsOnProduct Code=”Microsoft.Net.Framework.2.0″ />
<DependsOnProduct Code=”Microsoft.Data.Access.Components.2.8″ />
</RelatedProducts>

 

Crear y consumir Eventos en C#

Aunque parece un tema fácil y de novatos, los que venimos acostumbrados a muchos años de VB y VB.net, no lo tenemos tan claro. Tras revisar documentación por la RED te encuentras multitud de hilos en los que te explican, cada uno de su forma y manera, como crear un evento y consumirlo desde otra clase.  La mayoría de ellos lo hacen de forma intachable siguiendo las “normas Microsoft”, pero yo, cogiendo de aquí y de allí, os lo voy a explicar de forma sencilla y menos liosa.

En la clase que publica el evento hay que crear un delegado y un evento del tipo del delegado creado. Cada vez que se quiera hacer la invocación al evento basta con hacer la llamada al evento pasándole los argumentos creados en el delegado.

//Creamos el delegado
public delegate void EstadoProcesoDelegate(string Estado);

//Creamos el evento para llamamos al delegados
public event EstadoProcesoDelegate EstadoProceso;

//invocamos al evento
EstadoProceso("Cadena de texto a pasar");

En la clase en la que queremos consumir los eventos simplemente tendremos que crear una instancia de dicha clase y asignar a cada evento que hayamos creado en la clase un método con la misma estructura que el delegado creado.


cClaseConEventos oEvento = new cClaseConEventos();      
//Le asignamos al evento de la clase un metodo con la misma estructura que el delegado      
oEvento.ContadorProceso += oTraspaso_ContadorProceso;

//Creamos un método donde utilizaremos el valor que nos devuelve el evento cada vez 
//que se invoque en la clase en la que lo lanzamos
void oTraspaso_EstadoProceso(string Estado)
{            
     lblProcesoMSJ.Text = Estado;
     lblProcesoMSJ.Refresh();
     this.Refresh();
}

Método de extensión para clonar clase C#

En ocasiones necesitamos hacer una copia de una instancia, pero no queremos que nos arrastre los punteros a terceras, ya que queremos modificarlas sin que se vean alterados los valores del origen de la copia. La solución me la dio mi compañero Sergio Montes.

En principio montamos el método de extensión.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.Serialization;

namespace DeepClone
{
    public static class ExtensionMethods
    {
        public static T CloneCompleto<T>(this T origen)
        {
            using (var memStream = new MemoryStream())
            {
                var formateadorBinario = new BinaryFormatter();
                formateadorBinario.Serialize(memStream, origen);
                memStream.Seek(0, SeekOrigin.Begin);
                return (T)formateadorBinario.Deserialize(memStream);
            }
        }
    }
}

El método es sencillo de comprender, simplemente se serializa en memoria y se desserializa en una clase nueva, por lo que el resultado es una copia exacta sin enlaces de punteros.
Gracias a los tipos genéricos, T, podremos llamar a este método desde cualquier clase. El siguiente paso es añadir la propiedad [Serializable] en la clase que queremos clonar.

    [Serializable]
    public class Prueba
    {
        public int Codigo { get; set; }
        public int Serie { get; set; }
        public double Base { get; set; }
        public double ImporteIVA { get; set; }
        public List<Prueba> ListaClasesHijos { get; set; }

        public Prueba Clone()
        {
            return this.CloneCompleto<Prueba>();
        }

    }

Como veis en el ejemplo simple tenemos una clase que se clona a sí misma.

Puertos con C#. Envio y Recepción de Datos.

Aprenderemos a abrir el puerto, enviar datos y recibirlos. La recepción de datos puede ser de forma síncrona o asíncrona. Yo voy a enfocar todo el artículo a la forma de recepción síncrona, ya que necesito leer la trama y dejar de leer cuando este todo recepcionado, así que tendremos que ir analizando cada byte que recibimos.

En primer lugar abrimos el puerto:

    public void AbrirPuertoSerie(string Puerto,int Velocidad)
        {

            try
            {

                if (_PuertoSerie.IsOpen)
                    _PuertoSerie.Close();
                _PuertoSerie.PortName = Puerto;
                _PuertoSerie.BaudRate = Velocidad;

                _PuertoSerie.Parity = System.IO.Ports.Parity.None;
                _PuertoSerie.DataBits = 8;
                _PuertoSerie.StopBits = System.IO.Ports.StopBits.One;
                _PuertoSerie.Handshake = System.IO.Ports.Handshake.None;

                _PuertoSerie.RtsEnable = false;
                _PuertoSerie.DtrEnable = true;

                //_PuertoSerie.DataReceived += new SerialDataReceivedEventHandler(com_DataReceived);
                //_PuertoSerie.ErrorReceived += new SerialErrorReceivedEventHandler(com_ErrorReceived);

                _PuertoSerie.Open();

            }
            catch (ArgumentOutOfRangeException Ex)
            {
                _Errores.add("AbrirPuertoSerie. " + Ex.Message);
            }
            catch (InvalidOperationException Ex)
            {
                _Errores.add("AbrirPuertoSerie. " + Ex.Message);
            }
            catch (ArgumentException Ex)
            {
                _Errores.add("AbrirPuertoSerie. " + Ex.Message);
            }
            catch (IOException Ex)
            {
                _Errores.add("AbrirPuertoSerie. " + Ex.Message);
            }
            catch (Exception ex)
            {
                _Errores.add("AbrirPuertoSerie. " + ex.Message);
            }

        }

Una vez abierto ya podremos hacer el envío de los datos.

Los datos, la trama, la construiremos con un List<byte> que es lo más cómodo de utilizar y el envío se produce sin errores. He tenido muchos problemas al enviar los datos como char, inexplicable.
Sigue leyendo

Puertos con C#. Tramas y CRC.

Cuando necesitamos conectar con un dispositivo externo a través del puerto COM empiezan las peleas. Según el fabricante del dispositivo con el que queremos comunicarnos necesitamos construir una trama u otra. Cada una de las tramas con las que me he encontrado son de su padre y de su madre, algunas tramas exigen LSB(byte menos significativo)  y MSB(byte más significativo), otras CheckSum o CRC.  Las que piden  CRC suelen tener un algoritmo específico, CRC – 16,  CRC-CCITT, etc…

Otra cosa curiosa es el tema de la transparencia, método por el cual cuando te encuentras un carácter reservado dentro de la trama si va acompañado por un byte de escape hace que no utilicemos dicho carácter reservado como carácter especial, Inicio de datos, Fin de campo, etc…, sino como un carácter normal dentro de la trama, por ejemplo parte de una frase.

Pero gracias a la gente que publica su sapiencia en la red al final encuentras lo necesario para construirlas. El motivo de este artículo es intentar hacer un resumen para que aquel que se encuentre con las situaciones que yo me he encontrado no tenga que leer 35 post distintos, además de servirme a mí para futuras consultas.

Construir tramas

Según las especificaciones tendremos que construir una cosa u otra. Normalmente las tramas vienen definidas por un inicio y un fin, el inicio es llamado STX, 2 HEX, y el final de la trama un ETX,  3 HEX. Nosotros vamos a especificar una de ejemplo:

STX LBS MBS FLD Campo01 FLD CampoN CHK ETX

Sigue leyendo