PDA

Ver la Versión Completa : problema con embarcadero y dll


elmago00
13-01-2014, 19:56:56
hola,
hace días estoy desarrollando una aplicación que llame a una dll, esta dll obtiene un string, por lo que le paso como parámetro un texbox, luego la dll devuelve un array. todo al parecer funciona no tengo ningún error, ningún warning.
pero embarcadero xe3 se cierra automáticamente, no inicia la aplicación, solo empieza a compilar y no lo finaliza. e creado 10 programas grandes en delphi, pero en ninguno e implementado una dll.

así que intente, hacer pruebas con los ejemplos que hay aquí, 5 para ser específicos, luego intente con lo que esta en delphibasic y obtengo lo mismo, solo empieza a compilar y después no hace nada. repito que no me da ningún error ni llamada de atención.

algún experto en dll, que me pueda dar un pequeñísimo ejemplo de dll, sin que se me cierre el compilador.

muchas gracias

ecfisa
14-01-2014, 02:36:05
Hola elmago00.

Sin ver tu código es difícil sino imposible poder saber que sucede y por tanto darte un ejemplo específico. Pero te pongo unos enlaces que pueden ayudarte con el manejo de DLL's en Delphi:.

Creating and Using DLLs from Delphi (http://delphi.about.com/od/windowsshellapi/a/dll_basics.htm)
Creación y acceso a DLL (Librería) con Delphi (http://www.ajpdsoft.com/modules.php?name=News&file=article&sid=261)
How to write and call DLL's within Delphi (http://www.tutorialspoint.com/dll/dll_delphi_example.htm)


Saludos :)

nlsgarcia
14-01-2014, 21:26:06
elmago00,


...algún experto en dll, que me pueda dar un pequeñísimo ejemplo de dll...

No soy un experto en Delphi, pero tratare de ayudar lo mejor que pueda :rolleyes:

Revisa este código:

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

type
TForm1 = class(TForm)
Edit1: TEdit;
Memo1: TMemo;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

TA1 = Array of String;
TA2 = Array of Integer;
TA3 = Array of Double;

function FA1(Value : String) : TA1; stdcall; external 'TestDLL.dll';
function FA2(Value : String) : TA2; stdcall; external 'TestDLL.dll';
function FA3(Value : String) : TA3; stdcall; external 'TestDLL.dll';

var
Form1: TForm1;

implementation

{$R *.dfm}

function StringToCaseSelect(Selector : string; CaseList: array of string): Integer;
var
pos: integer;
begin
Result := -1;
for pos := 0 to Length(CaseList)-1 do
begin
if CompareText(Selector, CaseList[pos]) = 0 then
begin
Result := pos;
Break;
end;
end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
A1 : TA1;
A2 : TA2;
A3 : TA3;
i : Integer;

begin

case StringToCaseSelect(Edit1.Text,['A1','A2','A3']) of
0 : A1 := FA1(Edit1.Text);
1 : A2 := FA2(Edit1.Text);
2 : A3 := FA3(Edit1.Text);
else
MessageDlg('Solo se permiten los valores string A1, A2 y A3', mtInformation, [mbOK], 0);
end;

Memo1.Clear;

if Length(A1) > 0 then
for i := Low(A1) to High(A1) do
Memo1.Lines.Add(A1);

if Length(A2) > 0 then
for i := Low(A2) to High(A2) do
Memo1.Lines.Add(IntToStr(A2));

if Length(A3) > 0 then
for i := Low(A3) to High(A3) do
Memo1.Lines.Add(FloatToStr(A3[i]));

end;

end.

El código anterior en Delphi 2010 bajo Windows 7 Professional x32, [I]implementa tres funciones exportadas por un DLL en un programa tipo Desktop bajo Windows.

Revisa este código:

library TestDLL;

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

type
TA1 = Array of String;
TA2 = Array of Integer;
TA3 = Array of Double;


{$R *.res}

function FA1(Value : String) : TA1; stdcall;
var
A1 : TA1;
i : Integer;

begin

if Value = 'A1' then
begin

SetLength(A1,10);

Randomize;

for i := 0 to 9 do
A1 := 'Number-' + IntToStr(Random(100));

Result := A1;

end
else
begin
SetLength(A1,1);
A1[0] := 'La función esperaba A1 como string y recibio : ' + Value;
Result := A1;
end;


end;

function FA2(Value : String) : TA2; stdcall;
var
A2 : TA2;
i : Integer;

begin

if Value = 'A2' then
begin

SetLength(A2,10);

Randomize;
for i := 0 to 9 do
A2[i] := Random(100);

Result := A2;

end
else
begin
SetLength(A2,1);
A2[0] := High(Integer);
Result := A2;
end;

end;

function FA3(Value : String) : TA3; stdcall;
var
A3 : TA3;
i : Integer;

begin

if Value = 'A3' then
begin

SetLength(A3,10);

Randomize;
for i := 0 to 9 do
A3[i] := Random;

Result := A3;

end
else
begin
SetLength(A3,1);
A3[0] := 3.1415927;
Result := A3;
end;

end;

exports
FA1, FA2, FA3;

begin
end.

El código anterior en Delphi 2010 bajo Windows 7 Professional x32,[I] implementa un DLL que exporta tres funciones en la cual cada una devolverá un array en función del string de entrada, en caso de que el string recibido no sea el correcto se devuelve un valor por defecto.

Nota: Los códigos anteriores funcionan correctamente en una Máquina Física con Delphi 2010 bajo Windows 7 Professional x32 y en una Máquina Virtual con Delphi XE4 bajo Windows 7 Professional x32, [I]si el evento que describes se repite nuevamente con el ejemplo propuesto, te sugiero revisar la instalación de Delphi XE3 y en caso contrario la aplicación y dll en cuestión..

Espero sea útil :)

Nelson.

nlsgarcia
16-01-2014, 03:40:55
elmago00,

Revisa esta información:

In general, functions in a DLL can use any type of parameter and return any type of value. There are two exceptions to this rule:
If you plan to call the DLL from other programming languages, you should try using Windows native data types instead of Delphi-specific types. For example, to express color values, you should use integers or the Windows ColorRef type instead of the Delphi native TColor type, doing the appropriate conversions (as in the FormDLL example, described in the next section). For compatibility, you should avoid using some other Delphi types, including objects (which cannot be used by other languages) and Delphi strings (which can be replaced by PChar strings). In other words, every Windows development environment must support the basic types of the API, and if you stick to them, your DLL will be usable with other development environments. Also, Delphi file variables (text files and binary file of record) should not be passed out of DLLs, but you can use Win32 file handles.

Even if you plan to use the DLL only from a Delphi application, you cannot pass Delphi strings (and dynamic arrays) across the DLL boundary without taking some precautions. This is the case because of the way Delphi manages strings in memory—allocating, reallocating, and freeing them automatically. The solution to the problem is to include the ShareMem system unit both in the DLL and in the program using it. This unit must be included as the first unit of each of the projects. Moreover, you have to deploy the BorlndMM.DLL file (the name stands for Borland Memory Manager) along with the program and the specific library.
Tomado del link : http://etutorials.org/Programming/mastering+delphi+7/Part+II+Delphi+Object-Oriented+Architectures/Chapter+10+Libraries+and+Packages/Creating+a+DLL+in+Delphi/

En el ejemplo del DLL propuesto en el Msg #3, No se siguieron las reglas descritas anteriormente dado que funciono correctamente en Delphi 2010 y Delphi XE4 (Asumo que también funcionaría en Delphi XE3), sin embargo en Delphi 7 los ejemplos descritos no funcionan correctamente, dado que en esta versión las reglas mencionadas son válidas.

En lo personal independientemente de la versión de Delphi que se utilice siempre es preferible seguir las reglas anteriores por compatibilidad con otros lenguajes en Windows, pero si el DLL y el programa son ambos programados en Delphi 2010 o Delphi XE4 (Solo menciono estas versiones por que son las que he probado en el ejemplo, pero lo más probable es que se cumpla de Delphi 2010 a Delphi XE5), es posible hacer el desarrollo del DLL y su programa asociado como se sugiere en el mensaje anterior en lo relacionado al uso de arreglos y strings como parámetros de un DLL.

Para evitar usar como parámetros Arreglos y Strings en Delphi DLLs, estos pueden ser sustituidos por medio de tipos PChar que permitan un medio confiable de intercambio de información entre diferentes lenguajes bajo Windows.

Revisa este código:

library TestDLL2;

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;

{$R *.res}

function GetArraySim(Value : PChar) : PChar; stdcall;
var
i : Integer;
AuxStr : String;
StrList : TStringList;

begin

StrList := TStringList.Create;

if (Value = 'Integer') then
begin

Randomize;

for i := 0 to 9 do
begin
AuxStr := IntToStr(Random(1000));
StrList.Add(AuxStr);
end;

Result := PChar(StrList.CommaText);

end;

if (Value = 'Double') then
begin

Randomize;

for i := 0 to 9 do
begin
AuxStr := FormatFloat('##0.00',(Random(100)+Random));
StrList.Add(AuxStr);
end;

Result :=PChar(StrList.CommaText);

end;

if (Value = 'String') then
begin

Randomize;

for i := 0 to 9 do
begin
AuxStr := 'String-' + IntToStr(Random(1000));
StrList.Add(AuxStr);
end;

Result :=PChar(StrList.CommaText);

end;

if (Value <> 'Integer') and (Value <> 'Double') and (Value <> 'String') then
begin
MessageDlg('Error en String de Selección',mtInformation,[mbOK],0);
Result := PChar(EmptyStr);
end;

StrList.Free;

end;

exports
GetArraySim;

begin
end.

El código anterior en Delphi 7 bajo Windows 7 Professional x32, implementa un DLL que recibe un Pchar de entrada y devuelve un Pchar de salida que contiene varios items de tipo Integer, Double y String delimitados por coma, la idea es simular un arreglo de salida por medio de datos de tipo TStrinList y Pchar.

Revisa este código

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

type
TForm1 = class(TForm)
Memo1: TMemo;
Button1: TButton;
ComboBox1: TComboBox;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

function SimulaArray(Value : PChar) : PChar; stdcall; external 'TestDLL2.dll' name 'GetArraySim';

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
ComboBox1.Items.Add('Integer');
ComboBox1.Items.Add('Double');
ComboBox1.Items.Add('String');
end;

procedure TForm1.Button1Click(Sender: TObject);
var
ArrInt : Array of Integer;
ArrDbl : Array of Double;
ArrStr : Array of String;
i : Integer;
Msg : String;
StrList : TStringList;

begin

Memo1.Clear;

StrList := TStringList.Create;

if ComboBox1.Text = 'Integer' then
begin
StrList.CommaText := SimulaArray(PChar(ComboBox1.Text));
SetLength(ArrInt,StrList.Count);
for i := 0 to StrList.Count-1 do
begin
ArrInt := StrToInt(StrList.Strings[i]);
Memo1.Lines.Add(IntToStr(ArrInt[i]));
end;
end;

if ComboBox1.Text = 'Double' then
begin
StrList.CommaText := SimulaArray(PChar(ComboBox1.Text));
SetLength(ArrDbl,StrList.Count);
for i := 0 to StrList.Count-1 do
begin
ArrDbl[i] := StrToFloat(StrList.Strings[i]);
Memo1.Lines.Add(FloatToStr(ArrDbl[i]));
end;
end;

if ComboBox1.Text = 'String' then
begin
StrList.CommaText := SimulaArray(PChar(ComboBox1.Text));
SetLength(ArrStr,StrList.Count);
for i := 0 to StrList.Count-1 do
begin
ArrStr[i] := StrList.Strings[i];
Memo1.Lines.Add(ArrStr[i]);
end;
end;

StrList.Free;

end;

end.

El código anterior en Delphi 7 bajo Windows 7 Professional x32, [I]implementa el DLL (TestDLL2) que simula el arreglo de salida indicado anteriormente.

De esta forma se pueden pasar gran cantidad de datos hacia y desde el DLL por medio de datos de tipo TStrinList y Pchar, otra forma es por medio de Estructuras Tipo Record por Referencia, pero solo en los casos que amerite este tipo de datos.

Espero sea útil :)

Nelson.

elmago00
16-01-2014, 22:14:55
elmago00,

Revisa esta información:

En el ejemplo del DLL propuesto en el Msg #3, No se siguieron las reglas descritas anteriormente dado que funciono correctamente en Delphi 2010 y Delphi XE4 (Asumo que también funcionaría en Delphi XE3), sin embargo en Delphi 7 los ejemplos descritos no funcionan correctamente, dado que en esta versión las reglas mencionadas son válidas.

En lo personal independientemente de la versión de Delphi que se utilice siempre es preferible seguir las reglas anteriores por compatibilidad con otros lenguajes en Windows, pero si el DLL y el programa son ambos programados en Delphi 2010 o Delphi XE4 (Solo menciono estas versiones por que son las que he probado en el ejemplo, pero lo más probable es que se cumpla de Delphi 2010 a Delphi XE5), es posible hacer el desarrollo del DLL y su programa asociado como se sugiere en el mensaje anterior en lo relacionado al uso de arreglos y strings como parámetros de un DLL.

Para evitar usar como parámetros Arreglos y Strings en Delphi DLLs, estos pueden ser sustituidos por medio de tipos PChar que permitan un medio confiable de intercambio de información entre diferentes lenguajes bajo Windows.

Revisa este código:

library TestDLL2;

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;

{$R *.res}

function GetArraySim(Value : PChar) : PChar; stdcall;
var
i : Integer;
AuxStr : String;
StrList : TStringList;

begin

StrList := TStringList.Create;

if (Value = 'Integer') then
begin

Randomize;

for i := 0 to 9 do
begin
AuxStr := IntToStr(Random(1000));
StrList.Add(AuxStr);
end;

Result := PChar(StrList.CommaText);

end;

if (Value = 'Double') then
begin

Randomize;

for i := 0 to 9 do
begin
AuxStr := FormatFloat('##0.00',(Random(100)+Random));
StrList.Add(AuxStr);
end;

Result :=PChar(StrList.CommaText);

end;

if (Value = 'String') then
begin

Randomize;

for i := 0 to 9 do
begin
AuxStr := 'String-' + IntToStr(Random(1000));
StrList.Add(AuxStr);
end;

Result :=PChar(StrList.CommaText);

end;

if (Value <> 'Integer') and (Value <> 'Double') and (Value <> 'String') then
begin
MessageDlg('Error en String de Selección',mtInformation,[mbOK],0);
Result := PChar(EmptyStr);
end;

StrList.Free;

end;

exports
GetArraySim;

begin
end.

El código anterior en Delphi 7 bajo Windows 7 Professional x32, implementa un DLL que recibe un Pchar de entrada y devuelve un Pchar de salida que contiene varios items de tipo Integer, Double y String delimitados por coma, la idea es simular un arreglo de salida por medio de datos de tipo TStrinList y Pchar.

Revisa este código

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

type
TForm1 = class(TForm)
Memo1: TMemo;
Button1: TButton;
ComboBox1: TComboBox;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

function SimulaArray(Value : PChar) : PChar; stdcall; external 'TestDLL2.dll' name 'GetArraySim';

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
ComboBox1.Items.Add('Integer');
ComboBox1.Items.Add('Double');
ComboBox1.Items.Add('String');
end;

procedure TForm1.Button1Click(Sender: TObject);
var
ArrInt : Array of Integer;
ArrDbl : Array of Double;
ArrStr : Array of String;
i : Integer;
Msg : String;
StrList : TStringList;

begin

Memo1.Clear;

StrList := TStringList.Create;

if ComboBox1.Text = 'Integer' then
begin
StrList.CommaText := SimulaArray(PChar(ComboBox1.Text));
SetLength(ArrInt,StrList.Count);
for i := 0 to StrList.Count-1 do
begin
ArrInt := StrToInt(StrList.Strings[i]);
Memo1.Lines.Add(IntToStr(ArrInt[i]));
end;
end;

if ComboBox1.Text = 'Double' then
begin
StrList.CommaText := SimulaArray(PChar(ComboBox1.Text));
SetLength(ArrDbl,StrList.Count);
for i := 0 to StrList.Count-1 do
begin
ArrDbl[i] := StrToFloat(StrList.Strings[i]);
Memo1.Lines.Add(FloatToStr(ArrDbl[i]));
end;
end;

if ComboBox1.Text = 'String' then
begin
StrList.CommaText := SimulaArray(PChar(ComboBox1.Text));
SetLength(ArrStr,StrList.Count);
for i := 0 to StrList.Count-1 do
begin
ArrStr[i] := StrList.Strings[i];
Memo1.Lines.Add(ArrStr[i]);
end;
end;

StrList.Free;

end;

end.

El código anterior en Delphi 7 bajo Windows 7 Professional x32, [I]implementa el DLL (TestDLL2) que simula el arreglo de salida indicado anteriormente.

De esta forma se pueden pasar gran cantidad de datos hacia y desde el DLL por medio de datos de tipo TStrinList y Pchar, otra forma es por medio de Estructuras Tipo Record por Referencia, pero solo en los casos que amerite este tipo de datos.

Espero sea útil :)

Nelson.



Hermano, eres un genio! llegaste justo al problema, y todo salio al 100%, mil gracias a todos por responder.