Jump to content

Return a string from a dll ?


pixelsearch
 Share

Recommended Posts

Hi everybody,
The following script shows how easy it is to pass an integer parameter by Reference from AutoIt and have it modified by a Dll :

C++ code of the Dll :

extern "C" __declspec(dllexport) int test (int* inumber)
{
    *inumber = -12345;
    return 0;
}

AutoIt code :

#include <Array.au3>

$hDLL = DllOpen("test.dll")
If $hDLL = - 1 Then Exit Msgbox(0, "DllOpen", "error occured")

Local $iNumber = 6
$aRet = DllCall($hDLL, "int:cdecl", "test", "int*", $iNumber) ; <=== * after int  
If @error Then Exit Msgbox(0, "DllCall", "error " & @error)

_ArrayDisplay($aRet, "$aRet")
DllClose($hDLL)

2101742441_alterintegerindll.png.d87d96461bf8b53131f12d0769d6bbc2.png

We see how $iNumber changed from 6 (before DllCall) to -12345 (after DllCall)

I (naively) tried it for a string, thinking it could work same, with char* sstring in the dll [instead of int* inumber] and "str*", $sString in the DllCall [instead of int*, $iNumber] but nothing good happened, I couldn't return an altered string from the Dll

Then I tried it with a Structure, which soon became too complicated (though I didn't fully experiment the Structure way), then I started to write this post :)

So I would like to to ask : is there a simple way to return an altered string from a dll, in the same easy way than we just saw with the integer ?

2 more things :
* I need the "return 0" from the C++ code (which will define $aRet[0]) because it's also used if an error is found during the Dll, then it will return 1, 2 etc... in case of errors.

* The strings needing to be returned are @Jon's "text-encoding-detect" strings when everything works fine, like :
"Encoding: Binary" or
"Encoding: UTF-16 Big Endian" etc...

Because I intend to create a dll from his unaltered C++ code and post it here (if he agrees). Then, from an AutoIt script, we could choose a folder and have all files of the folder checked, with an ArrayDisplay output presented like this, for example :

File    Encoding
======  ========
File 1  Binary
File 2  UTF-16 Big Endian
...

Now I'm thinking of another way to achieve this, without returning any string from the dll.
We could, for example, use $aRet[0] for any possible Return, errors or not, let's say :

$aRet[0] = 1   : issue in the dll (Jon got some Return's 1 when it goes wrong)
$aRet[0] = 100 : this will correspond to "Encoding : Binary"
$aRet[0] = 101 : this will correspond to "Encoding : UTF-16 Big Endian"
etc...

and the match will be done directly in AutoIt (i.e. the corresponding string matching the return value) so there will be no need to return any string from the dll !

This could be doable because (gladly) Jon's function is natively called for one file only, so the Return value matches the file just checked.

But just curious and I'm re-asking : is there a simple way to return an altered string from a dll, in the same easy way than we saw with the integer ?
Thanks

Edited by pixelsearch
a silly param in AutoIt code
Link to comment
Share on other sites

The DllCall datatypes are: str for byte codepages strings (ANSI or UTF8) and wstr for Unicode  (UTF16) strings, both null-terminated.

This wonderful site allows debugging and testing regular expressions (many flavors available). An absolute must have in your bookmarks.
Another excellent RegExp tutorial. Don't forget downloading your copy of up-to-date pcretest.exe and pcregrep.exe here
RegExp tutorial: enough to get started
PCRE v8.33 regexp documentation latest available release and currently implemented in AutoIt beta.

SQLitespeed is another feature-rich premier SQLite manager (includes import/export). Well worth a try.
SQLite Expert (freeware Personal Edition or payware Pro version) is a very useful SQLite database manager.
An excellent eBook covering almost every aspect of SQLite3: a must-read for anyone doing serious work.
SQL tutorial (covers "generic" SQL, but most of it applies to SQLite as well)
A work-in-progress SQLite3 tutorial. Don't miss other LxyzTHW pages!
SQLite official website with full documentation (may be newer than the SQLite library that comes standard with AutoIt)

Link to comment
Share on other sites

1 hour ago, pixelsearch said:

and "str*", $sString in the DllCall

Hi jchd, yes this is how I scripted it, but with a "*" after "str" to pass it by reference "str*" as indicated in the help file, topic DllCall. The star gave a good result for int* but not for str*
 

$aRet = DllCall($hDLL, "int:cdecl", "test", "str*", $sString)

 

Edited by pixelsearch
Link to comment
Share on other sites

There is no * in my answer 😉

Nonetheless, there is a trap here: if you intend to "return" a string longer than what was allocated for passing in by modifying it on-place, bad things will occur.

Edited by jchd

This wonderful site allows debugging and testing regular expressions (many flavors available). An absolute must have in your bookmarks.
Another excellent RegExp tutorial. Don't forget downloading your copy of up-to-date pcretest.exe and pcregrep.exe here
RegExp tutorial: enough to get started
PCRE v8.33 regexp documentation latest available release and currently implemented in AutoIt beta.

SQLitespeed is another feature-rich premier SQLite manager (includes import/export). Well worth a try.
SQLite Expert (freeware Personal Edition or payware Pro version) is a very useful SQLite database manager.
An excellent eBook covering almost every aspect of SQLite3: a must-read for anyone doing serious work.
SQL tutorial (covers "generic" SQL, but most of it applies to SQLite as well)
A work-in-progress SQLite3 tutorial. Don't miss other LxyzTHW pages!
SQLite official website with full documentation (may be newer than the SQLite library that comes standard with AutoIt)

Link to comment
Share on other sites

Yes I noticed that lol
But how will I pass it by reference without the star ?

This is a part of the help file, DllCall :

Add * to the end of another type to pass it by reference. For example "int*" passes a pointer to an "int" type.

If I didn't add the star at the end of int, then my 1st example wouldn't work at all.

Now if you're saying that a star is possible after "int*", "struct*" but never after "str", that's something new to me. The help file should really be amended in this case... to indicate the types where a star is allowed or not (added these last words after edit)

Edited by pixelsearch
added a last sentence
Link to comment
Share on other sites

AutoIt uses Variants to hold your data, which isn't the form a C/C++ vanilla DLL expects. Fixed-size data (e.g. numeric values) cause no issue, but strings are a problem in this context, being variable-sized. DllCall does a lot of conversions in/out under your feet to insure the callee doesn't destroy AutoIt workspace memory.

This wonderful site allows debugging and testing regular expressions (many flavors available). An absolute must have in your bookmarks.
Another excellent RegExp tutorial. Don't forget downloading your copy of up-to-date pcretest.exe and pcregrep.exe here
RegExp tutorial: enough to get started
PCRE v8.33 regexp documentation latest available release and currently implemented in AutoIt beta.

SQLitespeed is another feature-rich premier SQLite manager (includes import/export). Well worth a try.
SQLite Expert (freeware Personal Edition or payware Pro version) is a very useful SQLite database manager.
An excellent eBook covering almost every aspect of SQLite3: a must-read for anyone doing serious work.
SQL tutorial (covers "generic" SQL, but most of it applies to SQLite as well)
A work-in-progress SQLite3 tutorial. Don't miss other LxyzTHW pages!
SQLite official website with full documentation (may be newer than the SQLite library that comes standard with AutoIt)

Link to comment
Share on other sites

For the very same reason it's very slow and painful to pass arrays in/out to/from C, unfortunately.

This wonderful site allows debugging and testing regular expressions (many flavors available). An absolute must have in your bookmarks.
Another excellent RegExp tutorial. Don't forget downloading your copy of up-to-date pcretest.exe and pcregrep.exe here
RegExp tutorial: enough to get started
PCRE v8.33 regexp documentation latest available release and currently implemented in AutoIt beta.

SQLitespeed is another feature-rich premier SQLite manager (includes import/export). Well worth a try.
SQLite Expert (freeware Personal Edition or payware Pro version) is a very useful SQLite database manager.
An excellent eBook covering almost every aspect of SQLite3: a must-read for anyone doing serious work.
SQL tutorial (covers "generic" SQL, but most of it applies to SQLite as well)
A work-in-progress SQLite3 tutorial. Don't miss other LxyzTHW pages!
SQLite official website with full documentation (may be newer than the SQLite library that comes standard with AutoIt)

Link to comment
Share on other sites

extern "C" __declspec(dllexport) int test2 (const char** oldstring)
{
    const char* newstring = "after DllCall";
    *oldstring = newstring;
    return 0;
}

 

#include <Array.au3>

$hDLL = DllOpen("test2.dll")
If $hDLL = - 1 Then Exit Msgbox(0, "DllOpen", "error occured")

Local $sString = "there_was_nothing"
$aRet = DllCall($hDLL, "int:cdecl", "test2", "str", $sString)
If @error Then Exit Msgbox(0, "DllCall #1", "error " & @error)
_ArrayDisplay($aRet, "$aRet without *")

Local $sString = "there_was_nothing"
$aRet = DllCall($hDLL, "int:cdecl", "test2", "str*", $sString) ; <==== * after str
If @error Then Exit Msgbox(0, "DllCall #2", "error " & @error)
_ArrayDisplay($aRet, "$aRet with *")

DllClose($hDLL)

2062184678_itworked.png.e9f386b7ef1ca203ca6a43717373736d.png

Call me stubborn :D

Link to comment
Share on other sites

Can you try with overwriting the string with a longer one?

Also, what happened to the input string? "there" turns into "$0Àfe" ???

Edited by jchd

This wonderful site allows debugging and testing regular expressions (many flavors available). An absolute must have in your bookmarks.
Another excellent RegExp tutorial. Don't forget downloading your copy of up-to-date pcretest.exe and pcregrep.exe here
RegExp tutorial: enough to get started
PCRE v8.33 regexp documentation latest available release and currently implemented in AutoIt beta.

SQLitespeed is another feature-rich premier SQLite manager (includes import/export). Well worth a try.
SQLite Expert (freeware Personal Edition or payware Pro version) is a very useful SQLite database manager.
An excellent eBook covering almost every aspect of SQLite3: a must-read for anyone doing serious work.
SQL tutorial (covers "generic" SQL, but most of it applies to SQLite as well)
A work-in-progress SQLite3 tutorial. Don't miss other LxyzTHW pages!
SQLite official website with full documentation (may be newer than the SQLite library that comes standard with AutoIt)

Link to comment
Share on other sites

45 minutes ago, jchd said:

Can you try with overwriting the string with a longer one?

sorry for the delay, was looking football on l'equipe TV

I sure can jchd, this is what shows with this AutoIt code :

Local $sString = "short"
$aRet = DllCall($hDLL, "int:cdecl", "test2", "str", $sString)
If @error Then Exit Msgbox(0, "DllCall", "error " & @error)
_ArrayDisplay($aRet, "$aRet without *")

Local $sString = "short"
$aRet = DllCall($hDLL, "int:cdecl", "test2", "str*", $sString)
If @error Then Exit Msgbox(0, "DllCall", "error " & @error)
_ArrayDisplay($aRet, "$aRet with *")

1869266930_itworked2.png.7d34051f93730cc564e0094bbdfa0e43.png

No idea for the repeated mess without * but it's fine it seems to work with star. Maybe a good thing that I quoted Jon in an earlier post, he may be interested by the "non-star" mess

Edit: oops, "a longer string" you asked ?

But "there_was_nothing" was already longer than "after DllCall", that's why I tested now with a shorter one named "short". Ok, I'll try again with a much longer than "there_was_nothing"

I tried again with :

Local $sString = "this is a very long sentence indeed !"
...

Local $sString = "this is a very long sentence indeed !"
...

25441435_itworked3.png.03c558e5f93803fe4ab1d37bbe08373a.png

Edited by pixelsearch
Link to comment
Share on other sites

Maybe I am totally wrong on this one.  But I think the mess is made by the fact that you assign a newstring to oldstring and newstring get out of scope on return, gets garbage collected and is replaced by something.  On the other hand when using * method, it may work but I would assume with memory leak.  Since my days on C are very very far, it is probably a bad assumption. ;)

Link to comment
Share on other sites

In fact it works too with 1 less line in C++ code !

extern "C" __declspec(dllexport) int test2 (const char** oldstring)
{
    *oldstring = "abc";
    return 0;
}

AutoIt code :

Local $sString = "W"
$aRet = DllCall($hDLL, "int:cdecl", "test2", "str", $sString)
If @error Then Exit Msgbox(0, "DllCall", "error " & @error)
_ArrayDisplay($aRet, "$aRet without *")
ConsoleWrite("1 - " & $sString & @crlf)

Local $sString = "W"
$aRet = DllCall($hDLL, "int:cdecl", "test2", "str*", $sString)
If @error Then Exit Msgbox(0, "DllCall", "error " & @error)
_ArrayDisplay($aRet, "$aRet with *")
ConsoleWrite("2 - " & $sString & @crlf)

AutoIt console :

1 - W
2 - W
>Exit code: 0

245713820_itworked4.png.2d3447876cb75dde542ae41e0c57d530.png

I notice that, without star, the garbage on the left pic appears always same (7 char. starting with $0Àf and ending with the hatted-Z) until $sString got 1 character more than the replacement string.

For instance, in this example with a replacement string = "abc", no matter $sString = "" or "W" or "WW" or "WWW", garbage appears same (7 char)

Now, when $sString got 1 character more than the replacement string (i.e. "WWW4" vs "abc"), then garbage displayed becomes 4 characters only : $0Àf

When $sString got 2 characters more than the replacement string (i.e. "WWW45" vs "abc"), then garbage displayed is 5 characters : $0Àf5

And the pattern is found : "WWW456" vs "abc" would display $0Àf56 etc...
"WWW45678901234567890" vs "abc" would display $0Àf5678901234567890 (all this just tested right now)

Anyway, it's good to know something possibly came out from this str* thing. I tried a loop (200.000 times), calling the dll without star and didn't notice a memory leak meanwhile or after, we'll see...

Link to comment
Share on other sites

Why not simple like this?

PS: maybe with some checks to prevent the overflows

#include <windows.h>

extern "C" {
    __declspec (dllexport) void change(char str[]) {
        strcpy(str, "This is my new string.");
    }
}

int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
  return 1;
}

int WINAPI WinMain(      
    HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpCmdLine,
    int nCmdShow)
{  
  return 0;
}

image.png.18077824631bbcf01ca48a369cd3d07c.png

Edited by Andreik
Link to comment
Share on other sites

@Andreik: that's great, it works fine. Thanks !

I did well opening this thread, hoping someone would give a simple solution :)

Do you think some precaution should be taken, concerning what's written in this link ?

To avoid overflows, the size of the array pointed by destination shall be long enough to contain the same C string as source (including the terminating null character), and should not overlap in memory with source.

* edit1: you edited your post while I was typing, thinking too about overflows lol

* edit2 : this is crazy. As soon as you google on "strcpy memory issue" you got dozen of links telling you how unsafe it can be if misused. Everything we try in C/C++ looks dangerous, especially when you're a C++ newbie like me !

Edited by pixelsearch
Link to comment
Share on other sites

That's true but I tested with a shorter and a longer string and seems to work fine. If we talk about plain C I assume that when you say a string you understand an array of null terminated chars but since I don't know exactly how AutoIt handle these things it would be safer to pass the length of the buffer for further checks and if it need to reallocate the buffer.

Link to comment
Share on other sites

  • 9 months later...

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...