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

Delphi DLL & VBA - passing strings as parameters

638 views
Skip to first unread message

Erik Turner

unread,
May 2, 1999, 3:00:00 AM5/2/99
to
I'm not familiar with VBA but the safest string type is PChar (null-
terminated pointer to character). I'm sure that when you prototype
the DLL function from VBA you can specify the C-style string format
(many Windows system calls require this format).

Pascal DLL side :

procedure ProcessString(SrcStr: PChar; DestStr: PChar);
// note that the caller must allocate storage for DestStr
for P := 0 to StrLen(SrcStr) do
DestStr[P] := UpCase(SrcStr[P]);

Pascal application side :

var
DestStr: Array[0..128] of Char;

ProcessString('AbcDef',DestStr);


VBA application side :

???

Erik Turner
Microgistics, Inc


Stuart Wheeldon wrote in message <7gesgq$sk...@forums.borland.com>...
>I have tried unsuccessfully to call a function in a Delphi dll which is
>passed a string and returns a string, from an Access97 VBA module.
>
>I would appreciate any advice or a pointer to any information on how this
is
>done, ie what types of strings can be passed in VBA (byref, byval etc) and
>what type of strings are declared in the Delphi DLL.
>
>At the moment Access causes a GPF and closes when I call the DLL.
>
>Regards and thanks in advance
>
>Stuart Wheeldon
>
>

Peter Below (TeamB)

unread,
May 2, 1999, 3:00:00 AM5/2/99
to
In article <7gesgq$sk...@forums.borland.com>, Stuart Wheeldon wrote:
> I have tried unsuccessfully to call a function in a Delphi dll which is
> passed a string and returns a string, from an Access97 VBA module.
>
> I would appreciate any advice or a pointer to any information on how this is
> done, ie what types of strings can be passed in VBA (byref, byval etc) and
> what type of strings are declared in the Delphi DLL.
>
Passing VB strings to Delphi DLL functions:

the rules are:

declare the parameter as of type Pchar on the Delphi side (without Var!)
declare it as ByVal SomeString$ on the VB side
make sure the string is allocated to the required length on the VB
side, either by filling it with some characters or by declaring it as a
fixed length string.
pass the maximal length of the string to the DLL function so it can
guard against overwriting. Use functions like StrLCopy, StrLCat or
StrPLCopy to copy data into the passed PChar.
do *not* return a PChar as function result, VB cannot process it!

Just because something is called the same (String) in two languages does
not mean that the implementations are in any way compatible <g>.

The safest way is to use the same "string" type used by the Windows API:
zero-terminated strings, aka PChar. Any language that can call API
functions will have a method to pass its own string type as a PChar to a
DLL function. In VB this is accomplished by declaring a parameters as
ByVal somename As String. Delphi supports PChars directly, of course,
for read access they can be assigned directly to Delphi Strings.

Do not export functions from your DLL that return PChars. You will note
that the API contains no functions of this type, for a good reason (who
owns the returned pointer and has to free it?). If a DLL routine needs
to return data as a zero-terminated string it has to receive a
preallocated buffer (specifically: a pointer to such a buffer) from the
caller, hopefully together with information on the buffers size. A
Delphi routine would use StrLCOpy or StrPLCopy to copy data into such a
buffer. If the data can vary widely in size there should be a way for
the application to ask the DLL what size the buffer needs to be (can be
a separate function, can be handled by returning the required size if
the buffer is too small). VB allocates the buffer as a fixed-length
string variable or by prefilling a dynamic string with some dummy
character to the required size.

Peter Below (TeamB) 10011...@compuserve.com)
No replies in private e-mail, please, unless explicitly requested!


Stuart Wheeldon

unread,
May 3, 1999, 3:00:00 AM5/3/99
to

Peter

Thank you for a most comprehensive reply.
I am sure that anyone browsing this newsgroup would want to download your
message and file it for future use.

Keep up the good work.

Regards
Stuart Wheeldon

Stuart Wheeldon

unread,
May 3, 1999, 3:00:00 AM5/3/99
to
Erik

Thank you for your reply. Peter's most comprehensive reply has, I think,
covered all the bases.

Regards
Stuart Wheeldon

Sven Pran

unread,
May 3, 1999, 3:00:00 AM5/3/99
to
This is a very good description which in my opinion only calls for one small
additional information: The Delphi DLL routine can, and should determine
the size of the submitted string buffer using the standard function:
StrLen(s)

Note that the characters in the string will have indexes ranging from 0
(zero)
to one less than the StrLen value, and the DLL routine should not attempt
to access or modify characters outside this range.

regards Sven

Peter Below (TeamB) <10011...@compuXXserve.com> wrote in message
news:VA.00002cf9.00c44039@petersnewbox...

Peter Below (TeamB)

unread,
May 3, 1999, 3:00:00 AM5/3/99
to
In article <7gjl21$3k...@forums.borland.com>, Sven Pran wrote:
> This is a very good description which in my opinion only calls for one small
> additional information: The Delphi DLL routine can, and should determine
> the size of the submitted string buffer using the standard function:
> StrLen(s)
>

This may not work on fixed strings. I don't know enough about VB but i think
there is no guarantee that a string*n will be zero-terminated if it has not
been initialized. Thats why i recommend passing the buffer size explicitely in
a separate parameter.

Sven Pran

unread,
May 4, 1999, 3:00:00 AM5/4/99
to
I may be wrong, but the way I understand the documentation is that the
only information passed with PChar is a pointer, and the actual value of the
string is the collection of character bytes starting at the pointed to
address
and proceeding till the first zero byte.
This implies for instance that the Delphi statement s := s + ' '; may very
well
result in a "new" string "s" located at a completely different address than
the original string "s". (The possible consequences of such activities are
far
too complicated for me to dig into here.)

Thus, a PChar string is always initialized to something, maybe rubbish,
maybe
an empty string, maybe a zillion characters long string. But whatever it is,
the
DLL must restrict itself to operate on just the actual string (as delimited
by
the zero byte) and nothing else.

(And it is obviously importatant that if you use a static array of
characters and
build your PChar string yourself as shown somewhere in the documentation,
adding the terminating zero byte must NOT be forgotten!)

I have found PChar very interesting, and very dangerous if you start
fiddeling
with the actual implementations.

regards Sven

Peter Below (TeamB) <10011...@compuXXserve.com> wrote in message

news:VA.00002d0d.00a5a5a9@petersnewbox...

Peter Below (TeamB)

unread,
May 4, 1999, 3:00:00 AM5/4/99
to
In article <7gm465$63...@forums.borland.com>, Sven Pran wrote:
> Thus, a PChar string is always initialized to something, maybe rubbish,
> maybe an empty string, maybe a zillion characters long string. But
> whatever it is, the DLL must restrict itself to operate on just the
> actual string (as delimited by the zero byte) and nothing else.

Your understanding is incomplete. A Pchar is a pointer to a memory chunk that
*by convention only* is supposed to hold an array of characters that is
terminated by a #0 character. The #0 defines the length of the "string" held
in this memory chunk but this may be smaller than the size of the memory block
that has been allocated. And depending on how the memory chunk was allocated
the complete memory chunk may contain random garbage *that may not contain a
#0* if you are unlucky! Using StrLen on such a Pchar will cause an access
violation since the StrLen code, looking for a #0, will run over the end of
the memory block. Given an arbitrary Pchar coming from external source (as is
the case if a VB app calls a Delphi DLL function) you cannot safely determine
the actual length of the memory block (only the caller knows that), using
StrLen on it may blow up if the memory has not been properly initialized.

Var // local var in a procedure/method
p, p2: PChar;

-> content of p is random garbage on entry to the procedure, any
attempt to read or write to p^ will likely blow up

p:= StrAlloc( 51 );
-> p points to 51 bytes of memory which are uninitialized and
most likely contain random garbage. Using StrLen on this
will likely blow up.
p:= 'Hello world';
-> we just orphaned the memory allocate with StrAlloc, p now points
to the address of the string literal 'Hello World'#0. An attempt
to write more than 11 characters to this pchar will likely blow up.
p2 := p;
-> p2 now contains the same address as p, the characters are *not*
copied (different from a String assignment).

p2 := StrAlloc( StrLen( p ) + 1 );
StrCopy(p2, p);
-> This is the proper method to copy the characters of a PChar.

PChars are treacherous, if you do not properly understand their operation (and
the operation of pointers in general) they can cause great havok in your app,
from memory leaks to access violation, stack overwrites and other
unpleasentness.

0 new messages