Foros del Grupo Albor

Un lugar de encuentro para los programadores de habla hispana

Bienvenido(a), Visitante. Por favor, ingresa o regístrate.
 
   Inicio   Buscar Ingresar Registrarse  
Páginas: [1]   Ir Abajo
  Imprimir  
Autor Tema: DLL Delphi y C  (Leído 5172 veces)
pepelu
Newbie
*
Desconectado Desconectado

Mensajes: 6


« : 22 de Noviembre de 2006, 19:22:28 »

estoy desarrollando una DLL en Delphi cuyas funciones deben ser llamadas desde C.

El prototipo de la funcion C que hace la llamada es
int (char *Cadena).

Mi funcion debe de colocar en *Cadena , un texto , para lo cual mi prototipo de funcion en mi dll delphi es:
Function MiFuncion(Cadena:Pchar):Integer
El problema es el siguiente: cuando intento modificar el valor de *Cadena desde mi funcion, utilizo la funcion StrPCopy de delphi , pero la aplicacion C que hace la llamada a esta funcion, peta.

¿Que otra funcion puedo utilizar o como debería hacerlo...??

Gracias anticipadas por vuestra colaboracion.

J.L.
En línea
Mario
Moderador Global
*****
Desconectado Desconectado

Mensajes: 81



WWW
« Respuesta #1 : 22 de Noviembre de 2006, 20:23:44 »

estoy desarrollando una DLL en Delphi cuyas funciones deben ser llamadas desde C.

Hola.

Conviene que revises las declaraciones (mira en la ayuda) porque, a ambos lados (C y Pascal) se está presuponiendo un convenio de llamada distinto...

Citar
El prototipo de la funcion C que hace la llamada es
int (char *Cadena).

Con esa declaración, cualquier compilador C va a interpretar que la función llamada tiene el convenio __cdecl...

Citar
mi prototipo de funcion en mi dll delphi es:
Function MiFuncion(Cadena:Pchar):Integer

Con esa declaración, el compilador de Object Pascal (el que viene con Delphi) entenderá que el convenido de llamada es fastcall o pascal (mira en la ayuda de Delphi porque no estoy seguro de si fastcall se escribe así o __fastcall ni si pascal va así o como __pascal).

Para que dos aplicaciones intercambien datos (y no produzcan excepciones en la llamada o en su retorno), los dos compiladores deben utilizar el mismo convenio de llamada. Para ello, se declara la función en el código fuente, remarcando el convenio. Por ejemplo:

/* En C/C++ */
__declspec(dllimport) int (__stdcall *ptr_MiFuncion)(char *Cadena);

// En Object Pascal
Function MiFuncion(Cadena: Pchar): Integer; stdcall; export;

He utilizado el convenio __stdcall porque es el que se usa en el API de Windows. Tú puedes utilizar cualquier otro, siempre que ese convenio sea el mismo en ambos compiladores. En cuanto a ptr_MiFuncion es un puntero a la función que se pretende importar en C/C++ (la que está hecha en Object Pascal): tendrás que cargar la biblioteca (con LoadLibrary()) e iniciar ese puntero a función (con GetProcAddress()) para poder utilizarlo. Es necesario, también, indicar la exportación (por ejemplo, con export en Delphi) y la importación (por ejemplo, con __declspec(dllimport) en C/C++).

Si te manejas bien con el inglés, puedes leer la página Dynamic-link libraries and packages, donde se explica el uso de DLL y funciones exportadas, tanto por bibliotecas como por paquetes (BPL).

Marco Cantú habla, muy someramente, sobre los convenios de llamada en el apartado Convenios de llamada en Delphi del Capítulo 6. Procedimientos y funciones del libro en línea Pascal Esencial (para Delphi) (Essential Pascal, en inglés).

Citar
Mi funcion debe de colocar en *Cadena , un texto

Con eso ten cuidado porque fallará si una de las funciones (la que llama o la llamada) adquiere memoria y la otra función (la llamada o la que llama) la libera. Cada aplicación tiene un gestor de memoria interno. Si has de hacer esas operaciones que digo (obtener memoria en una aplicación y liberarla en otra), deberás usar un gestor de memoria común. Trabajando en Windows, se trataría de llamar a las funciones del API de las familias GlobalAlloc() o HeapAlloc().

Saludos
Mario
« Última modificación: 22 de Noviembre de 2006, 20:47:27 por Mario » En línea
pepelu
Newbie
*
Desconectado Desconectado

Mensajes: 6


« Respuesta #2 : 24 de Noviembre de 2006, 09:27:58 »

Muchas gracias por tu ayuda Mario. He implementado el comando stdcall y funciona mucho mejor , aunque sigo teniendo problemas cuando quiero modificar el contenido de (char *Cadena)... Si consigo solucionarlo, pubicaré la solucion por si le sirve a alguien.....

Saludos

En línea
Mario
Moderador Global
*****
Desconectado Desconectado

Mensajes: 81



WWW
« Respuesta #3 : 24 de Noviembre de 2006, 19:50:28 »

Muchas gracias por tu ayuda Mario.

Hola.

Y, por supuesto, que de nada: para esto estamos.

Citar
He implementado el comando stdcall y funciona mucho mejor

No sé qué es eso: o funciona (seguro que sí) o no lo hace, lo que es dudoso porque yo utilizo esa técnica todos los días y nunca ha fallado.

Citar
sigo teniendo problemas cuando quiero modificar el contenido de (char *Cadena)

¿Qué problemas? No debería haber problemas, excepto que pretendas pasar algo ahí que no sea un array o matriz de caracteres o que éste sea constante o demasiado pequeño. Si comentas cuales son los problemas (y, especialmente, si envías algún ejemplo de uso), quizá podamos ayudarte a solucionar esos problemas.

Saludos
Mario
En línea
pepelu
Newbie
*
Desconectado Desconectado

Mensajes: 6


« Respuesta #4 : 27 de Noviembre de 2006, 13:03:07 »

Hola Mario:

Casi lo he conseguido pero me pasa lo siguiente:

Mi codigo Delphi es el siguiente...

Function CambiaValor(Datos:Pchar):integer;stdcall
var
Bucle:Integer;
CadenaFinal:Array[0..100] of Char;
Caracter:char;

begin


StrPcopy(CadenaFinal,'Esta es la cadena con la que se va a rellenar...');

Cambiamos  el contenido de Datos con los de la variable CadenaFinal

For Bucle := 0 to  100 do begin
   Caracter := CadenaFinal[Bucle];
   if Caracter = #0 then break;      // Si es final de cadena, salimos
   Datos[Bucle] := Caracter;
end;



Result := 1;



En Mi codigo C,  defino la variable..
char *Cadena;

Si inicializo la variable con algun valor, antes de llamar la funcion por ejemplo si hago :
Cadena  = "                "   y luego llamo a mi funcion CambiaValor(Cadena) ,  funciona perfectamente
pero si llamo a mi funcion sin inicializar la variable,, entondes peta...

La pregunta es ,, ¿Como puedo inicializar esta variable desde mi funcion delphi, con algun valor...Huh
He probado con FillMemory, CopyMemory pero no me funciona o no lo estoy haciendo bien.....


Gracias de nuevo y saludos
 J.L.

En línea
Mario
Moderador Global
*****
Desconectado Desconectado

Mensajes: 81



WWW
« Respuesta #5 : 28 de Noviembre de 2006, 19:54:07 »

Hola Mario:

Hola.

Citar
Casi lo he conseguido

Pues sí. Pero por pura casualidad smiley

Cuando se intercambian datos entre dos aplicaciones o procesos que tengan distintos gestores de memoria (caso de una aplicación hecha en C/C++ y otra hecha en Object Pascal, como en este caso), la asignación y liberación de memoria ha de estar bien controlada.

En líneas generales (o, si lo prefieres, en el caso más sencillo), se trata de que el proceso que llama reserve tanta memoria como se necesite para la operación a realizar; a continuación, pasaría esa memoria (por ejemplo, tu array de char) al otro proceso, que la manipularía; finalmente, el proceso llamador liberaría la memoria.

La parte que has hecho en Object Pascal, sin ser muy óptima...
Citar
Código:
For Bucle := 0 to  100 do begin
   Caracter := CadenaFinal[Bucle];
   if Caracter = #0 then break;      // Si es final de cadena, salimos
   Datos[Bucle] := Caracter;
end;

... (¿por qué copiar byte a byte cuando se puede realizar una copia de un tramo de memoria, bien con las funciones de cadena del tipo StrPcopy() o bien con funciones de manipulación directa de la memoria, como memcpy o similar?), está bien.

El código en C/C++ tiene el problema que comentaba antes y que tú mismo descubres...

Citar
Si inicializo la variable con algun valor, antes de llamar la funcion por ejemplo si hago :
Cadena  = "                "   y luego llamo a mi funcion CambiaValor(Cadena) ,  funciona perfectamente
pero si llamo a mi funcion sin inicializar la variable,, entondes peta...

A poco que conozcas el funcionamiento de los punteros (y los arrays de char los puedes identificar con punteros) verás que es correcto ese comportamiento: al iniciar el puntero con algún valor estás "obteniendo memoria" (reservando un espacio de memoria para el puntero); cuando no lo inicias, el tamaño de la memoria reservada para el puntero es cero. Por ello, cualquier intento de asignación provocará una violación de acceso a memoria.

Citar
La pregunta es ,, ¿Como puedo inicializar esta variable desde mi funcion delphi, con algun valor...Huh

La forma más sencilla es que, en la función que llama, inicies el array y que pases ese array a Object Pascal. Por ejemplo:

Código:
/* En C/C++ */
char Cadena[256];        // Se reservan 256 bytes de memoria
CambiaValor(Cadena); // Llamada a la función de Object Pascal
// ...

Otra manera sería utilizar las funciones de memoria del API de Windows ('GlobalAlloc()' y familia). Esto requería de mayor control por tu parte y es muy poco seguro (o, lo que es lo mismo, muy poco recomendable). Por ejemplo:

Código:
/* En C/C++ */
char *Cadena = 0;        // Se declara un puntero nulo
// ... ¿Adquirir memoria?
CambiaValor(Cadena); // Llamada a la función de Object Pascal
// ...
// Importante: liberar memoria
GlobalFree(Cadena);

La adquisición de memoria se podría hacer antes de CambiaValor() o en la función llamada. En este último caso, la cosa sería más o menos así...

Código:
(* En Object Pascal *)
Function CambiaValor(Datos: Pchar): integer; stdcall
begin
   // Reserva de 256 bytes de memoria
   Cadena := PChar(GlobalAlloc(GMEM_FIXED, 256));
   // ... como lo tienes
end;

Repito: este último método no lo recomiendo. En ocasiones, no hay otra forma de hacerlo o esa otra forma es más difícil aún. Así que sólo en esas contadas excepciones debería adquirirse memoria en un lado y liberarse en otro.

Saludos
Mario
« Última modificación: 29 de Noviembre de 2006, 19:35:27 por Mario » En línea
pepelu
Newbie
*
Desconectado Desconectado

Mensajes: 6


« Respuesta #6 : 29 de Noviembre de 2006, 11:11:53 »

Hola Mario:

Gracias de nuevo..... esta claro que tengo que ponerme al dia con esto de los punteros, la verdad es que siempre he  programado aplicaciones de bases de datos  y esto es nuevo para mi....

Saludos

J.L.
En línea
Páginas: [1]   Ir Arriba
  Imprimir  
 
Ir a:  

Impulsado por MySQL Impulsado por PHP Foros del Grupo Albor | Impulsado por SMF 1.1.16.
© 2005, Simple Machines. Todos los Derechos Reservados.
XHTML 1.0 válido! CSS válido!
Página creada en 0.269 segundos con 20 consultas.