[Old School] Acceso a bases de datos ODBC desde MFC

Descargar código fuente

El verano. Esa época en la que crees que por fin tendrás algo de tiempo libre para hacer cosas como actualizar el blog, y resulta que acabas estando más ocupado que durante el curso. Y para colmo, las 2 o 3 horas muertas que tienes al día se van al traste cuando haciendo limpieza encuentras un CD lleno de ROMs y emuladores de consolas de 16 bits. Emuladores que misteriosamente son compatibles con el mando cableado de XBox 360. Razones más que de sobra para volver a machacar píxeles como antaño…

En fin. Entre rato y rato, y debido a, como viene siendo costumbre desde hace 5 años, estar contratado por cierta empresa postal cuyo nombre no quiero mencionar, he vuelto a la programación para no terminar de oxidarme del todo. La razón ha sido la necesidad de actualizar el callejero del que se dispone en la oficina para corregir las cartas que llegan mal direccionadas; actualmente se disponía de unas hojas mecanografiadas con una antigüedad de unos 20 años en las que se habían ido escribiendo y tachando una larga lista de correcciones (nuevas calles, cambios de domicilio, etc). Pues bien, decidí recopilar todos los datos más actuales y, ya puestos, compilarlos en una base de datos bien estructurada, y no un documento de Word u hoja de Excel como suele ser la costumbre. Los problemas no habían hecho nada más que empezar.

Bien. En la oficina se disponen de varios PCs, y uno de ellos es de uso exclusivo por el personal de reparto. La base de datos estaría almacenada únicamente en este equipo, con la “gracia” de contar con las siguientes peculiaridades:

  1. Sistema operativo Windows XP. Vale, no está mal, yo mismo sigo usándolo.
  2. Ausencia total de cualquier .NET Framework (hala, a olvidarse de tener las cosas fáciles).
  3. Únicos programas del paquete Office 2003 instalados: Word y Excel (adiós, base de datos de Access editable por los usuarios).
  4. Ausencia total de permisos de instalación de aplicaciones y de acceso al Panel de Control (para terminar de arreglar las cosas, vamos).

O lo que es lo mismo: adiós C#, adiós SQL Server, adiós Rapid Application Development. Pero bueno, no estaba todo perdido.

Dándole vueltas a qué API podía usar para hacer algo con un GUI mínimamente decente, me acordé de mis inicios con VC++ y la programación gráfica: MFC. Si compilaba el proyecto con las librerías en modo estático (Link as static library), el tamaño final del ejecutable se incrementaría notablemente, pero me ahorraría el problema de tener que comprobar si las DLLs ya estaban presentes en el sistema. Por otra parte, el problema del acceso a la BD también estaba solucionado; aunque no podía acceder al Panel de Control y por tanto añadir un origen de datos ODBC, sí podía conectar en tiempo de ejecución gracias al driver de Microsoft Access que todas las instalaciones de Windows XP traen por defecto.

Ya sólo quedaba ponerse manos a la obra: se crea una nueva aplicación MFC basada en diálogos (nada de modelo documento-vista, no es necesario complicarse la vida) y se añade el soporte para bases de datos:

29 #include <afxdb.h>

30 #pragma comment(lib, «odbc32.lib»)

Creamos un objeto CDatabase y lo abrimos mediante Open, especificándole una cadena de conexión válida en nuestro caso.

80 if (!m_pDb.Open(_T(«bdprueba»),FALSE,FALSE,_T(«DBQ=.\bdprueba; DefaultDir=.\; Driver={Microsoft Access Driver (*.mdb)}; DriverId=25; FIL=MS Access; MaxBufferSize=2048; MaxScanRows=8; PageTimeout=5; SafeTransactions=0; Threads=3; UID=admin; UserCommitSync=Yes;»),FALSE))

81 {

82 AfxMessageBox(_T(«Error al conectar con la BD!»));

83 SendMessage(WM_CLOSE,0,0);

84 }

Y una vez abierto, operamos mediante objetos CRecordset, que podemos instanciar directamente para pasarles consultas SQL, o crear clases derivadas de éste para añadir funcionalidades que vayamos a usar frecuentemente.

185 CRecordset resultado;

186 CString query;

187 query.Format(_T(«SELECT Nombre FROM Juegos WHERE Nombre LIKE ‘%%%s%%'»), buscar);

188

189 if (resultado.Open(CRecordset::dynaset, query, CRecordset::none) && resultado.GetRecordCount() > 0)

190 {

191 // La consulta ha devuelto registros, recorrerlos…

192 }

Como viene siendo costumbre, adjunto un pequeño programa de ejemplo que demuestra las funcionalidades explicadas. Aunque el uso de MFC ha decaído bastante desde el nacimiento de .NET, espero que a alguien le sirva de provecho 🙂

Detectando fugas de memoria: Visual Leak Detector

Ésta librería me la encontré por casualidad un día navegando por Internet, y curiosamente unos meses más tarde tuve que recurrir a ella. Si has desarrollado programas para Linux usando C o C++, seguramente tarde o temprano habrás recurrido a usar Valgrind, el entorno que emula una arquitectura x86 y detecta todos los usos indebidos de la memoria.

Pues bien, Visual Leak Detector es una librería que, al incluirla en nuestro proyecto, nos muestra toda la información (por medio de la consola de Visual Studio o, si lo deseamos, con salida a un fichero de texto) sobre la memoria no liberada (memory leaks) con una precisión pasmosa. Y ahora, un ejemplo de su uso:

  1. Descargamos el instalador.
  2. Lo ejecutamos y procedemos a instalarlo en el lugar que más adecuado nos parezca. Muy seguramente, durante el proceso de instalación nos preguntará si deseamos añadir la ruta a la librería “vld.dll” al path automáticamente. Aceptamos y continuamos.image
  3. Ya esta listo para usar. Si queremos, en la última pantalla podemos visualizar la documentación.image
  4. Ahora hay dos opciones para que Visual C++ reconozca el archivo de cabecera y la librería; la primera consiste en ir a la carpeta donde hemos instalado Visual Leak Detector y copiar los directorios “include” y “lib” a la carpeta “VC” existente dentro de nuestra instalación de Visual Studio. Sin embargo, este método puede considerarse poco más que una chapuza, así que vamos a hacer las cosas bien y lo vamos a configurar desde dentro de nuestro proyecto.
  5. Ejecutamos el Visual Studio y creamos una nueva aplicación de consola de Visual C++. En el diálogo del asistente seleccionamos la sección “Application Settings” y activamos la casilla “Empty project”. No queremos que Visual C++ nos añada archivos que puedan dar lugar a confusión (como las cabeceras precompiladas).image

    image

  6. Ahora es el turno de configurar las nuevas rutas de inclusión del proyecto. Nos vamos al menú “Project” y seleccionamos la última opción, “<Nombredeproyecto> Properties…”. Navegamos el árbol de opciones seleccionando “Configuration Properties / C/C++ / General” y pinchamos en el campo “Additional Include Directories” de la lista de opciones de la derecha. Veremos que aparece un botón con unos puntos suspensivos que nos abrirá una nueva ventanita, en la que pulsando el botón con el icono de una carpeta nos permitirá especificar un nuevo directorio para añadir a la lista. Añadimos el subdirectorio “include” dentro de nuestra instalación de Visual Leak Detector y pinchamos en “OK”.image

    image

  7. Ahora en el árbol de opciones de la izquierda nos desplazaremos hasta “Configuration Properties / Linker / General”. Aquí, con el campo “Additional Library Directories” tenemos que hacer exactamente lo mismo que en el punto anterior, solo que en lugar de agregar el directorio “include”, ésta vez seleccionaremos “lib”.image
  8. Por último, vamos a “Configuration Properties / Linker / Input” y en “Additional Dependencies” escribimos “vld.lib”. De ésta manera le decimos al enlazador que si encuentra algún símbolo desconocido, busque información en dicha librería para resolver las dependencias.image
  9. Ahora ya solo queda comprobar el funcionamiento de la librería. Vamos a añadir un nuevo fichero de código fuente a la carpeta “Source Files” de nuestro proyecto, llamado “main.cpp”, y vamos a teclear el siguiente código:image
  10. Y procedemos a compilarlo y ejecutarlo. Aparecerá la ventana de consola con la ejecución de nuestro programa y podremos finalizarla. Aparentemente todo funciona bien, pero si vamos a “View / Output”, habilitaremos la ventana en la que podremos ver la salida del Visual Leak Detector. Aquí nos informa que se ha producido un memory leak, concretamente de 100 bytes, y el inicio del problema está en la línea 8 de “main.cpp”.image
  11. Podemos comprobar que en dicha línea hemos realizado una reserva de memoria con el operador new, pero luego no la hemos liberado. Vamos a subsanar el error y a comprobar qué ocurre esta vez tras la ejecución:image

En efecto, Visual Leak Detector notifica que no hay ninguna fuga de memoria y que todo ha funcionado correctamente. Ahora es el turno de emplearlo en vuestras propias aplicaciones 😉

Uso de la función “main” en C/C++

Escribo este post porque según las estadísticas, en los últimos días he recibido bastantes visitas a través de buscadores intentando encontrar una explicación al uso de la función main en C/C++ (seguro que no es la única sorpresa que me da haber escogido este nombre para el blog…). Qué menos que agradar a mi escaso público y ganarme unos cuantos lectores, así que voy a escribir algo útil de una vez y explicaré todo lo profundamente posible esta función.

En primer lugar, la función nunca debe ser declarada como void. Ésta es una mala práctica que se suele cometer, sobre todo al comenzar la andadura con la programación (lo admito, ¡yo también lo hacía al principio!); si bien algunos compiladores lo aceptan, los más estrictos se quejarán en forma de warning o error. Así que hay que usar int para declarar el tipo, o incluso también se puede obviar, porque el compilador le asignará este tipo por defecto.

void main() // MAL: algunos compiladores se quejarán
{
}

int main() // CORRECTO: la forma adecuada de hacerlo
{
}

main() // CORRECTO: no es la mejor, pero los compiladores que sigan el estándar la darán por buena
{
}

Bien, ahora atacaremos los valores de retorno. Esto es muy sencillo; como hemos declarado la función del tipo int, al llegar al final de su ejecución tendremos que devolver un valor. Normalmente se devuelve 0 para indicar que el programa ha finalizado correctamente, y cualquier otro valor para indicar una ejecución anormal. Pero, si queremos ser completamente estrictos (en algunas plataformas el significado de los valores se invierte, y 0 puede significar que ha ocurrido un fallo de ejecución), emplearemos las macros EXIT_FAILURE y EXIT_SUCCESS. Como su nombre bien indica, la primera devuelve una ejecución errónea, y la segunda indica al proceso padre que todo ha ido como debería. Sus definiciones se encuentran en stdlib.h para el lenguaje C y cstdlib.h para C++.

int main()
{
  return EXIT_SUCCESS;  // El programa terminó la ejecución debidamente
}

int main()
{
  return EXIT_FAILURE;  // El programa encontró algún fallo y no pudo finalizar la ejecución correcta
}

int main()
{
  return 5;  // Valor de retorno personalizado; de esta manera podemos indicarle al proceso que invoca a nuestro programa que ha ocurrido algo determinado (por ejemplo, que no se ha llamado al programa con determinados parámetros)
}

Y por último, hablaremos de los parámetros de la función. Hasta ahora, la hemos definido sin ningún tipo de parámetros, porque según el estándar esto puede hacerse perfectamente; sin embargo, main acepta dos parámetros que se explicarán a continuación (hay que recordar que siempre es o cero o dos parámetros, en caso de emplear cualquier otro número el compilador dará warning o error, dependiendo de lo quisquilloso que sea):

  • Primer parámetro: tipo int. Por costumbre se le llama argc, diminutivo de argument count, pero se le puede llamar como se desee. Como su propio nombre indica, contiene el número de argumentos que se le pasan al programa. Hay que tener en cuenta que el primer argumento de un programa siempre será el nombre de este, de tal manera que si hacemos la siguiente ejecución

    :> miprograma param1 param2

    nuestro parámetro argc valdrá 3, y por consiguiente, si llamamos al programa sin parámetros, argc equivaldrá a 1.

  • Segundo parámetro: tipo char**. Aunque también puede especificarse como char* argv[], lo importante es indicar que estamos trabajando con un doble puntero a char, esto es, un vector de vectores de caracteres, o para simplificar, un vector de cadenas. Como se ha indicado anteriormente, el nombre más común para llamar a este parámetro es argv, contracción de argument vector, y al principio puede resultar un poco lioso lidiar con él, más aún si no se han usado punteros nunca. Así que simplemente hay que recordar que estamos trabajando con un vector de cadenas, y que cada posición (recordad, su extensión va desde 0 hasta argc-1) guarda uno de los argumentos empleados al llamar a nuestro programa. Para la ejecución de ejemplo del punto anterior, esta sería la traza de nuestros parámetros:

    argc = 3;
    argv[0] = “miprograma”;
    argv[1] = “param1”;
    argv[2] = “param2”;

Y esto es todo por hoy, como veréis tampoco es para tanto, aunque al principio se pueda atragantar. Y aprovecho para animar a todo el que llegue aquí a que formule las preguntas que quiera, intentaré ayudar en la medida de lo posible 😀