Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations TouchToneTommy on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Returning a string from a DLL to a VB program

Status
Not open for further replies.

sggaunt

Programmer
Jul 4, 2001
8,620
GB

Still having a problem with this, the examples below just returns gibberish. Can anyone see what is wrong?

Code:
simplified version DLL function to return a pchar (for compatibility with VB)

function Readline: Pchar; stdcall;
var s: string;
begin
   s := 'hello world';
   result := Pchar(s);
end;

Code:
Delphi calling code based on previously posted Delphi way of doing this:
------------------
var
  Ret  : AnsiString;
  P : PChar;
begin
  SetLength(Ret, 1024); // assign space
  P := PChar(Ret);        // make a pointer to the space
  P := Readline;        // as the function returns a pointer (PChar) this should now be a pointer to the string 
  MessageBox.Text := Ret;  // display the result
end;

Steve [The sane]: Delphi a feersum engin indeed.
 
These two lines don't establish any addressability on "Ret" from "P":

Code:
SetLength(Ret, 1024); // assign space
P := PChar(Ret);        // make a pointer to the space

Therefore when the call is made to the DLL function, the value of P is *not* in Ret (both are dead code). If you access and convert P directly *after* the DLL call is made, then it should work (PCharToString I believe).
 
Glenn: Are you sure about that?

There is no PCharToString function,or anything like it well not in my version of Delphi anyway.

One of the problems here is that I am writting a DLL that will work with VB, but I must test it with Delphi.
I did a bit more Googling, yes I have done lots,
and found this Website

I modified the 'Dll passing back a pchar' example thus...

Code:
// Having a separate function to get the length is clearer than
// asking GetDLLVersion to provide that length if parameters are nil.
function GetDLLVersionLength: Integer;
begin
    Result := Length(DLLVersion + IntToStr(VersionNumber));
end;

// Returns number of characters copied, excluding zero byte
function GetDLLVersion(Buffer: PChar): Integer; stdcall;
var MaxLen: integer;
begin
   MaxLen := GetDLLVersionLength;
   if (Buffer <> nil) and (MaxLen > 1) then
      begin
        StrLCopy(Buffer, PChar(DLLVersion + IntToStr(VersionNumber)), MaxLen);
        Result := StrLen(Buffer);
      end
    else
       Result := 0;
end;

and called it from Delphi thus

Code:
procedure TForm1.GetVerBtnClick(Sender: TObject);
var P: Pchar;
    Ret: AnsiString;
begin
   SetLength(Ret,1024);
   P := PChar(ret);
   GetDLLVersion(P);
   SerialMessageBox.Text := Ret;
end;

If you do not use the lines allocating space for 'Ret' and Pointing 'P' at it then the call fails.

The above code does seem to work!! more testing will be done to make sure its not just 'landing' at the right memory location (this has happened before), but I may be on the right track?

Steve [The sane]: Delphi a feersum engin indeed.
 
Glenn: Are you sure about that?

Yes I'm sure. I took your code in post #1 and tested it before I posted it.

Here's what happened when I was looking at it:

Code:
SetLength(Ret, 1024);

Sets Ret's length to 1024. Okay there.

Code:
P := PChar(Ret);

Sets P to the value in Ret. Which means P is now 1024 bytes of whatever. If I access P now, it has the CONTENTS of Ret, not the ADDRESS. Which makes what I said true, this does not provide addressability to Ret from P.

Code:
P := Readline;

Here your comment is correct. P now holds the contents of the string in the DLL. If I were to access P and type convert it to the AnsiString, then I would have the string in the DLL.

Code:
MessageBox.Text := Ret;

This returns the contents of the original 1024 bytes.

There is no PCharToString function,or anything like it well not in my version of Delphi anyway.

I was guessing at that point, since I realized you needed a type conversion and didn't have my Delphi in front of me when I was posting it. The basic idea is there. For what I did with the code you posted, type convert P after the DLL call, you get the value that was in the DLL. Now if Visual Basic acts differently, it might be beneficial to test the DLL with Visual Basic, if Delphi does something VB doesn't (like auto-allocate memory for PChar types).
 
Thanks for that Glenn:
So what you are saying is that the comments are wrong, not that the code doesnt work, and that this is all type casting not pointer conversion?

The comments were my own added in an attempt to understand what is happening here, based on this reasoning.

If a PChar is simply a terminated array of Chars, why can't the memory allocation be done directly?
Also my understanding was that a PChar is by definition a pointer (Pointer to Char).

Further testing with Delphi seems to show that it does continue to work as required.

As for VB testing our client is doing that himself, and as it says on the web link I posted it up to the calling program to do the memory allocation in this case.
In a 'C' program this should be easier, don't know about VB

Unfortunately (or not) I have no VB experience and no access to a compiler.



Steve [The sane]: Delphi a feersum engin indeed.
 
Now given your second example, there is a question of memory allocation that comes into play with all of this. You allocate memory in the main program then:

Example #1: You assign a string value to it.
Example #2: You call StrLCopy and move the bytes over the buffer, which makes sense and should give the proper original result.

If I get the time, I'd like to do more playing around with the code than I did before, but a guess possibly is that in your first example, the allocation is due to the string value you used in the DLL more than anything innate out of PChar, the value passed back out of the DLL is a Pchar pointer to the string as loaded in the DLL.

If you statically linked the DLL (you don't tell me that anywhere), the memory it uses will remain undisturbed throughout the duration of the program. In your example #1, you pass a typecast PChar of string s back to the main program (hence the two different string spaces as I mentioned before). The test I would try is to dynamically load the DLL, run the procedure, and then free the DLL before you access your resulting value.

(I'm sure my explanations have been lacking in a lot of ways, but with the way Windows programming is anymore, a lot of stuff isn't firm in my mind. As I'm sure a lot of people. Just can only go off of what I observe)
 
In all my tests so far the DLL is static (and resides in the test executable folder).

Our client talks about 'registering the DLL in his VB environment', I am not sure why this needs to be done or what it achieves?

IMHO the whole project is too fraught, and it would have made a lot more sense to write the whole thing (Data gathering/ routing from hardware using Ethernet) in Delphi or in VB.
I was all down to our client having no experience of Ethernet programming (neither did we).









Steve [The sane]: Delphi a feersum engin indeed.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top