Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Returning PChar from a DLL

1,027 views
Skip to first unread message

Daniel / Tecnobyte

unread,
Jun 7, 2001, 2:45:24 PM6/7/01
to
In a DLL I created a procedure:

procedure GetMsg(var S: string); stdcall;
const
cMsg = ' Test message';
begin
S := cMsg;
end;

I want to alter the procedure above and to use PChar instead of string. How
to do?


Peter Below (TeamB)

unread,
Jun 7, 2001, 5:03:12 PM6/7/01
to

Do it like the API does it for functions like GetWIndowsDirectory. The caller
of the function has to provide the memory for the buffer the function is to
copy the characters into. It has to tell the function how large the buffer is
(so the function can guard against overwriting the buffers end) and it can
call the function with the buffer size set to 0 to ask how large the buffer
needs to be.

Function GetMsg( pBuf: PChar; var bufsize: DWORD ): LongBool; stdcall;
const
cMSg = ' Test message';
Begin
If not Assigned(pBuf) or (bufsize <= Length(cMsg)) Then Begin
result := false;
bufsize := Length( cMsg ) + 1 ; // + 1 for #0 terminator
End
Else Begin
result := true;
StrPCopy( pBuf, cMsg );
End
End;


Peter Below (TeamB) 10011...@compuserve.com)
No e-mail responses, please, unless explicitly requested!
Note: I'm unable to visit the newsgroups every day at the moment,
so be patient if you don't get a reply immediately.

Paul Tomkins

unread,
Jun 14, 2001, 8:26:52 AM6/14/01
to
Hi,

Another closely related question. I have a routine in a dll that accepts a
PChar as a parameter instead of a String to avoid needing to ship
Borlndmm.dll . However, inside the routine I need to convert that PChar to a
String as the ClassName for CreateOleObject (ClassName : String) . I've
tried assigning a String variable to the PChar variable. I've tried putting
the PChar variable as the ClassName. All I get are Access Violations. Any
clues would be very welcome.

Thanks

Paul

"Peter Below (TeamB)" <10011...@compuXXserve.com> wrote in message
news:VA.0000720...@antispam.compuserve.com...

Peter Below (TeamB)

unread,
Jun 14, 2001, 9:11:25 AM6/14/01
to
In article <3b28ad94_1@dnews>, Paul Tomkins wrote:
> Another closely related question. I have a routine in a dll that accepts a
> PChar as a parameter instead of a String to avoid needing to ship
> Borlndmm.dll . However, inside the routine I need to convert that PChar to a
> String as the ClassName for CreateOleObject (ClassName : String) . I've
> tried assigning a String variable to the PChar variable.

That is obviously the wrong way around. Since a Pchar can be assigned to a
String variable you can directly use it as parameters for CreateOLEObject.
If this blows it simply means that your PChar is Nil or points at some random
garbage.

Barry Kelly

unread,
Jun 14, 2001, 9:04:04 AM6/14/01
to
In article <3b28ad94_1@dnews>
"Paul Tomkins" <p.to...@clickout.co.uk> wrote:

> Another closely related question. I have a routine in a dll that accepts a
> PChar as a parameter instead of a String to avoid needing to ship
> Borlndmm.dll . However, inside the routine I need to convert that PChar to a
> String as the ClassName for CreateOleObject (ClassName : String) . I've
> tried assigning a String variable to the PChar variable. I've tried putting
> the PChar variable as the ClassName. All I get are Access Violations. Any
> clues would be very welcome.

You should post your declaration in the DLL, your declaration in the
calling app, the pertinent piece of code that does the calling, the
pertinent piece of code that gets the data on the DLL side, all into a
new message on a new thread.

-- Barry

Jonas Erlandsson

unread,
Jun 14, 2001, 9:22:42 AM6/14/01
to
> That is obviously the wrong way around. Since a Pchar can be assigned to a
> String variable you can directly use it as parameters for CreateOLEObject.
> If this blows it simply means that your PChar is Nil or points at some
random
> garbage.

I have had the same problem (Delphi 4) and I cant assign a PChar to any
String value ... Ever .. But it is solved by using:
SomeProcThatWantsString(String(PCharVar));


Pete Harris

unread,
Jun 14, 2001, 11:01:40 AM6/14/01
to
It's high time someone wrote the definitive work on pChars.

They are obviously driving a lot of people nuts.

Barry Kelly

unread,
Jun 14, 2001, 11:12:46 AM6/14/01
to
In article <3B28D1D4...@excite.com>
Pete Harris <pet...@excite.com> wrote:

> It's high time someone wrote the definitive work on pChars.

They came from C, mainly. Strings, by their nature (dynamically
resizing) with shared ownership implies that some scheme must be
devised to pass them safely.

A simple and effective method is to allocate on the client (app) side
and pass in the PChar & Length. On the server (DLL), copy into the
PChar at most Length characters, return the actual length. Then, on
the client side, check that the actual length isn't greater than the
passed in Length. If it was, it means you chopped off the string - so
reallocate and re-pass in the new PChar and new length, so that the
full string is copied.

This little protocol is fairly easy to follow, once you get the hang
of it. However, it isn't universally applicable, since different DLL
authors might have different ideas about how best to do this stuff.
C'est la vie.

-- Barry

--
One must sometimes choose between expressiveness, safety, and
performance. But a scarcity of one isn't always excused by an
abundance of another. - Thant Tessman
Team JEDI: http://www.delphi-jedi.org
NNQ - Quoting Style in Newsgroup Postings
http://web.infoave.net/~dcalhoun/nnq/nquote.html

Paul Tomkins

unread,
Jun 14, 2001, 11:59:46 AM6/14/01
to
Hi again,

I'm embarrassed to say that I've been hacking away trying to solve the
problem. Went out for that proverbial walk. Came back and now can't
reproduce the problem. Despite the best part of the last hour trying to make
my code fail. When you're newish to Pascal writing workable code is the
challenge! So, it's quite entertaining to realise you're actually TRYING to
write fallible code.

I noticed Jonas had a similar problem. If I found out what I did then I'll
post an article for others to learn from.

Thanks

Paul

"Barry Kelly" <dyn...@eircom.net> wrote in message
news:qskhit87b28hrqqd8...@4ax.com...

Barry Kelly

unread,
Jun 14, 2001, 12:20:26 PM6/14/01
to
In article <3b28df57_2@dnews>
"Paul Tomkins" <p.to...@clickout.co.uk> wrote:

> Hi again,

Hi. Please read this, thanks.

> > NNQ - Quoting Style in Newsgroup Postings
> > http://web.infoave.net/~dcalhoun/nnq/nquote.html

-- Barry

Pete Harris

unread,
Jun 14, 2001, 1:33:07 PM6/14/01
to
Barry, I like the direction you're headed.

Maybe somebody could provide specific code snippets that cover the
following situation:

(1) You have a Delphi DLL function that accepts, modifies, then returns
a pChar to a calling application. The modifications may or may not
change the length of the string.

(2) Inside the DLL function, you convert the pChar to AnsiString for the
modifications, then convert it back again for the return.

(3) Three calling applications are used, written in VB, C++ and Delphi.
The Delphi application converts from AnsiString to pChar before calling
the DLL function, then converts back again.

(4) You might be using any version of Delphi from 3 through 6.

I think code for this would answer 95% of the questions on here about
passing strings to DLLs.

Knoppen

unread,
Jun 14, 2001, 9:41:36 PM6/14/01
to
HI

Hope this help will help? Use StrPas for conversion from C pointer to a
Pascal String and StrPCopy for convertion from Pascal String to C String

function extWriteStrToRegistry(
const xLocalkey: PCHAR;
const xField: PCHAR;
const CStr: PCHAR ): LongBool;
var
K, F, S: String;
begin
S := StrPas( CStr );
K := StrPas( xLocalKey );
F := StrPas( xField );
Result := WriteToRegistry( HKEY_LOCAL_MACHINE,K,F,S );
end;

//
// For this function the client must allocatte the array for CStr
//
function extGetStrFromRegistry(
const xLocalkey: PCHAR;
const xField: PCHAR;
CStr: PCHAR): PCHAR;
var
K, F, S, V: String;
begin
S := StrPas( CStr );
K := StrPas( xLocalKey );
F := StrPas( xField );
V := GetFromRegistry( HKEY_LOCAL_MACHINE, K, F, S );
StrPCopy( CStr, V );
Result := CStr;
end;


// GetFromRegistry and ReadFromRegistry are function defined in the same
DLL.


This sode is compiled into a DLL that is used from a VC++ 6.0 application.
I couldn't use the MFC library due to problems with different lib files and
a
third part lib file from Fraunhofer.

NOTE the LongBools. A C boolean is often an int (4 Bytes as LongBool)

--
Lars Rosenberg
CEO of Rosoft Engineering, Karlstad - Sweden
Email: La...@RosoftEngineering.com
Homepage: http://www.RosoftEngineering.com/

------------------------------------------------
This email is subject to copyright and is intended only for the person(s)
named. You may not disclose the contents of this email to other person(s)
or take copies of it without the permission of the author.
"Pete Harris" <pet...@excite.com> wrote in message
news:3B28F553...@excite.com...

Barry Kelly

unread,
Jun 15, 2001, 9:55:19 AM6/15/01
to
In article <3B28F553...@excite.com>
Pete Harris <pet...@excite.com> wrote:

> Barry, I like the direction you're headed.
>
> Maybe somebody could provide specific code snippets that cover the
> following situation:
>
> (1) You have a Delphi DLL function that accepts, modifies, then returns
> a pChar to a calling application. The modifications may or may not
> change the length of the string.

Because you are modifying the string, it becomes both an input
parameter and an output parameter. This complicates matters, and makes
the code less clear than if it were just an output parameter.

Input parameters are, of course, simple since just a pointer to the
start of the string need be passed in (a simple PChar), length not
being necessary.

> (2) Inside the DLL function, you convert the pChar to AnsiString for the
> modifications, then convert it back again for the return.
>
> (3) Three calling applications are used, written in VB, C++ and Delphi.
> The Delphi application converts from AnsiString to pChar before calling
> the DLL function, then converts back again.
>
> (4) You might be using any version of Delphi from 3 through 6.

It is pretty much the same convention; the buffer must be preallocated
by the caller, and if it isn't big enough, then we must return the
proper size.

[DLL]
function Modify(buf: PChar; bufSize: Integer): Integer; stdcall;
var
s: string;
begin
s := buf; // stops at #0 (null terminator)
// so it doesn't include whole string (i.e. won't include
// slack at end of string for modification space)

// modify string here

Result := Length(s);
StrPLCopy(buf, Result, bufSize);
end;

[Client (Delphi)]
function Modify(buf: PChar; bufSize: Integer): Integer; stdcall;
external 'mydll.dll';

procedure CallModify(const s: string): string;
const
ExpectedLength = 300;
var
len, newLen: Integer;
buf: string;
begin
buf := s + #0;
Result := s + #0;
if Length(buf) > ExpectedLength then
len := Length(buf)
else
len := ExpectedLength;

SetLength(buf, ExpectedLength);

Result := buf;
UniqueString(Result); // need to make string unique, because the
// string will be modified behind Delphi's back

newLen := Modify(PChar(@Result[1]), len);
if newLen > len then
begin
SetLength(buf, newLen);
Result := buf;
UniqueString(Result);
Modify(PChar(@Result[1]), newLen); // note: I'm not checking
// the returned length here, because I'm assuming an invariant
// function (i.e. same output for same input)
end else
SetLength(Result, newLen);
end;

HTH,

Pete Harris

unread,
Jun 18, 2001, 11:33:45 AM6/18/01
to
Barry:

Thanks for rising to the bait! It's too bad that so much code is needed
to do something so simple.

Now, how would you call your Modify function from a VB app?

I believe

Dim S As String * <N>

allocates <N> chars of memory to the string, but pads it with spaces.

Barry Kelly

unread,
Jun 19, 2001, 6:06:56 AM6/19/01
to
In article <3B2E1F59...@excite.com>
Pete Harris <pet...@excite.com> wrote:

> Now, how would you call your Modify function from a VB app?

I don't use VB, sorry.

Manuel Algora

unread,
Jun 21, 2001, 10:01:37 AM6/21/01
to
On Mon, 18 Jun 2001 08:33:45 -0700, Pete Harris <pet...@excite.com>
wrote:

>Now, how would you call your Modify function from a VB app?

To use PChars, or any kind of buffer, as parameters to DLL functions
under Vb, you need declare them as Strings ByVal.

Declare Function (ByVal SomeBuffer as String...) ...

Against common sense, the string (acting as a buffer) is passed by
reference, and thus the DLL function gets its address.

Manuel Algora
m...@encomix.es

Pete Harris

unread,
Jun 21, 2001, 1:59:31 PM6/21/01
to
OK so far, but some dirty details to take care of, like...

If the VB string buffer is declared as a "String * <Size>" (to allocate
memory), you have to watch out for...

1. The extra nul returned at the end of a pChar; and

2. The padding used by VB to fill the buffer (spaces or nulls, I
forget).

0 new messages