:: El Monstruillo wARfeando ::

Clase acceso a bases de datos *.mdb (MS Access)

August 7th, 2007

Hace poco me ví en la necesidad de acceder a unas tablas en unas bases de datos access (*.mdb) y acabé escribiendo esta entrada.

Es sencillo y funciona, como digo al final de la entrada, pero es engorroso a más no poder de modo que acabé escribiendo una pequeña clase para conectar con una base de datos access, hacer consultas (SELECTS, INSERTS, UPDATES … vamos cositas básicas).

Aquí hay un zip con la cabecera y el cuerpo de la clase.

Para comenzar a utilizarla, como ya dije en la entrada anterior hay que enlazar el programa con la biblioteca odbc32.lib. Además de eso es probalbe que debais cambiar algún #include <> para amoldarlo a donde pongais los archivos. Yo siempre uso una estructura de directorios del tipo de src\headers y src\fonts de modo que el #include en el cuerpo de la clase aparece #include “..\headers\accessdb.h”.

Una breve explicación de uso de la biblioteca.

Lo primero que se debe hacer es crear el objeto AccessDB para establecer la conexión y lanzar las consultas:

AccessDB conn;

Una vez tenemos el objeto conn creado ya podemos establecer la conexión (capturando errores):

if (conn.fileConnect(pathToMDBFile) < 0) {

char errstr[1024];

conn.getStrError(errstr);

// se hace lo que sea con este errstr

return -1; // por ejemplo

}

Ahora ya estamos conectados y podemos lanzar consultas SQL a diestro y siniestro

conn.sendQuery(”SELECT * FROM tablilla_molona”); // si devuelve < 0 se ha producido un error

En este caso es conveniente recoger los datos para hacer luego con ellos lo que quieras. sendQuery, cuando se usa con un SELECT genera una tabla de resultados de f filas por c columnas (una tabla FxC) y para obtener los datos de ella se hace del siguiente modo:

// vamos a rellenar una listview (por ejemplo) con todos los datos encontrados:

int filas = conn.getRows();

int cols = conn.getCols();

for (int f=0; f < filas; f++) {

for (int c=0; c < cols; c++) {

char valor[1024];

conn.getData(f, c, valor);

// aquí haríamos algo con valor

}

}

Una vez hecho todo solo queda desconectar la conexión:

conn.Disconnect();

Y con esto y un bizcocho… espero que os sea de utilidad a alguno. Y si lo ampliáis o mejoráis de algún modo no dejéis de notificármelo y pasarme el nuevo código que estaré encantado de colgarlo.


Access, ODBC y WinAPI

May 9th, 2007

¿Cómo acceder a bases de datos de MS Access desde ODBC usando las WinAPI?

Para comenzar se debe enlazar la biblioteca odbc32.lib e incluir las cabeceras sql.h, sqlext.h y sqltypes.h.

Una vez echo esto ya se puede comenzar a llamar a las funciones de la ODBC API.

Para establecer una conexión ODBC con un archivo .mdb (MS Access) hay que seguir estos pasos:

1. Alojar memoria para un entorno ODBC e inicializarlo.
SQLHENV env; // El entorno
SQLRETURN ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
// ret = { SQL_SUCCESS | SQL_SUCCESS_WITH_INFO | SQL_INVALID_HANDLE | SQL_ERROR }
SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void *)SQL_OV_ODBC3, 0);
2. Alojar memoria para un manejador de conexión y realizar la conexión.
SQLHDBC dbc; // El manejador de conexión
ret = SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);
ret = SQLDriverConnect(dbc, (HWND)NULL, (SQLCHAR *) “DRIVER={Microsoft Access Driver (*.mdb)};, SQL_NTS, DBQ=D:\\basedatos.mdb”, outConnStr, outConStrLen, &retOutConnStrLen, SQL_DRIVER_COMPLETE);
// ret = { SQL_SUCCESS | SQL_SUCCESS_WITH_INFO | SQL_NO_DATA | SQL_INVALID_HANDLE | SQL_ERROR }

Una vez tenemos el manejador de conexión podemos comenzar a lanzar sentencias SQL sobre esta conexión, para ello seguiremos los siguientes pasos:

1. Alojar memoria para el manejador de sentencias:
SQLHSTMT stmt;
ret = SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt);
2. Crear y lanzar la sentencia SQL:
SQLCHAR sentencia[1024];
char tabla[] = “variable”; // o lo que sea, es solo una excusa para crear la sentencia :P
sprintf(sentencia, “SELECT * FROM %s”, tabla);
SQLExecDirect(stmt, sentencia, sizeof(sentencia));
3. Recuperar los datos si los hay:
SQLSMALLINT columns;
SQLNumResultCols(stmt, &columns);
// Ahora vamos fila por fila sacando las columnas
while (SQL_SUCCEEDED(SQLFetch(stmt))) {
SQLUSMALLINT i;
for (i=0; i <=columns; i++) {
char buf[1024];
SQLINTEGER len;
ret = SQLGetData(stmt, i, SQL_C_CHAR, buf, sizeof(buf), &len);
if (SQL_SUCCEEDED(ret)) {
if (len == SQL_NULL_DATA) strcpy(buf, “NULL”);
// AHORA EN “buf” tenemos un dato tratable como una cadena obtenido de la base de datos
}
}
}
El “ret” de SQLGetData puede ser (SQL_SUCCESS | SQL_SUCCESS_WITH_INFO | SQL_NO_DATA | SQL_STILL_EXECUTING | SQL_ INVALID_HANDLE | SQL_ERROR).
4. Se liberan los manejadores, en este mismo orden:
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
SQLFreeHandle(SQL_HANDLE_DBC, dbc);
SQLFreeHandle(SQL_HANDLE_ENV, env);

Bien sencillo y funciona.
Ale.


Poner un icono en la TrayIcon con WinAPI

March 17th, 2007

¿Cómo hacer lo mismito que hace el Messenger o el Skype para poner su iconcico en ese rectangulito al lado de la hora en la barra de Windows (a partir de ahora ‘TrayIcon’, para abreviar)?

Hace poco vi en GetAFreeLancer.com la petición de alguien para hacer un programa que entre otras cosas se “iconificara” en la trayicon y me pregunté ¿cómo haría yo eso? así que me puse a investigar un poco (¡viva google y las MSDN!) y esto es lo que saqué en claro:

Lo primero que se necesita para poder poner el icono de una aplicación en la trayicon es tener una aplicación con icono… :P.

Para poner un icono vamos a necesitar unas estructuras de datos y unas funciones que se encuentran en shell32.lib (o shel32.dll si os van más las dlls) con los prototipos de las funciones en shellapi.h, de modo que hay que hacer que el compilador enlace esta librería y hay que añadir el include para . Además vamos a necesitar definirnos un mensaje para que se comuniquen el iconillo en el trayicon y nuestra aplicación, esto podemos hacerlo así:

#define MSG_TRAYICON WM_USER + 1

Mediante este mensaje el icono del trayicon le notificará a la aplicación los eventos de ratón que se hagan en su área (no se si le notificará más eventos, como alguno de teclado, he leido tanto que no lo hará como que sí, pero no lo he probado me queda pendiente hacerlo).

Vamos a suponer que tenemos un botoncico que al hacer click sobre él, la aplicación se nos iconifica en el trayicon y que luego, al hacer doble click con el botón derecho sobre el icono la aplicación vuelve a mostrarse y el iconillo del trayicon desaparece. Y vamos a suponer que el botoncico tiene un identificador tal que así: IDICONIFICAR.

Entonces, en la función de gestión el bucle de mensajes principal (WndProc) después de capturar el mensaje de el WM_COMMAND (donde se caputra el click en los botones) tendríamos algo así para capturar el click en IDICONIFICAR:

switch(LOWORD(wParam)) {

case IDICONIFICAR:

NOTIFYICONDATA data;

data.cbSize = sizeof(data);
data.hWnd = hWnd;
data.uID = 10001;
data.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICONCILLO));
data.uFlags = NIF_ICON|NIF_TIP | NIF_MESSAGE;
strcpy(data.szTip, “Estoy iconificao, hazme doble click”);
data.uCallbackMessage = MSG_TRAYICON;

Shell_NotifyIcon(NIM_ADD, &data);
ShowWindow(hWnd, SW_HIDE);
break;

// … demás botones

}

Con la estructura NOTIFYICONDATA preparamos el iconillo que pondremos en el trayicon:data.cbSize es para proveer algo de seguridad a la hora de leer luego la estructura (si no se pone, no funciona porque no leera nada), data.hWnd es ventana de la aplicación que se iconificará, data.uID es un identificador para el icono en el trayicon, data.hIcon es el manejador del icono que se pondrá en el trayicon, data.hTip es el mensaje que se mostrará al dejar el puntero del ratón sobre el icono durante un momentito, data.uCallbackMessage es el mensaje que hemos creado para que se comuniquen el icono en la trayicon y la aplicación y data.uFlags indica a que campos de la estructura data se va a hacer caso, en este caso al icono, al tip y al mensaje que compartirán.

Una vez cargada la estructura usamos Shell_NotifyIcon() para actuar sobre trayicon, se usa esta función tanto para añadir (NIM_ADD), como para modificar el icono (NIM_MODIFY) o para quitarlo (NIM_DELETE). Como veis se le pasa la acción (NIM_*) y la estructura. Después de esto, o antes, según os apetezca se esconde la aplicación (poner el icono en el trayicon no hace que la aplicación desaparezca de la pantalla, hay que quitarla) con ShowWindow(hWnd, SW_HIDE).

Vale, en este punto la aplicación ya es capaz de “iconificarse”, pero ¿cómo hacemos para volverla mostrar cuando hago doble click con el botón izquierdo sobre el icono (por poner un ejemplo)?

Pues en la misma función de control del bucle de mensajes (WndProc, por ejemplo), cuando se capturan los mensajes (switch(message)) como WM_COMMAND, WM_INITDIALOG, etc… Aquí podemos añadir algo así:

case MSG_TRAYICON:

NOTIFYICONDATA data;

data.cbSize = sizeof(data);
data.hWnd = hWnd;
data.uID = 10001;

// El mensaje enviado por el icono está en lParam
switch (lParam) {
case WM_LBUTTONDBLCLK:
Shell_NotifyIcon(NIM_DELETE, &data);
ShowWindow(hWnd, SW_SHOW);

}
break;

}

Ale, así de simple…