Board index » delphi » Bad experiences with strings and PChars calling API in D@

Bad experiences with strings and PChars calling API in D@

I have a couple of hell days, getting code to work properly that calls Win
API and other C-compatible function calls needing PChars.

1. PROBLEM WITH PCHARS
----------------------

In particular, this series of functions fails (A is a COM interface
function, in a DLL):

procedure A(Msg: PChar)

  B(Msg)
end

procedure B(Msg: PChar)

  C(Msg)
end

procedure C(Msg: PChar)

  {open a file)
  BytesToWrite := StrLen(PChar);
  FileWrite(hFile, Msg, BytesToWrite);

end;

Now, I have traced through using the de{*word*81}, to function C. I can place a
watch, and Msg shows a valid string (say, 'hello'), and BytesToWrite is
valid, (5 in this case). I test the FileWrite, and it writes the correct
number of bytes, without error. The file is closed properly. But when I
look at the file, it contains garbage characters, not the string
characters.

The following was necessary, to get it to work:

procedure C(Msg: PChar)
var
  buffer: array[0..255] of char;

  {open a file)
  StrLenCopy(buffer, Msg, 255);
  BytesToWrite := StrLen(PChar);
  FileWrite(hFile, @buffer, BytesToWrite);

end;

This works just fine.

I tried lots of other things, such as turning off optimizations, and
reassigning Msg to a local variable. Does anyone have any suggestions?

2. PROBLEMS WITH STRINGS AS PCHAR
---------------------------------

I have a structure which needs to be filled with some values and PChars
before calling a C-compatible function in a COM interface. The values I
needed were in components. So I did the following:

trec = record
  value1: Pchar;
  value2: Pchar;
{etc.}

arec.value1 := PChar(Edit1.Text);
arec.value2 := PChar(Edit2.Text);

This would not work. It would result in garbled results. To make it work, I
had to do this:

var
  string1, string2: string;

string1 := PChar(Edit1.Text);
string2 := PChar(Edit2.Text);
arec.value1 := PChar(string1) ;
arec.value2 := PChar(string2 );

Can anyone suggest what is going wrong?  I thought you were supposed to be
able to cast 'strings' as PChars?

Note that in none of the examples I have given involve the function being
called writing to the string -- they only read, expecting a null-terminated
C-string.

Any feedback would be welcome.

--
Brad Aisa <ba...@tor.hookup.net>  http://www.hookup.net/~baisa/

"The highest responsibility of philosophers is to serve as the
guardians and integrators of human knowledge."   -- Ayn Rand

 

Re:Bad experiences with strings and PChars calling API in D@


[er, it is obvious that my horrible experiences with strings in Delphi over
the last couple of days has fried my mind. :-) here is what the code snip
in the second example should have read:]

Quote
>var
>  string1, string2: string;

>string1 := PChar(Edit1.Text);
>string2 := PChar(Edit2.Text);
>arec.value1 := PChar(string1) ;
>arec.value2 := PChar(string2 );

var
  string1, string2: string;

string1 := Edit1.Text;
string2 := Edit2.Text;
arec.value1 := PChar(string1) ;
arec.value2 := PChar(string2 );

Re:Bad experiences with strings and PChars calling API in D@


Quote
ba...@tor.hookup.net (Brad Aisa) wrote:
>I have a couple of hell days, getting code to work properly that calls Win
>API and other C-compatible function calls needing PChars.
>1. PROBLEM WITH PCHARS
>----------------------
>  {open a file)
>  BytesToWrite := StrLen(PChar);
>  FileWrite(hFile, Msg, BytesToWrite);

Ah, you picked a bad procedure to use. The Online help suggests you
should not use this function, so I could say if you call it
incorrectly it is your own fault!

However, in the interests of science, FileWrite is expecting a
variable reference for the second parameter, not an address. The
compiler will take the address of the object you pass it. So Msg^
would work.

Quote
>  {open a file)
>  StrLenCopy(buffer, Msg, 255);
>  BytesToWrite := StrLen(PChar);
>  FileWrite(hFile, @buffer, BytesToWrite);
>end;
>This works just fine.

Sorry, I don't believe you! Theory suggests this is incorrect and my
D2 compiler did too.

        FileWrite(hFile, buffer, BytesToWrite);

Should work.

Quote
>2. PROBLEMS WITH STRINGS AS PCHAR
>---------------------------------
>I have a structure which needs to be filled with some values and PChars
>before calling a C-compatible function in a COM interface. The values I
>needed were in components. So I did the following:
>trec = record
>  value1: Pchar;
>  value2: Pchar;
>{etc.}
>arec.value1 := PChar(Edit1.Text);
>arec.value2 := PChar(Edit2.Text);
>This would not work. It would result in garbled results.

Well I think it should work, and it did on my simple test program.

Are you sure you're using the same D2?? I suspect the problem lies
elsewhere.

--
Bob Cousins, Software Engineer.
http://www.demon.co.uk/sirius-{*word*104}netics/

Re:Bad experiences with strings and PChars calling API in D@


Quote
ba...@tor.hookup.net (Brad Aisa) wrote:
>I have a couple of hell days, getting code to work properly that calls Win
>API and other C-compatible function calls needing PChars.
>1. PROBLEM WITH PCHARS
>----------------------
>In particular, this series of functions fails (A is a COM interface
>function, in a DLL):
>procedure A(Msg: PChar)
>  B(Msg)
>end
>procedure B(Msg: PChar)
>  C(Msg)
>end
>procedure C(Msg: PChar)
>  {open a file)
>  BytesToWrite := StrLen(PChar);
>  FileWrite(hFile, Msg, BytesToWrite);

   FileWrite(hFile, Msg^, BytesToWrite); { I think you were writing
out the bytes comprising the Msg PChar pointer itself (and whatever
followed), rather than the bytes pointed to }

Quote
>end;
>Now, I have traced through using the de{*word*81}, to function C. I can place a
>watch, and Msg shows a valid string (say, 'hello'), and BytesToWrite is
>valid, (5 in this case). I test the FileWrite, and it writes the correct
>number of bytes, without error. The file is closed properly. But when I
>look at the file, it contains garbage characters, not the string
>characters.
>The following was necessary, to get it to work:

                  ^-not really, unless my guess is wrong ;-)

Quote
>procedure C(Msg: PChar)
>var
>  buffer: array[0..255] of char;
>  {open a file)
>  StrLenCopy(buffer, Msg, 255);
>  BytesToWrite := StrLen(PChar);
>  FileWrite(hFile, @buffer, BytesToWrite);

                    ^-- @-sign probably ignored as redundant, I would
guess...

Quote
>end;
>This works just fine.
>I tried lots of other things, such as turning off optimizations, and
>reassigning Msg to a local variable. Does anyone have any suggestions?

see above, but:

Quote
>2. PROBLEMS WITH STRINGS AS PCHAR
>---------------------------------

There's a Borland bug behind this one, I believe...

Quote
>I have a structure which needs to be filled with some values and PChars
>before calling a C-compatible function in a COM interface. The values I
>needed were in components. So I did the following:
>trec = record
>  value1: Pchar;
>  value2: Pchar;
>{etc.}
>arec.value1 := PChar(Edit1.Text);
>arec.value2 := PChar(Edit2.Text);
>This would not work. It would result in garbled results. To make it work, I
>had to do this:
>var
>  string1, string2: string;
>string1 := PChar(Edit1.Text);
>string2 := PChar(Edit2.Text);

            ^^^^^--a nit, but why, going from string to string?
Just left over in copy/paste?

Quote
>arec.value1 := PChar(string1) ;
>arec.value2 := PChar(string2 );
>Can anyone suggest what is going wrong?  I thought you were supposed to be
>able to cast 'strings' as PChars?

I agree, it _should_ be ok, since you're using "D@" ;-)

On this, I think you're onto a Borland BUG, which is still present
in D2.01, according to my tests. The new strings are a complicated
business, so it's not so surprising, I guess, but ...

Quote
>Note that in none of the examples I have given involve the function being
>called writing to the string -- they only read, expecting a null-terminated
>C-string.
>Any feedback would be welcome.

I made a little test prog with two edit boxes, two buttons, and a
dummy com function using the record with the PChars, as follows:

If you push button1 you get a bad value1 string, but ok on value2
(so it's not hard & fast whether it will fail).
If you push button2 it's all ok. If you experiment with the number
of characters in edit1.text, you'll note they appear by 4 at a time,
which is suggestive. I suspect this ought to be a known bug. But if
it isn't, the below is an easy way to reproduce the problem.
If someone pushes the right button (money, pride, etc) I may go
after it, but I'm not automatically going to do it. It's getting old.
... or maybe it's me ;-/

Regards,
Bengt Richter
----------------------------------------------------------------------
{standard stuff at top for form with 2 tedits & 2 tbuttons}
implementation

{$R *.DFM}

type trec = record
  value1: Pchar;
  value2: Pchar;
  {etc.}
  end;

procedure fcom(var r:trec);
begin
  AllocConsole; // lazy debug kludge,
                //.. redundant after first time
  Writeln(r.Value1);
  Writeln(r.Value2);
end;

procedure TForm1.Button1Click(Sender: TObject);
var arec: trec;
begin
  arec.value1 := PChar(Edit1.Text);
  arec.value2 := PChar(Edit2.Text);
  fcom(arec);
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  arec:trec;
  string1, string2: string;
begin
  string1 := Edit1.Text;
  string2 := Edit2.Text;
  arec.value1 := PChar(string1);
  arec.value2 := PChar(string2);
  fcom(arec);
end;

end.
----------------------------------------------------------------------

Other Threads