Error: The command copy exited with code 1…

Problema:

Al compilar una librería de clases o ejecutable, aparece un error del tipo “The command copy … exited with code 1”.

Solución:

Es un error que surge debido a que la librería o ejecutable ejecuta algún comando en los eventos Pre-Build o Post-Build (accesible desde las propiedades del proyecto actual), que en este caso es un copy… Deberíamos estar verificando la correcta sintaxis de los comandos que se ejecutan.

Configuración Pre-Build y Post-Build

Configuración Pre-Build y Post-Build

En mi caso me surgió este error debido a que una de las carpetas a la cual copiaba la dll no existía.

Publicado en Errores. Etiquetas: . 1 Comment »

Error: Unhandled exception en WinForms…

Como desarrolladores una de las cosas que debemos tener siempre en cuenta es la posibilidad de que una instrucción ocasionalmente produzca una excepción. Para capturar excepciones y manipularlas, lo hacemos con la ayuda de nuestros queridísimos try{}catch{}finally{}.

Sin embargo hay veces en que se nos pueden pasar de largo y terminar en lo que se denomina una excepción no manejada o unhandled exception. En esos casos, el resultado es un hermoso mensaje del framework como el que se muestra a continuación:

Mensaje de excepción no manejada de WinForm

Mensaje de excepción no manejada de WinForm

En casos como este, existe la posibilidad de reemplazar este mensaje por uno personalizado, quizás un poco mas amigable que el que nos brinda el framework.
Para personalizar el mensaje, podemos hacer uso del evento Application.ThreadException.

Ejemplo:

static class Program {

    [STAThread]
    static void Main() {
        //Manejamos excepciones no controladas
        Application.ThreadException += App_ThreadException;

        Application.Run(new Form1());
    }

    static void App_ThreadException(object sender, ThreadExceptionEventArgs e) {
        //El usuario desea continuar?
        StringBuilder msg = new StringBuilder();

        msg.AppendLine("Ooops! ha ocurrido un problema en la aplicación:");
        msg.AppendLine(e.Exception.Message);
        msg.Append("¿Desea continuar la ejecución de la aplicación?");

        DialogResult res = MessageBox.Show(msg.ToString(), "Error inesperado", 
            MessageBoxButtons.YesNo, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);

        if (res == DialogResult.Yes) {
            //Si desea continuar, no hacemos nada
            return;
        }
        else {
            //Si desea salir de la aplicación, la cerramos
            Application.Exit();
        }
    }
}

como resultado obtenemos el siguiente mensaje cuando ocurre una excepción no manejada:

Mensaje excepción no manejada personalizado

Mensaje excepción no manejada personalizado

Obviamente este no es más que un simple ejemplo. Además, cuando ocurre un error de este tipo, podriamos brindar más información o incluso permitirle al usuario enviar un mail que nos notifique que ocurrió un error, junto con la información del mismo, como ser StackTrace, Source, InnerException, etc. O simplemente no hacer nada….

Publicado en C#. Etiquetas: , . Leave a Comment »

REFLECTION: Invocar un método a partir de un String…

En esta segunda entrega les voy a mostrar como hacer para invocar un método, a partir de una cadena de String, la cual contiene simplemente el nombre del método.

Si inspeccionamos un poco la clase Type del framework podemos ver que tiene un método llamado Type.GetMethod(), el cual obtiene información de un método.
Lo que necesitamos saber de este método es que recibe como párametros el nombre del método y opcionalmente un Array con los tipos de los parámetros (para el caso de que exista sobrecargas de método), siendo que nos devuelve el MethodInfo relacionado.
Este MethodInfo tiene una gran cantidad de miembros, entre los cuales, los que se destacan son:

  • GetParameters(): obtiene los parámetros del método o constructor especificado.
  • GetType(): obtiene el objeto Type de la instancia actual.
  • Invoke(): realiza la llamada al método.
  • Attributes: obtiene los atributos asociados a este método.
  • IsConstructor.
  • IsAbstract, IsPrivate, IsPublic, IsStatic, IsVirtual.
  • ReturnType: obtiene el tipo de valor devuelto por este método.
  • Otros.

Lo que necesitamos utilizar entonces es el método Invoke(). La firma más común de este método es la Invoke(Object obj, Object[] parameters), donde:

  • obj: objeto en el que debe invocarse el miembro o constructor. Si el método o constructor es de clase o estático, se pasa null. Si, en cambio es un método o constructor de instancia, se debe pasar el objeto en el cual queremos ejecutar ese método.
  • parameters: lista de argumentos del método o constructor invocado. Este array debe tener el mismo número, orden y tipo que los parámetros del método o constructor que se va a invocar. Si no hay ningún parámetro, parameters debe ser null.

Les muestro un ejemplo, en el cual realizo una llamada a un método de clase o estático:

//obtengo el tipo
Type tipo = Type.GetType(namespaceTipo);

//obtengo la información del método, sé que no tiene sobrecargas
MethodInfo metodo = tipo.GetMethod(nombreMetodoEstatico);

DataSet ds = new DataSet();

//objeto que devuelve el método de clase
object objVuelta;

//tiene parámetros y sé que es un objeto de tipo DataSet 
objVuelta = metodo.Invoke(null, new object[] { ds }); 

otro ejemplo, pero con llamada a método de instancia:

//tengo una instancia
MiClase miInstancia = new MiClase();

//obtengo el tipo
Type tipo = miInstancia.GetType();

//Array de tipos
Type[] tiposParametros = new Type[] { typeof(Int32) };

//obtengo la información del método de instancia de la clase MiClase, sobrecarga de Int32
MethodInfo metodo = tipo.GetMethod(nombreMetodoInstancia, tiposParametros);

Int32 parametro = 3;

//invoco el método, con parámetro igual a 3
Object objVuelta = metodo.Invoke(miInstancia, new object[] { parametro }); 

REFLECTION: Crear una instancia a partir de un String con el nombre de la clase…

Hay muchas situaciones en las que se nos hace necesario crear una instancia de una clase a partir de una cadena de caracteres que tenemos almacenada, ya sea para utilizarla en una Factory o para mantener cierta abstracción en el tipo de datos que se maneja.
Cualquiera sea el caso, deberíamos tener un String con formato “assembly-qualified name”.
Comunmente se utiliza el siguiente formato:
“namespace+NombreClase, assembly”
(Es importante aclarar que la utilización de este String va a ser case-sensitive).

Ejemplos de assembly-qualified names:

  • “System.Int32”
  • “Configuracion.General.ClaseGeneral, Configuracion.General”
  • “TopNamespace.SubNameSpace.ClaseContenedora+ClaseAnidada, MiAssembly, Version=1.3.0.0, Culture=neutral, PublicKeyToken=b17a5c561934e089”

Lo primero que debemos hacer es obtener el tipo a partir de la cadena de caracteres:

Type tipo = Type.GetType(namespaceControlador);

Una vez que tenemos el tipo, debemos obtener el constructor e invocarlo (en el siguiente ejemplo el constructor que se usa es el vacío):

IControlador controlador = (IControlador)tipo.GetConstructor(Type.EmptyTypes).Invoke(null);

Notar que es necesario tener una interface definida para ese tipo de datos o una clase abstracta de la cual herede, de lo contrario se nos haría casi imposible manejarlo (digo casi porque también podemos ejecutar un método a partir de un String).

GetConstructor(Type.EmptyTypes): obtiene el constructor a partir de los tipos de los parámetros que el mismo acepta. En este caso obtenemos el constructor que no tiene parámetros, en otros casos deberíamos pasarle un array con los tipos.
Invoke(null): invocamos el método (en este caso el constructor) pasandole los parámetros como un array, si le pasamos null es que ejecutamos el método sin parámetros.

¿Donde lo podemos utilizar?
En el proyecto en que trabajo necesitaba mantener cierta abstracción en cuanto a los controladores para los distintos tipos de tiqueteras fiscales y no fiscales. Entonces, lo que hice es crear una Factory la cual cuando se le especifica un tipo de tiquetera, va a un almacén de datos (Base de Datos o xml) en busca de un String con el nombre del controlador específico. Una vez teniendo ese String, creo el controlador usando reflection y lo uso… Así logro que el día de mañana si se agrega un nuevo tipo de tiquetera o un nuevo controlador, lo único que debo hacer es redefinir el String del controlador.