Acerca de:

Este blog contiene los códigos, ejemplos y bases de datos que he usado cuando aprendía acerca de algún tema específico. En lugar de borrarlos (una vez dominado ya el tema), he decidido publicarlos :)

miércoles, 9 de julio de 2014

Crystal Reports Runtime: No se ha podido cargar el informe

El error completo es:
"CrystalDecisions.Shared.CrystalReportsException: No se ha podido cargar el informe. Un documento procesado por el motor JRC no se puede abrir en la pila C++".
 En concreto, salta cuando se utilizan las librerías de CrystalReports para Net.


Es un error muy feo para el motivo que lo produce: el archivo rpt que debe cargarse simplemente no está en la ruta que le indicamos. Exactamente en el método Load del objeto ReportDocument:

Private WithEvents oRpt As ReportDocument
oRpt = New ReportDocument
oRpt.Load("../informes/CrystalReportAutores.rpt")


Código sacado de la Güeb de Joaquín :D
Agradezco a este enlace por darme la solución :D

martes, 3 de junio de 2014

Npgsql lanza una System.OutOfMemoryException

Jugaba un día con el C#, las librerías del Npgsql y con una base de datos con dos tablas vinculadas entre sí por una llave foránea. Luego de insertar unos cuantos registros en ambas tablas, intenté borrar los registros de una de ellas y entonces saltó esta excepción:


Saltó una "System.OutOfMemoryException". Llegué a pensar que mi programita tendría fugas de memoria en algún lado, pero al intentar borrar los mismos registros desde el pgAdmin III saltaba el verdadero error: violación de las reglas de la llave foránea.

Me pareció curioso así que, luego de experimentar un poco, descubrí que el Npgsql lanza la System.OutOfMemoryException cada vez que se viola una relación de llaves foráneas (en mi caso: intentar borrar los registros cuyas llaves primarias estaban vinculadas hacia la otra tabla por medio de la llave foránea).
En general, esta excepción suele saltar si se violan las reglas establecidas para las secuencias (datos de tipo serial), llaves primarias y llaves foráneas.

Pero me quedaba una pregunta: ¿no debería saltar una InvalidOperationException?

Mi teoría es la siguiente: una llave foránea es en realidad un puntero hacia los datos de otra tabla. Al querer realizar una operación no permitida (por ejemplo intentar borrar los datos a donde apunta el puntero pero sin borrar el puntero) se tiene un puntero que apunta (valga la redundancia) hacia un área de memoria que ahora es inválida. Para colmo, el sistema operativo (si es código no administrado no hay colector de basura) no puede reasignar esa memoria para su uso posterior ni liberar el puntero. En resumen: resulta una fuga de memoria, la cual se traduce (al pasar ya al mundo del código administrado del .Net) como una OutOfMemoryException.

Para saber más del tema de los punteros y sus problemas, recomiendo este pdf.

viernes, 9 de mayo de 2014

¿Cuál es la diferencia entre “Null” y “Nothing” en VB6?

Esto lo pregunté en StackOverflow hace un tiempo. Acá pongo la traducción:

Tengo un recordset como el siguiente:


Dim rs as Recordset
Set rs as New Recordset

'... a lot of coding ...

if Err.Number <> 0 Then ' oops, algo salió mal
    If rs.State <> adStateClosed Then rs.Close
    Set rs = Nothing
end if

' quiero evaluar si rs es Nothing o Null

if rs is Nothing then 
' esto no tira errores y funciona bien :D
end if

if rs is Null then
' esto tira un error de "tipos no compatibles"
end if

if rs = Null then
' esto tira un error de "tipos no compatibles"
end if

if isNull(rs) then
' nunca entra aquí, isNull(rs) evalua a Falso
end if 

La respuesta que me dio Mark Bertenshaw es:
"Null" es un subtipo específico de Variant. No existe fuera de Variant, y se creó para permitir modelar un dato de tipo nulo en una base de datos.
"Nothing" es un valor de tipo Object. Identifica un puntero nulo, es decir: no hay objeto.
Al evaluar un recordset con el operador "is" tira un error porque sólo se puede usar con datos de tipo Object.
IsNull(rs) retorna falso porque evalúa variables del tipo Variant.
IsNull(rs) equivale a VarType(rs) = VBNull

sábado, 22 de marzo de 2014

Dibujando matrices con Latex

El código básico para dibujar una matriz lo conseguí de este enlace. Pero yo necesitaba dibujar una matriz como la siguiente:


El código en Latex para dibujarla es:

\[ \left(
\begin{array}{cccc}
b\textsubscript{11} & b\textsubscript{12} & ... & b\textsubscript{1n} \\
b\textsubscript{11} & b\textsubscript{12} & ... & b\textsubscript{1n} \\
\vdots & \vdots & \vdots & \vdots \\
b\textsubscript{n1} & b\textsubscript{n2} & ... & b\textsubscript{nn}
\end{array}
\right)\]



Luego tenía que dibujar una matriz como combinación de columnas multiplicadas por un escalar, como la siguiente:

El código es:

\[b= \left(
\begin{array}{c|c|c|c|c}
&&&& \\
&&&& \\
u\textsubscript{1}\mbox{. \={v}} & u\textsubscript{2}\mbox{. \={v}} & u\textsubscript{3}\mbox{. \={v}} &...& u\textsubscript{n}\mbox{. \={v}}\\
&&&& \\
&&&&
\end{array}
\right)\]


jueves, 20 de febrero de 2014

Continuando con el Conjunto de Wirth...

Actualización de Electrónica a Martillazos, hallé algunas cosas interesantes con respecto al algoritmo de PuntoPeek para calcular el Conjunto de Wirth.
El enlace aquí.
Un mirror aquí.

lunes, 13 de enero de 2014

Resetear campo autoincrementable (serial) en Postgre 9.3

Tengo una base de datos llamada "Prueba" con una tabla "phonebook", la cual tiene un campo autoincrementable (tipo serial) llamado "ID":


Cuando se crea un dato serial usando el pgAdmin, Postgre automáticamente nos crea una secuencia (en este caso llamada "phonebook_ID_seq") a la cual el campo serial está vinculado mediante la siguiente instrucción:

ALTER TABLE phonebook ALTER COLUMN "ID" SET DEFAULT nextval('"phonebook_ID_seq"'::regclass);

Para Postgre, un dato serial es un dato Integer con la función "nextval".

Esta es mi situación:
Luego de añadir y borrar varios registros a la tabla "phonebook", y finalmente dejarla vacía, quiero resetear el valor del campo "ID" a 1.

Al igual que en MySql, Postgre tiene una función llamada "setval" que me permite ponerle un valor arbitrario a la secuencia desde el cual continuará con los incrementos.
En el pgAdmin seleccionamos el campo "ID", abrimos la ventana de queries mediante el botón "Execute Arbitrary Queries" (el que tiene una lupa y el texto "SQL"), e ingresamos el siguiente query:

select setval('"phonebook_ID_seq"',1);

Hay que notar que lo que se altera es la secuencia, no el campo "ID". Hay que tener cuidado de poner el nombre de la secuencia entre comillas dobles dentro de las comillas simples. Como Postgre es Case Sensitive, saltarán errores si omitimos las comillas dobles y si el nombre de la secuencia contiene mayúsculas.

En la ventana de queries quedará así:


Si todo sale bien, nos aparecerá el valor al que hemos reseteado la secuencia. No tiene que ser 1, puede ser cualquier valor que queramos.

miércoles, 8 de enero de 2014

Postgre 9.3: Errores 42501 y 42703:

Tengo la siguiente base de datos Prueba instalada en un servidor local de Postgre versión 9.3, la cual tiene una sola tabla "phonebook" cuya estructura copié de algún sitio de Internet (al instalar Postgre se tiene la opción de instalar también el pgAdmin):


El Owner de la base de datos y de la tabla es el superusuario "postgre". La base de datos Prueba tiene como esquema por defecto (Default Schema) igual a público (public), también sus privilegios son públicos.

También he creado un usuario "user01" con las siguientes propiedades:


Al conectar una aplicación en C# mediante el usuario "user01", usando Npgsql (el cual se puede instalar junto con el Postgre usando la aplicación Stack Builder que también viene con el instalador del Postgre) y luego de ver los datos de la tabla phonebook todo iba bien; pero cuando quería modificar la tabla phonebook con un update, insert ó delete me arrojaba el siguiente error:


"Error 42501: permiso denegado a la secuencia phonebook_ID_seq"

Este error salta si el usuario con el que intento modificar la tabla no es su dueño (owner). No interesa si la base de datos tiene como dueño al superusuario "postgre", la tabla debe tener como dueño al usuario que intenta modificarla.

Para cambiar al dueño de la tabla phonebook de "postgre" a "user01" basta este script:

ALTER TABLE phonebook
  OWNER TO user01;

Y entonces al usar la siguiente sentencia insert desde C#:

string query = @"insert into phonebook (phone, firstname, lastname, address) values ('1234', 'Wert', 'Asdlo', 'Fake Street 123')";

Los datos se guardaban correctamente, pero al querer actualizar o borrar: 

string query1 = @"Update phonebook set phone ='0000', firstname ='Carry', lastname ='Doe', address = 'No signed' where ID = 1";
string query2 = "delete from phonebook where ID = 1";

Me saltaba el siguiente error:


"Error 42703: no existe la columna id"

Lo extraño es que la tabla phonebook sí tiene una columna llamada "ID". Buscando por internet, hallé aquí la solución: se debe poner el nombre del campo que contiene mayúsculas entre comillas:

string query1 = "Update phonebook set phone ='0000', firstname ='Carry', "
" lastname ='Doe', address = 'No signed' where \"ID\" = 1";
string query2 = "delete from phonebook where \"ID\" = 1";

El caracter \ le indica al C# que la comilla que le sigue no indica el final de la cadena. El motor del Postgre convierte todos los nombres de las columnas en una sentencia sql a minúsculas. Por ahí leí que, internamente, Postgre es Case Sensitive.

sábado, 14 de diciembre de 2013

Cómo Convertir de long a LPCWSTR (puntero largo a Wide String constante) en Visual C++

Todo empezó cuando quise calcular el alto en píxeles de la barra de título de una ventana, ya que éste valor varía dependiendo del tema de Windows que se utilice.

Para calcular el alto de la barra de título utilizo dos apis: GetWindowRect y GetClientRect. La primera devuelve las coordenadas que ocupa la ventana, la segunda devuelve las coordenadas que ocupa la ventana sin la barra de título.

El valor calculado es del tipo long. Faltaba convertirlo a LPCWSTR para pasarlo como parámetro a la api setWindowText que lo escribirá en lugar del título de la ventana.

He usado la información que hallé en estas webs:
La función ltoa (reemplazada por _ltoa)
Convertir de char* a LPCWSTR
La función MultiByteToWideChar

El código es el siguiente:

RECT r1, r2;

GetWindowRect(hWnd, &r1);
GetClientRect(hWnd, &r2);

long alto1 = r1.bottom - r1.top;
long alto2 = r2.bottom - r2.top;

long alto3 = alto1-alto2;

// convertir long a Wide WCHAR*
int size=sizeof(long)*8+1;
char* texto = new char[size];
WCHAR* texto2 = new WCHAR[size];

_ltoa(alto3,texto,10);
MultiByteToWideChar(CP_UTF8, 0, texto, -1, texto2, size);
// fin de la conversión. Para convertir de WCHAR* a LPCWSTR (puntero largo a Const Wide String) basta un casteo
SetWindowText(hWnd, (LPCWSTR)texto2);


En el caso de una aplicación tipo win32, coloqué este código dentro de la función que procesa los mensajes de la ventana (WndProc), exactamente en el mensaje que se envía al crearla:


Al ejecutar el programa, mostrará el alto de la barra de título en el texto de... la barra de título.


sábado, 23 de noviembre de 2013

Implementación Thread-safe de una clase Singleton

Entrada original de WhatILearntToday: "El patrón Singleton se usa cuando queremos que una clase permita definir una, y sólo una, instancia de esa misma clase".

La implementación WhatILearntToday de una clase Singleton Thread-Safe es la siguiente:

public class SingletonClass
    {
        private SingletonClass()
        {
        }

        //Implementation #1 NO Thread-Safe: puede fallar si varios hilos intentan instanciarla al mismo tiempo
        //static SingletonClass _instance = null;
        //public static SingletonClass GetInstance()
        //{
        //    if (_instance == null)
        //    {
        //        _instance = new SingletonClass();
        //    }
        //    return _instance;
        //}

        //Implementation #2 Thread-safe
        static readonly SingletonClass _instance = new SingletonClass();

        public static SingletonClass GetInstance()
        {
            get
            {
                return _instance;
            }
        }

        public string GetWelcomeMessage()
        {
            return "Welcome to the Singleton world!! ";
        }
    }

Mi implementación (también Thread-Safe) es:

public class SingletonClass
    {
        private SingletonClass()
        {
        }

        static readonly SingletonClass _instance = null;
        static object lockThis;

        public static SingletonClass GetInstance()
        {
            lock(lockThis)
            { 
                 if (_instance == null)
                         _instance = new SingletonClass();
            }  
           return _instance;           
        }

        public string GetWelcomeMessage()
        {
            return "Welcome to the Singleton world!! ";
        }
    }