PDA

View Full Version : DLL String Pointer Question



NewsArchive
07-09-2010, 01:55 AM
Hi Friderich,

I've a few questions about Call DLL and String pointers.

1.) Which string encoding do you use? (ANSI, UNICODE, ...)

2.) What is the difference between *CSTRING and *STRING?

3.) How big is the buffer you allocate for *STRING and *CSTRING?

4.) How do you find the end of the returned(!) string?

Thanks for your answer.

Markus

NewsArchive
07-09-2010, 03:39 AM
Hi Markus,

The "Call Dll" script function makes use of the ANSI string encoding. The
return result is an union of standard types, ranging from a four-byte INT
(generic four-byte type) to an eight-byte DOUBLE. The difference between
*CSTRING and *STRING is that the byte width of the individual function
arguments is different (64 bit for *CSTRING and 32 bit for *STRING). The
maximum buffer size is 10,240 bytes.

Friedrich

NewsArchive
07-09-2010, 03:40 AM
Hi Friedrich,

> The
> return result is an union of standard types, ranging from a four-byte INT
> (generic four-byte type) to an eight-byte DOUBLE.

Do you mean %_SB_RETURN%?

> The difference between
> *CSTRING and *STRING is that the byte width of the individual function
> arguments is different (64 bit for *CSTRING and 32 bit for *STRING).

I'm sorry, but I need a more detailed explanation...

> The
> maximum buffer size is 10,240 bytes.

Do you ALWAYS allocate 10240 bytes, even if the given SB-variable is
empty? I ask this, because I use a SB-variable as OUT-Parameter to
receive a string result form the called DLL.

And:

4.) How do you find the end of the returned(!) string?


Thanks again.

Markus

NewsArchive
07-09-2010, 03:40 AM
Markus,

>
> Do you mean %_SB_RETURN%?
>

%_SB_RETURN% itself is "typeless". I meant the internal return types in the
underlying Call Dll method.

>> The difference between
>> *CSTRING and *STRING is that the byte width of the individual function
>> arguments is different (64 bit for *CSTRING and 32 bit for *STRING).
>
> I'm sorry, but I need a more detailed explanation...

That's the only information I can give. There is no other difference. All
the parameters are pushed directly onto the stack. So for example, a
*STRING parameter uses four bytes (32-bit) of stack space, a *CSTRING
requires eight bytes.

> Do you ALWAYS allocate 10240 bytes, even if the given SB-variable is
> empty? I ask this, because I use a SB-variable as OUT-Parameter to receive
> a string result form the called DLL.

Yes, when you use Call Dll, then we always allocate 10,240 bytes (in the
Call Dll method).

>
> 4.) How do you find the end of the returned(!) string?
>

We just adjust the stack and read the return value(s). If the return value
isn't passed through registers, memory copy is performed instead.

Friedrich

NewsArchive
07-10-2010, 02:16 AM
> %_SB_RETURN% itself is "typeless". I meant the internal return types
in the underlying Call Dll method.

Ok, that's what I meant. It seams that SB script variables are always
represented as String and you do the conversion on the fly, if it is
needed for arithmetics or call dll and so on. Right?

>> 4.) How do you find the end of the returned(!) string?
>>
>
> We just adjust the stack and read the return value(s). If the return value
> isn't passed through registers, memory copy is performed instead.
>


Perhaps it's to hot today, but I still don't understand this.

Let's have a look at the following example:

Set Variable %STRINGBUFFER% to ""
Set Variable %BUFFERSIZE% to "10240"
Call DLL "%SYS32DIR%\Advapi32.dll" -- Function "GetUserName"
(*STRING,*LONG) (%STRINGBUFFER%,%BUFFERSIZE%)
Display Message Box "«%STRINGBUFFER%»\n«%BUFFERSIZE%»" -- "Result"

If my understanding is correct, you allocate 10240 Bytes in your
internal "call dll" call for the %STRINGBUFFER%. After the internal call
returns, Advapi32 has written let's say 11 bytes to the allocated
buffer. And if I look at %STRINGBUFFER%, it has a length of 11, not
10240. How is this done? Do you truncate at the first #0 or is the
memory allocated for %STRINGBUFFER% now also 10240 bytes and I just
don't see this, because the trailing 10229 #0 bytes are ignored. Or do
you even always allocate 10240 bytes for each and every variable I use,
without regarding its contents.

I'm sorry, that I ask so much questions, but I don't feel good with "it
just works" when I don't understand, why it works.

Thanks for your patience.

Markus

NewsArchive
07-10-2010, 02:16 AM
Hi Markus,

> > %_SB_RETURN% itself is "typeless". I meant the internal return types
> in the underlying Call Dll method.
>
> Ok, that's what I meant. It seams that SB script variables are always
> represented as String and you do the conversion on the fly, if it is
> needed for arithmetics or call dll and so on. Right?

Yes, this is absolutely correct!

> Perhaps it's to hot today, but I still don't understand this.
>
> Let's have a look at the following example:
>
> Set Variable %STRINGBUFFER% to ""
> Set Variable %BUFFERSIZE% to "10240"
> Call DLL "%SYS32DIR%\Advapi32.dll" -- Function "GetUserName"
> (*STRING,*LONG) (%STRINGBUFFER%,%BUFFERSIZE%)
> Display Message Box "«%STRINGBUFFER%»\n«%BUFFERSIZE%»" -- "Result"
>
> If my understanding is correct, you allocate 10240 Bytes in your internal
> "call dll" call for the %STRINGBUFFER%. After the internal call returns,
> Advapi32 has written let's say 11 bytes to the allocated buffer. And if I
> look at %STRINGBUFFER%, it has a length of 11, not 10240. How is this
> done? Do you truncate at the first #0 or is the memory allocated for
> %STRINGBUFFER% now also 10240 bytes and I just don't see this, because the
> trailing 10229 #0 bytes are ignored. Or do you even always allocate 10240
> bytes for each and every variable I use, without regarding its contents.
>
> I'm sorry, that I ask so much questions, but I don't feel good with "it
> just works" when I don't understand, why it works.
>
> Thanks for your patience.

The second parameter in the GetUserName Windows API is an in/out parameter.
You specify the size of the buffer (*IN*), for example, 260. After the call
to that function, the value is the number of bytes copied to the buffer
(*OUT*).

Let us assume, the user name is LINDER (6 bytes). If you set %BUFFERSIZE%
to 260 (that would be large enough here), then it will return LINDER in the
%STRINGBUFFER% parameter and 7 (size including null terminating character)
in %BUFFERSIZE%. %_SB_RETURN% is 1 (success). But if you set %BUFFERSIZE%
to 6 (we need at least 7! in this case) then the function will fail and
%BUFFERSIZE% holds the required buffer size, including the terminating null
character. %_SB_RETURN% would be 0 in this case (failed).

Does this help?

Friedrich

NewsArchive
07-10-2010, 02:17 AM
> The second parameter in the GetUserName Windows API is an in/out parameter.
> You specify the size of the buffer (*IN*), for example, 260. After the call
> to that function, the value is the number of bytes copied to the buffer
> (*OUT*).
>
> Let us assume, the user name is LINDER (6 bytes). If you set %BUFFERSIZE%
> to 260 (that would be large enough here), then it will return LINDER in the
> %STRINGBUFFER% parameter and 7 (size including null terminating character)
> in %BUFFERSIZE%. %_SB_RETURN% is 1 (success). But if you set %BUFFERSIZE%
> to 6 (we need at least 7! in this case) then the function will fail and
> %BUFFERSIZE% holds the required buffer size, including the terminating null
> character. %_SB_RETURN% would be 0 in this case (failed).
>
> Does this help?
>

Da wir uns offensichtlich missverstehen schreibe ich ausnahmesweise auf
Deutsch.

Die Funktion des In/Out-Parameters ist mir klar. Aber: SB weiß ja nicht,
dass der zweite Parameter die Länge des vorangehenden Rückgabewertes
angibt. Wie also ist es möglich, dass %STRINGBUFFER% nachher exakt der
Länge des Rückgabewertes entspricht und nicht etwa den vollen 10240
allokierten Bytes?

Angenommen, ich habe %STRINGBUFFER% nicht leer initialisert, sondern mit
10 aufeinanderfolgenden A-Zeichen:

AAAAAAAAAA

Die Byte-Werte sind dann

#65 #65 #65 #65 #65 #65 #65 #65 #65 #65 #0

Mein %BUFFERLENGTH% setze ich auf 7 (absichtlich nicht auf die
tatsächlich zuverfügung gestellten 10240, um mein verständnisproblem
weiter unter besser erklären zu können), so dass es gerade für den Wert
"LINDER" mit abschließendem #0 reicht.

SB allokiert für *STRING 10k Speicherplatz, dies resultiert dann in 10
Bytes mit dem Wert #65 und 10230 Bytes mit dem Wert #0, sofern man davon
ausgeht, dass SB den allokierten Speicher mit #0 auffüllt.

Nun wird die DLL-Funktion aufgerufen. Aufgrund der (absichtlich) falsch
angegebenen Buffer-Länge glaubt die DLL nun, der Buffer wäre nur 7 Byte
groß. Sie schreibt Ihre Antowrt also in die ersten 7 Byte des
Übergebenen Buffers.

Der Speicher sieht nun wie folgt aus (ASCII-Darstellung):

L I N D E R #0 A A A #0 und weitere 10229 mal #0

Wenn ich nun, nach dem die Scriptfunktion CALL DLL zurückgekehrt ist,
die Variable %STRINGBUFFER% ausgebe, erhalte ich "LINDER" und wenn ich
ihre Länge Abfrage, so erhalte ich 6.

Wie sieht zu diesem Zeitpunkt die Speicher-Räpresentation von
%STRINGBUFFER% aus?

Sind der Variable nach wie vor 10240 Bytes zugewiesen und es wird bloß
alles, was nach dem ersten #0 kommt ignoriert?

Ist eventuell allen Variabeln stets 10240 Bytes zugewiesen und es wird
immer alles nach dem ersten #0 ignoriert?

Sind der Variable nur noch 7 Bytes zugewiesen und SB hat die Länge
irgendwie (z.B. durch suchen des ersten Vorkommens von #0) unmittelbar
nach dem internen CALL DLL aufruf angepasst?

Ich werde weitere Tests vornehmen, um das zu verstehen (z.B. mir einen
String OHNE abschließendes #0 zurückgeben lassen und sehen, was passiert.)

In der Hoffnung, dass wir diese dunkle Ecke bald ausgeleuchtet haben.

Markus

NewsArchive
07-10-2010, 02:18 AM
Hi Markus,

I understand what you are saying. The null terminating character is the
secret here. The Windows API returns LINDER and the null terminating
character.

SetupBuilder does not (directly) search for a null terminating character or
any other "delimiter". In the Call DLL method, it always allocates 10,240
bytes. If the called function (e.g. GetUserName) returns then it copies the
retrieved return value via lstrcpy() to the "final" variable location (e.g.
%STRINGBUFFER%). This variable is dynamically allocated (using lstrlen).

Everything you have after the null terminating character is "ignored" in
this final step.

Friedrich

NewsArchive
07-10-2010, 02:18 AM
:-)

Now, it's clear.

lstrcpy() was the point I missed...

Thank you very much.

Markus

NewsArchive
07-10-2010, 02:18 AM
I've forgotten one thing:

> a *CSTRING requires eight bytes.

What ist the format of *CSTRING? 4 bytes pointer + 4 bytes length?

Markus

NewsArchive
07-10-2010, 02:19 AM
Hi Markus,

> I've forgotten one thing:
>
> > a *CSTRING requires eight bytes.
>
> What ist the format of *CSTRING? 4 bytes pointer + 4 bytes length?

It's some kind of "hidden" pointer to an appropriate amount of memory. It
pushes a hidden argument on the stack, which is a pointer to a temporary
buffer (located on the stack). The called function itself then copies the
return value to this buffer.

Friedrich

NewsArchive
07-10-2010, 02:19 AM
Is this clarion specific?

Or can you give me a "real world" example, when I should use *CSTRING?

Markus

NewsArchive
07-10-2010, 02:19 AM
Hi Markus,

> Is this clarion specific?
>
> Or can you give me a "real world" example, when I should use *CSTRING?

If you make use of Windows APIs or DLLs written in Visual Studio C/C++ then
you would not use the *CSTRING type. You would use *STRING in this case.

To be honest, I don't know if this is Clarion specific. But we need this
type if we call a Clarion DLL.

Friedrich

NewsArchive
07-10-2010, 02:20 AM
Thank you.

Markus

NewsArchive
07-15-2010, 01:39 AM
Hi Friedrich,

here are the important sentences of our conversation. It would be nice,
if you could add this to the documentation:

String Encoding
The "Call Dll" script function makes use of the ANSI string encoding.

Result Type
The internal return types in the underlying Call Dll methoda are an
union of standard types, ranging from a four-byte INT (generic four-byte
type) to an eight-byte DOUBLE. %_SB_RETURN% itself is "typeless". SB
script variables are always represented as String and SB do the
conversion on the fly, if it is needed for arithmetics or call dll and
so on.

*STRING and *CSTRING
The difference between *CSTRING and *STRING is that the byte width of
the individual function arguments is different (64 bit for *CSTRING and
32 bit for *STRING). In the Call DLL method, it always allocates 10,240
bytes. All the parameters are pushed directly onto the stack. If the
called function (e.g. GetUserName) returns then it copies the retrieved
return value via lstrcpy() to the "final" variable location (e.g.
%STRINGBUFFER%). This variable is dynamically allocated (using
lstrlen). Everything you have after the null terminating character is
"ignored" in this final step. We adjust the stack and read the return
value(s). If the return value isn't passed through registers, memory
copy is performed instead.

What ist the format of *CSTRING?
It's some kind of "hidden" pointer to an appropriate amount of memory. It
pushes a hidden argument on the stack, which is a pointer to a temporary
buffer (located on the stack). The called function itself then copies the
return value to this buffer.

When should I use *CSTRING and when *STRING?
If you make use of Windows APIs or DLLs written in Visual Studio C/C++
then you would use the *STRING type. If you call a Clarion DLL, use
*CSTRING.


Markus

NewsArchive
10-21-2010, 01:03 AM
Hi Friedrich,

this seems not to be added to the coumentation in 7.2.3062

Markus

NewsArchive
10-21-2010, 01:12 AM
Hi Markus,

>
> this seems not to be added to the coumentation in 7.2.3062
>

I don't know if there were plans to add this to the documentation. But I'll
check this.

Thanks,
Friedrich

NewsArchive
10-22-2010, 01:54 AM
Added as a FAQ topic to the "Call DLL Function" description now.

Thanks,
Friedrich

NewsArchive
10-22-2010, 02:01 AM
Thank you.

Markus Zander