CallDLL, with parameters!

Blitz3D Forums/Blitz3D Programming/CallDLL, with parameters!

ChrML(Posted 2003) [#1]
I've coded this DLL in Delphi:
library dll;

uses
  SysUtils,Classes,Dialogs;

function ShowFileDialog(title,inidir:string):string; stdcall;
var
  dlg: topendialog;
begin
  dlg := topendialog.Create(nil);
  dlg.Title := title;
  dlg.InitialDir := inidir;

  if dlg.Execute then
  begin
    ShowFileDialog := dlg.FileName;
  end;
end;

exports
  ShowFileDialog;

end.


How can I set those "title", and "indir" parameters for that function, and get the string returned back into a Blitz variable?


ChrML(Posted 2003) [#2]
Someone knows?


ChrML(Posted 2003) [#3]
Ok, forget the main question. I have that working now, with userlibs, but now I get an another problem. The string returned by my Delphi dll crashes the project when it runs. The function works right, until the dll returns the Delphi string. Then it crashes. Any ideas?


poopla(Posted 2003) [#4]
I remember reading something about the string needing to be a certain format... I can't remember though.


ChrML(Posted 2003) [#5]
Yeah, they have to be static, but idunno how do to that in Delphi :(


BlitzSupport(Posted 2003) [#6]
Hmm, try making the string in Delphi a global -- apparently that's the fix for PureBasic, so maybe it'll work for Delphi too... something to do with stacks and... um... bits.


BlitzSupport(Posted 2003) [#7]
Doh! Just saw the last post. No idea how you declare a global/static in Delphi though...


ChrML(Posted 2003) [#8]
In Delphi, the return strings are the same as the function names, which means, if the function is named MyFunction, and has the return type "string", then I have to set a variable named MyFunction to the string I want to return, so they can't be globally declared without getting the error: "Identifier redeclared".

Here are two dialogs with compiler settings for the DLL. Should anything here be checked/unchecked?:





ChrML(Posted 2003) [#9]
Btw, static means nonchaning, but how would a variable that can't change be used to return a result of a function?
Constants are statics in Delphi I believe, and constants can't change.


Sweenie(Posted 2003) [#10]
Try creating a function that only returns a string.
That is, comment out the dialog code just to make sure that it isn't that part that causes the crash.


ChrML(Posted 2003) [#11]
I tried that. When it just returns the string, it crashes, but when the dialog is there, then it shows the dialog, and then crashes when it's about to return the string. I tried to return an integer instead, but then it crashes before the dialog. I've also tried ShortString, WideString, and AnsiString, but same result. Any idea?


Koriolis(Posted 2003) [#12]
Btw, static means nonchaning, but how would a variable that can't change be used to return a result of a function?
Supposing this is really what causes your problem, what you have to do is have a global string variable that stores the return value. So, instead of directly returning the string, store the (string) result in this global variable, and then return this global variable. This is needed because if you directly return the string (supposing this is a simple pointer to the array of characters), then you must understand that being a local variable, the memory (containing the characters) this pointer points to doesn't exist anymore once the funciton as exited, so basically the one who called the DLL function (your blitz application) will try to access an unallocated memory block -> crash. On the contrary if your string is global, it will stay alive after the DLL function exits (the time needed for blitz to copy and convert it into its own internal string format).


Also I don't really know Delphi, but aren't strings handled differently thatn in C? I believe they are encoded with the length first, and then the characters. The string format needed to pass from/to uselib DLLs is the ASCIIZ format (or the "C string" format if you prefer): a string is in this case a simple pointer to the first character of an array of characters that is terminated by 0. Isn't that what is called PChar in Delphi (I think ther are also lib functions to convert back and forth, handling the terminal 0)? Sorry if I'm all wrong, I'm not a specialist at Delphi, not found of it.


ChrML(Posted 2003) [#13]
Ok, thanks for all help, I tried the following:
I tried to set the returntype to PChar, but then the DLL crashes in Blitz even before it tries to return. I also tried your other tip: Assign the value to a global before I return it, but then it crashed in the same way. I also tried to have the global as a PChar, but then it also crashed.

Maybe Blitz is incomatible with Delphi DLL's?


Floyd(Posted 2003) [#14]
I can't really help with Delphi. But I want to emphasize exactly what Blitz is doing with strings.

The DLL must handle these as C-style strings, even though these are not what Blitz uses internally.

The C-style string is just a sequences of bytes, followed by a zero byte which marks the end of the string.

When you pass a string to a DLL function Blitz builds a C-style version of the string and passes the address of the first character.

Your DLL must handle this string appropriately, as an AsciiZ string. I don't know what that is in Delphi.
You build the return string in the correct, zero-terminated form.
The value you return to Blitz must be the address of the first character of this string.

Finally, Blitz automatically reads the data at this address, up to the terminating zero.
It then builds the corresponding Blitz string.

The bottom line is that Blitz communicates string information to and from the DLL the same way that C does.
If you can find documentation on writing a Delphi DLL for use with C then the same technique should work for Blitz.


Koriolis(Posted 2003) [#15]
Maybe Blitz is incomatible with Delphi DLL's?

No, it's not. Another thing also, do you use the right calling convention? You MUST use _stdcall, or else it won't work (common effect being the stack is not cleaned up, and you get a stack overflow).
If you still don't find the problem, you really should post your code, or at least the declaration and definition of the offending function.


ChrML(Posted 2003) [#16]
Ok, I do use stdcall in my code. Here is my current code in Delphi for the DLL:
library dll {$J+};

uses
  SysUtils,Classes,Dialogs;

var
  toreturn: string;

function ShowFileDialog(title:string;inidir:string):string ;stdcall;
var
  dlg: topendialog;
begin
  dlg := topendialog.Create(nil);
  dlg.Title := title;
  dlg.InitialDir := inidir;

  if dlg.Execute then
  begin
    toreturn := dlg.FileName;
    Result := toreturn;
  end;
end;

exports
  ShowFileDialog;

end.


Here's my .decls file:
.lib "dll.dll"

ShowFileDialog$(title$,inidir$)


Here's the blitz code:
filename=ShowFileDialog("Open a file","D:")
Print(filename)

WaitKey()


Hope anyone can see the problem out of that.


Perturbatio(Posted 2003) [#17]
I believe when you return a string with a delphi dll, you need to use sharemem as well and deploy it along with BORLNDMM.DLL. Either that or return a PChar or ShortString which bypasses the whole issue.


ChrML(Posted 2003) [#18]
Ok, I'll try that tommorrow. Thanks for all help. Btw, Perturbation, is it possible to run Mandrake Linux 9, and Windows 2000/XP on the same partition? I just wonder, cuz I see you have both in your signature.


Perturbatio(Posted 2003) [#19]
Same partition, no, same drive, yes, in theory, but it's a lot easier to have two seperate drives.