Accediendo a rutinas C y C++ desde Oracle

0
17286

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

mailto:jclopez@autentia.com

Autentia Real Business Solutions
S.L – «Soporte a Desarrollo»

Dejar respuesta

Please enter your comment!
Please enter your name here