Probar DLL de Delphi bloquea VB6 IDE
typelib (5)
He tenido mi primer intento al escribir una DLL en Delphi. Hasta aquí todo bien. Al usar un typelib, he podido pasar Widestrings desde y hacia el DLL sin dificultad.
Lo que es curioso en este momento es que estoy usando VB6 como banco de pruebas, y cada vez que ejecuto una prueba dentro del IDE, el programa se ejecuta y luego el proceso IDE desaparece repentinamente de la memoria, sin mensajes de error, nada. Si paso por el código, todo funciona bien hasta que ejecute la última línea, entonces el IDE desaparece.
Por el contrario, cuando compilo la prueba en un EXE, el programa corre hasta su final, sin mensajes de error, etc.
¿Alguien ha tenido este problema antes y hay una solución obvia que me está mirando a la cara?
Código fuente a continuación, en caso de que importe:
- proyecto
library BOSLAD;
uses
ShareMem,
SysUtils,
Classes,
BOSLADCode in ''BOSLADCode.pas'';
exports
version,
DMesg,
foo;
{$R *.res}
begin
end.
- unidad
unit BOSLADCode;
interface
function version() : Double; stdcall;
procedure DMesg(sText : WideString; sHead : WideString ); stdcall;
function foo() : PWideString; stdcall;
implementation
uses Windows;
function version() : Double;
var
s : String;
begin
result := 0.001;
end;
procedure DMesg( sText : WideString; sHead : WideString);
begin
Windows.MessageBoxW(0, PWideChar(sText), PWideChar(sHead), 0);
end;
function foo() : PWideString;
var s : WideString;
begin
s := ''My dog''''s got fleas'';
result := PWideString(s);
end;
end.
- typelib
// This is the type library for BOSLAD.dll
[
// Use GUIDGEN.EXE to create the UUID that uniquely identifies
// this library on the user''s system. NOTE: This must be done!!
uuid(0C55D7DA-0840-40c0-B77C-DC72BE9D109E),
// This helpstring defines how the library will appear in the
// References dialog of VB.
helpstring("BOSLAD TypeLib"),
// Assume standard English locale.
lcid(0x0409),
// Assign a version number to keep track of changes.
version(1.0)
]
library BOSLAD
{
// Now define the module that will "declare" your C functions.
[
helpstring("Functions in BOSLAD.DLL"),
version(1.0),
// Give the name of your DLL here.
dllname("BOSLAD.dll")
]
module BOSLADFunctions
{
[helpstring("version"), entry("version")] void __stdcall version( [out,retval] double* res );
[helpstring("DMesg"), entry("DMesg")] void __stdcall DMesg( [in] BSTR msg, [in] BSTR head );
[helpstring("foo"), entry("foo")] void __stdcall foo( [out,retval] BSTR* msg );
} // End of Module
}; // End of Library
Moví la declaración de WideString fuera de la función en la que lo había declarado, con la expectativa de que eso aumentaría la vida útil de la variable a más tiempo que solo la duración de la función foo
. No hizo ninguna diferencia en absoluto.
Del mismo modo comenté fuera de la VB6 la llamada a la función foo
. Eso tampoco hizo ninguna diferencia. No importa lo que haga, VB6 IDE muere después de que se ejecuta la última línea de código.
La causa es algo aparte de los indicadores de las variables locales. ¿Pero que?
Para explicar la respuesta de GSerg:
result := PWideString(s);
uno pensaría que estaría bien porque s se inicializó con un literal de cadena ... pero las cadenas anchas en Delphi no son contadas como cadenas normales, por lo que en realidad contienen un poco de memoria dinámica asignada, y tan pronto como la función devuelve esta memoria se puede reutilizar :(
Lo siguiente debería estar bien, aunque:
function foo() : PWideString;
const s : WideString = ''My dog''''s got fleas'';
begin
result := PWideString(s);
end;
Crear archivos DLL en el sitio delphi.wikia.com tiene la respuesta que estaba buscando. Y la solución también.
Por ejemplo, Delphi asigna y libera automáticamente la memoria para almacenar sus cadenas, sabe cuándo ya no se necesitan, etc. Lo mismo se aplica, por ejemplo, a Visual Basic, pero ambos lo hacen de diferentes maneras. Por lo tanto, si transfiriera una cadena asignada por Visual Basic a un archivo DLL escrito en Delphi, terminaría en un gran problema, ya que ambos intentarían administrar la cadena y se pelearían entre ellos.
¡La solución es usar FastMM y funciona de manera brillante! Ahora tengo un BORLNDMM.DLL
reemplazo con mi proyecto y todo funciona.
result := PWideString(s);
Está devolviendo el puntero a una variable local aquí. Inmediatamente se vuelve inválido.
Acabo de enderezarlo por completo, gracias a Rob Kennedy en las noticias: comp.lang.pascal.delphi.misc
Él dijo, entre otras cosas que:
- Esta DLL no necesita ShareMem, SysUtils o Classes.
- Has tomado una WideString y le has dicho al compilador que realmente es un puntero a WideString. Estás mintiendo al compilador. No le importa, pero la persona que llama de esta función probablemente sí.
Por lo tanto, el código revisado, que funciona bien sin ShareMem (y SysUtils y Classes que fueron agregados por el Asistente para DLL como sucede) es el siguiente:
library BOSLAD;
uses
BOSLADCode in ''BOSLADCode.pas'';
exports
version,
DMesg,
foo;
{$R *.res}
begin
end.
BOSLADCode.pas:
unit BOSLADCode;
interface
function version() : Double; stdcall;
procedure DMesg(sText : PWideChar; sHead : PWideChar ); stdcall;
function foo() : PWideChar; stdcall;
implementation
uses Windows;
var s : WideString;
function version() : Double;
begin
result := 0.001;
end;
procedure DMesg( sText : PWideChar; sHead : PWideChar);
begin
Windows.MessageBoxW(0, sText, sHead, 0);
end;
function foo() : PWideChar;
begin
s := ''My dog''''s got fleas'';
result := PWideChar(s);
end;
end.
boslad.odl:
// This is the type library for BOSLAD.dll
[
uuid(0C55D7DA-0840-40c0-B77C-DC72BE9D109E),
helpstring("BOSLAD TypeLib"),
lcid(0x0409),
version(1.0)
]
library BOSLAD
{
[
helpstring("Functions in BOSLAD.DLL"),
version(1.0),
dllname("BOSLAD.dll")
]
module BOSLADFunctions
{
[helpstring("version"), entry("version")]
void __stdcall version( [out,retval] double* res );
[helpstring("DMesg"), entry("DMesg")]
void __stdcall DMesg( [in] BSTR msg, [in] BSTR head );
[helpstring("foo"), entry("foo")]
void __stdcall foo( [out,retval] BSTR* msg );
}
};
test.bas:
Sub Main()
Dim cfg As New CFGProject.cfg
cfg.Load "test.cfg"
Dim s As String
s = cfg.Recall("msg")
DMesg s, "" & version
s = foo
DMesg s, "" & version
End Sub
test.cfg
msg=毅訜訝
Todo eso funciona perfectamente. IDE de VB6 ejecuta felizmente el DLL y los MsgBoxs aparecen con todo como deberían.
Creo que podemos cerrar este. El código siguiente parece ser suficiente para mantener al público news:comp.lang.pascal.delphi.misc
feliz y realmente necesito pasar de la prueba conceptual a hacer algo con ella.
BOSLAD.bdsproj:
library BOSLAD;
uses
BOSLADCode in ''BOSLADCode.pas'';
exports
version,
DMesg,
foo;
{$R *.res}
begin
end.
BOSLADCode.pas:
unit BOSLADCode;
interface
function version() : Double; stdcall;
procedure DMesg(const sText : WideString; const sHead : WideString ); stdcall;
function foo() : PWideChar; stdcall;
implementation
uses Windows, ActiveX;
function version() : Double;
begin
result := 0.001;
end;
procedure DMesg( const sText : WideString; const sHead : WideString);
begin
Windows.MessageBoxW(0, PWideChar(sText), PWideChar(sHead), 0);
end;
function foo() : PWideChar;
var s : WideString;
begin
s := ''My dog''''s got fleas'';
result := SysAllocString(PWideChar(s));
end;
end.
Ahora VB está contento y no tengo bloqueos IDE raros.