Fecha de publicación del tutorial: 2007-04-25
Accediendo a rutinas C y C++ desde Oracle
Creación: 09-04-2007
1. Introducción
En este tutorial os quiero mostrar como llamar a rutinas desarrolladas en c o cpp desde un procedimiento almacenado de ORACLE.
2. Entorno
El tutorial está escrito usando el siguiente entorno:
Hardware: HP COMPAQ Presario V6000 (Centrino Duo 1.66GHz, 2048 MB RAM, 100 GB HD)
Sistema Operativo: Windows XP Home Edition
Oracle 9.2
MinGW 5 para windows.
Eclipse como editor de ficheros
3. Creando el .c y el .cpp
Para comenzar vamos a crear dos pequeños ficheros, uno escrito en c, y otro escrito en cpp. Crearemos una función que escribirá en un fichero una pequeña frase. A continuación os muestro el código de nuestros archivos:
helloWord.c
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
#define EXPORT _declspec(dllexport)
EXPORT void HelloWord(char* cadena, int* numero);
EXPORT void HelloWord(char* cadena, int* numero)
{
FILE * f = fopen("c:\\HelloWordC.log", "aw");
fprintf(f, "Hola desde la libreria HelloWordC.dll:\n");
fprintf(f, "Datos antes de modificarse \nCadena: %s \nNumero: %d\n", cadena, *numero);
strcpy(cadena,"Modificada por helloWordC.dll");
*numero = 10;
fprintf(f, "Despues\nCadena: %s \nNumero: %d\n", cadena, *numero);
fflush(f);
fclose(f);
}
Al llamar a la función HelloWord, se creará un fichero, si no existe ya, con el nombre HelloWordC.log en c:\ y se le añadirá la frase "Hola desde la librería HelloWordC.dll". Se imprimirán los parámetros antes y despúes de ser modificados, para ver que realmente los datos son obtenidos y modificados correctamente.
helloWord.cpp
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
#define EXPORT extern "C" _declspec(dllexport)
EXPORT void HelloWord(char* cadena, int* numero);
EXPORT void HelloWord(char* cadena, int* numero)
{
FILE * f = fopen("c:\\HelloWordCpp.log", "aw");
fprintf(f, "Hola desde la libreria HelloWordCpp.dll:\n");
fprintf(f, "Datos antes de modificarse \nCadena: %s \nNumero: %d\n", cadena, *numero);
strcpy(cadena,"Modificada por helloWordCpp.dll");
*numero = 20;
fprintf(f, "Despues\nCadena: %s \nNumero: %d\n", cadena, *numero);
fflush(f);
fclose(f);
}
La única diferencia entre las dos implementaciónes está en la definición de EXPORT. En cpp se antepone extern "C" a _declspec(dllexport) .
4. Creando las librerías
Vamos a crear un fichero makefile para facilitarnos la compilación de los ficheros y la creación de la librería. El contenido es el siguiente:
JDK_HOME=$(JAVA_HOME)
WIN32_CPP=g++
WIN32_C=gcc
CPPFILE=helloWord.cpp
CFILE=helloWord.c
CPPLIBNAMEWIN32=helloWordCpp.dll
CLIBNAMEWIN32=helloWordC.dll
CFLAGSWIN32=-DLOG -I$(JDK_HOME)\include -I$(JDK_HOME)\include\win32 -shared -Wl,--kill-at -Wall
all:
@echo "Uso: make {win32 | help}"
win32: $(FILE) $(JNIFILE)
$(WIN32_CPP) $(CFLAGSWIN32) -o $(CPPLIBNAMEWIN32) $(CPPFILE)
$(WIN32_C) $(CFLAGSWIN32) -o $(CLIBNAMEWIN32) $(CFILE)
help:
@echo "Uso: make win32"
Se crearán dos librerías. Una llamada helloWordCpp.dll generada a partir del fuente helloWord.cpp y otra llamada helloWordC.dll, generada a partir del fuente helloWord.c
Estas dos librerías son las que invocaremos desde Oracle.
5. Configurando Oracle para poder acceder a librerías externas
Debemos editar el listener de oracle (archivo listener.ora), situado en %oracle_dir%\ora92\network\admin\listener.ora, y añadir a cada entrada de SID_DESC la opción (ENVS = "EXTPROC_DLLS=ANY"). A continuación os muestro un ejemplo:
[...]
SID_LIST_LISTENER =
(SID_LIST =
(SID_DESC =
(SID_NAME = PLSExtProc)
(ORACLE_HOME = C:\oracle\ora92)
(PROGRAM = extproc)
(ENVS = "EXTPROC_DLLS=ANY")
)
(SID_DESC =
(GLOBAL_DBNAME = WOODY)
(ORACLE_HOME = C:\oracle\ora92)
(SID_NAME = WOODY)
(ENVS = "EXTPROC_DLLS=ANY")
)
)
[...]
Una vez editado el fichero, debemos reiniciar el servicio del listener de Oracle.
6. Llamada a las librerías
Lo primero que debemos hacer es crear un objeto LIBRARY asociado a la librería a la que queremos acceder. Lanzamos en la consola de Oracle:
CREATE OR REPLACE LIBRARY helloWordC AS 'C:\helloWordC.dll'; /
CREATE OR REPLACE LIBRARY helloWordCpp AS 'C:\helloWordCpp.dll'; /
Como vemos, las librerías se encuentran en C:\. Oracle no comprueba que la librería es correcta (el path, el código, etc.) hasta el momento de la ejecución, por lo tanto no sabríamos si lo hemos hecho bien hasta que no lanzaramos la prueba.
Ahora vamos a asociar dos procedimientos almacenados con las dos funciones HelloWord de las librerías. Lanzamos en la consola de Oracle:
CREATE OR REPLACE PROCEDURE helloC(cadena IN OUT VARCHAR, numero IN OUT PLS_INTEGER) AS LANGUAGE C LIBRARY helloWordC name "HelloWord"; /
CREATE OR REPLACE PROCEDURE helloCpp(cadena IN OUT VARCHAR, numero IN OUT PLS_INTEGER) AS LANGUAGE C LIBRARY helloWordCpp name "HelloWord"; /
Hemos asociado un procedimiento helloC al procedimiento HelloWord() de la librería de C, y un procedimiento helloCpp asociado al HelloWord() de la librería de Cpp. Para mapear los parámetros podemos consultar el capítulo 2 del manual de oracle (http://www.unix.org.ua/orelly/oracle/prog2/ch21_04.htm). Nos indica que VARCHAR mapea a un tipo char* de C, y PLS_INTEGER mapea a un tipo de dato int* de C.
Vamos a crear otro procedimiento llamado test para poder probar nuestras librerías:
CREATE OR REPLACE PROCEDURE test
AS
cadena VARCHAR(50);
numero PLS_INTEGER;
begin
cadena := 'Hola mundo';
numero := 0;
dbms_output.put_line('Valores iniciales');
dbms_output.put_line('Cadena:' || cadena);
dbms_output.put_line('Numero:' || to_char(numero));
helloC(cadena, numero);
dbms_output.put_line('Valores modificados con helloWordC.dll');
dbms_output.put_line('Cadena:' || cadena);
dbms_output.put_line('Numero:' || to_char(numero));
helloCpp(cadena, numero);
dbms_output.put_line('Valores modificados con helloWordCpp.dll');
dbms_output.put_line('Cadena:' || cadena);
dbms_output.put_line('Numero:' || to_char(numero));
end; /
Se imprimen los valores iniciales de las variables y se realiza las llamadas a las funciones, imprimiéndose los parámetros para comprobar que se modifican correctamente.
Ahora lanzamos nuestro test. Primero hay que activar el serveroutput de oracle para que se impriman las variables:
set serveroutput on;
Lanzamos el test:
exec test;
Comprobamos que los cambios se realizan correctamente, viendo el resultado por pantalla en la consola de Oracle. Comprobamos también que se generan los dos archivos de texto en C:\ con el contenido descrito anteriormente.
7. El código fuente
Aquí adjunto el fuente y las dll de windows.
8. Sobre el autor
José Carlos López Díaz, Ingeniero en Informática
Autentia Real Business Solutions S.L - "Soporte a Desarrollo"











