Jump to content

DllCall pass string containing zero chrd


Go to solution Solved by pixelsearch,

Recommended Posts

NULL isn't a string!

Pass  an empty string: $str = ''

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

One question : why chr(0) can't be considered as a full character ?

Maybe OP could create a structure containing his string, include his chr(0)'s within the string, then pass the pointer of the structure (and probably its length) to the dll, for example :

Local $hDLL = DllOpen(@ScriptDir & "\afac9.dll")
If $hDLL = -1 Then Exit Msgbox(0, "DllOpen", "error occured")

Local $sData = "1234" & chr(0) & "6789"
Local $iBufferSize = StringLen($sData) ; $iBufferSize = 9 (as chr(0) counts for 1 char)

Local $tStruct = DllStructCreate("char[" & $iBufferSize & "]")
If @error Then Exit Msgbox(0, "DllStructCreate", "error " & @error)

DllStructSetData($tStruct, 1, $sData)
If @error Then Exit Msgbox(0, "DllStructSetData", "error " & @error)

MsgBox(0, "$iBufferSize = " & $iBufferSize, DllStructGetPtr($tStruct))

Local $aRet = DllCall($hDLL, "int:cdecl", "string_with_chr0", "ptr", DllStructGetPtr($tStruct))
If @error Then Exit Msgbox(0, "DllCall", "error " & @error)

$tStruct = 0
DllClose($hDLL)

559605134_stringwithchr(0).png.fbae93990ee17eed65011fb56029e26b.png

C++ code :

#include <windows.h>
#include <iostream>
using namespace std;

extern "C" __declspec(dllexport) int string_with_chr0 (LPVOID string_adress)
{
    cout << "String adress = " << string_adress << endl;
    return 0;
}

The C++ keyword "cout" isn't really recommended in a dll, but as we run the AutoIt script from Scite, then we can access the informative output of the dll into Scite's console (which imho is a fast way for debugging while creating a dll called from AutoIt)

Next step should be to access directly the memory from C++ code (using the string adress + string length, i.e $iBufferSize) and do what has to be done when a chr(0) is encountered at a memory location, like the one found in the pic above.

There is certainly a shorter way to achieve this, maybe another reader could share his solution.

Link to comment
Share on other sites

Weird things happen when dealing with strings containing one or more 0x00

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

@Ontosy Yes you have to add a parameter to pass the string length, as showed in the script below

Local $hDLL = DllOpen(@ScriptDir & "\afac9.dll")
If $hDLL = -1 Then Exit Msgbox(0, "DllOpen", "error occured")

Local $sData = "1234" & chr(0) & "6789"
Local $iBufferSize = StringLen($sData) ; $iBufferSize = 9 (as chr(0) counts for 1 char)

Local $tStruct = DllStructCreate("char[" & $iBufferSize & "]")
If @error Then Exit Msgbox(0, "DllStructCreate", "error " & @error)

DllStructSetData($tStruct, 1, $sData)
If @error Then Exit Msgbox(0, "DllStructSetData", "error " & @error)

MsgBox(0, "$iBufferSize = " & $iBufferSize, DllStructGetPtr($tStruct))

Local $aRet = DllCall($hDLL, "int:cdecl", "string_with_chr0", _
    "ptr", DllStructGetPtr($tStruct), "int", $iBufferSize)
If @error Then Exit Msgbox(0, "DllCall", "error " & @error)

$tStruct = 0
DllClose($hDLL)

458931875_stringwithchr(0)bis.png.1377e39cb19355e2f0af6bf03c663d68.png

C++ code :

#include <windows.h>
#include <iostream>
using namespace std;

extern "C" __declspec(dllexport) int string_with_chr0
    (void* string_adress, int string_length)
{
    char string_text[string_length];
    memcpy (string_text, string_adress, string_length);

    cout << "String adress = " << string_adress << endl;
    cout << "String length = " << string_length << endl;
    cout << "String text   = " << string_text   << endl;

    for (int i = 0; i < string_length; i++)
    {
        cout << string_text[i] << " ";
    }
    cout << endl;

    // MessageBox(0, "", "Inside Dll", MB_OK | MB_TOPMOST);
    return 0;
}

I've added a variable in C++ code, named string_text, which contains... the string text and its inner chr(0)
When this variable is displayed in Scite Console, it will display only "1234" as chr(0) at 5th position will  be treated as the end of the string. But you can see in C++ For loop (and in Scite Console) that the variable contains all the characters, when they're displayed one by one.

Edited by pixelsearch
typo
Link to comment
Share on other sites

  • Solution

Though the preceding script works, I'm not a big fan of these C char[] arrays that you can't display because there is a chr(0) inside and their manipulation isn't very easy.

I tried initially to use std::string but I couldn't make it (i.e. to fill a std::string directly from both parameters) though I guess it's doable.

Anyway, now that the char[] array is created and functional in the preceding script, I succeeded to create a std::string based on the char[] array, which gives good results for displaying the variable at once (no for loop). Also, manipulating std::string is easier than manipulating char[]

So if we add the 3 following lines at the end of the preceding C++ code, this is the good result that will be displayed in Scite Console :

std::string true_string(string_text, string_length);
cout << "True String : size = " << true_string.size() << endl;
cout << "True String : text = " << true_string << endl;

1657066971_stringwithchr(0)ter.png.feacb141ceea23ed13bbf2b744e3bb34.png

Now that's fresh air :)
If I find a way to create the std::string directly (without the char[] lines) then I'll add a post here.

Link to comment
Share on other sites

1 hour ago, pixelsearch said:

If I find a way to create the std::string directly (without the char[] lines) then I'll add a post here.

Just found it :thumbsup:

C++ code :

#include <iostream>
using namespace std;

extern "C" __declspec(dllexport) int string_with_chr0
    (void* string_adress, int string_length)
{
    string string_text (static_cast<char*>(string_adress), string_length);

    cout << "String size = " << string_text.size() << endl;
    cout << "String text = " << string_text << endl;

    return 0;
}

394085233_stringwithchr(0)4th.png.40fb5644b21d0aa71b58c9cab24e7dee.png

Edited by pixelsearch
Link to comment
Share on other sites

Looong post coming, divided in 4 parts :

1) the workable solution which solved OP's need : how to pass Chr(0) from Autoit to a dll. This will be a short resume of precedent posts, showing also that Scite Console can be used for output during this process.

2) Extend this solution to wide characters (Unicode), replacing Chr(0) with ChrW(1034) for example... but Scite Console will stop displaying the output, because of the wide char.

3) Try another way for wide char's, but here again, Scite Console won't display the wide char's part.

4) Several examples of MessageBox directly in C++ code : if we really want to debug a C++ code (without the use of a debugger) then C++ MessageBox can help a lot because it will display the wide char's part (then we can forget Scite's console which helps only when dealing with ANSI characters)


Part 1)
ANSI characters chr(0) to chr(255)
As we need to pass a chr(0) from AutoIt to the dll, we can't use a "str" parameter in DllCall because the string will be truncated + possible error at run time. The solution found is to create a structure of char's and pass its pointer to the dll

AutoIt code (without error checking to make it shorter, error checking found in preceding posts)

Local $hDLL = DllOpen(@ScriptDir & "\afac9b.dll")

Local $sData = "1234" & Chr(0) & "6789"
Local $iBufferSize = StringLen($sData) ; $iBufferSize = 9

Local $tStruct = DllStructCreate("char[" & $iBufferSize & "]")
DllStructSetData($tStruct, 1, $sData)

MsgBox(0, "$iBufferSize = " & $iBufferSize, DllStructGetPtr($tStruct))

Local $aRet = DllCall($hDLL, "int:cdecl", "string_with_chr0", _
    "ptr", DllStructGetPtr($tStruct), "int", $iBufferSize)

$tStruct = 0
DllClose($hDLL)

1790089911_part1.png.4c63090b7503df41d5cd2abeaf42533d.png

C++ code

#include <iostream>
using namespace std;

extern "C" __declspec(dllexport) int string_with_chr0
    (void* string_adress, int string_length)
{
    string string_text (static_cast<char*>(string_adress), string_length);

    cout << "String size = " << string_text.size() << endl;
    cout << "String text = " << string_text << endl;

    return 0;
}

So far, so good. Scite Console shows correctly the output of C++ code
This one-liner C++ code did a great job :

string string_text (static_cast<char*>(string_adress), string_length);

It allows to "convert" what points at string_adress memory to char* (it surely could be explained better). After all, C++ doesn't know anything about what type of data string_adress is pointing to, string_adress being only a pointer (void*)

As it worked fine, then I thought : "why not trying a cast to Unicode wide characters (i.e. wchar_t*) and that's how the idea of Part 2 started.


Part 2)
UNICODE characters in the range 0-65535
Now that Chr(0) got its solution, let's replace Chr(0) with ChrW(1034) to see where it goes :

AutoIt code (structure is now wchar)

Local $hDLL = DllOpen(@ScriptDir & "\afac9c.dll")

Local $sData = "0123" & ChrW(1034) & "5678"
Local $iBufferSize = StringLen($sData) ; 9 characters

Local $tStruct = DllStructCreate("wchar[" & $iBufferSize & "]")
DllStructSetData($tStruct, 1, $sData)

MsgBox(0, "$iBufferSize = " & $iBufferSize, DllStructGetPtr($tStruct))

Local $aRet = DllCall($hDLL, "int:cdecl", "wstring_with_unicode", _
    "ptr", DllStructGetPtr($tStruct), "int", $iBufferSize)

$tStruct = 0
DllClose($hDLL)

668087055_part2.png.517c54e76cdd086519858d16b1c90c6d.png

C++ code (using wstring)

#include <iostream>
using namespace std;

extern "C" __declspec(dllexport) int wstring_with_unicode
    (void* string_adress, int string_length)
{
    std::wstring string_text (static_cast<wchar_t*>(string_adress), string_length);

    cout << "String size = " << string_text.size() << endl;
    wcout << "String text = " << string_text << endl;

    std::wstring s4 = string_text.substr(4,1); // remember 1st char is at pos 0
    wcout << "s4 = " << s4 << endl;
    if (s4 == L"\u040A") // 1034 dec
    {
        cout << "s4 OK" << endl;
    }

    std::wstring s8 = string_text.substr(8,1); // remember 1st char is at pos 0
    wcout << "s8 = " << s8 << endl;
    if (s8 == L"\u0038")
    {
        cout << "s8 OK" << endl;
    }

    return 0;
}

ChrW(1034) is correctly found in the memory dump pic above (04*256 = 1024) + 0A (10) => decimal 1034

We notice that Scite Console output is incomplete : as soon as a Unicode character appears, Scite Console seems to stop displaying all further wcout found in C++ code (for example it didn't display wcout << "s4 = " or wcout << "s8 = ")

 

Part 3)
Just to make sure, let's try an alternate script with ChrW(1034), without using a structure.

AutoIt code (no more structure, just a unicode wide character string passed to the dll)

Local $hDLL = DllOpen(@ScriptDir & "\afac9d.dll")

Local $sData = "0123" & ChrW(1034) & "5678"
Local $iBufferSize = StringLen($sData) ; 9 characters

Local $aRet = DllCall($hDLL, "int:cdecl", "wstring_with_unicode", _
    "wstr", $sData, "int", $iBufferSize)

DllClose($hDLL)

C++ code

#include <iostream>
using namespace std;

extern "C" __declspec(dllexport) int wstring_with_unicode
    (wchar_t* sbigdata, int string_length)
{
    std::wstring string_text = sbigdata;

    cout << "String size = " << string_text.size() << endl;
    wcout << "String text = " << string_text << endl;

    std::wstring s4 = string_text.substr(4,1); // remember 1st char is at pos 0
    wcout << "s4 = " << s4 << endl;
    if (s4 == L"\u040A") // 1034 dec
    {
        cout << "s4 OK" << endl;
    }

    std::wstring s8 = string_text.substr(8,1); // remember 1st char is at pos 0
    wcout << "s8 = " << s8 << endl;
    if (s8 == L"\u0038")
    {
        cout << "s8 OK" << endl;
    }

    return 0;
}

Scite Console would show exactly the same truncated output than the pic in Part 2)

 

Part 4)
No more dll but a C++ console exe, showing the syntax of MessageBox when we need to display constants of variables (including wide char)
The C++ MessageBox syntax isn't as easy as in AutoIt but at least it shows a way to display variables results while scripting a C++ console app or a C++ dll, it can help to debug (especially if you don't use a debugger)

C++ code :

#include <windows.h>
// #include <string>
#include <iostream>
using namespace std;

int MsgBox(int flag, const char* title, const char* text)
{
    return MessageBox(NULL, text, title, flag); // NULL = no owner window
}

int MsgBoxW(int flag, const wchar_t* title, const wchar_t* text)
{
    return MessageBoxW(NULL, text, title, flag); // NULL = no owner window
}

int main()
{
    // 2 functions MsgBox() & MsgBoxW() match AutoIt MsgBox parameters passing order
    MsgBox(0, "1 - Title", "Text"); // 0 is MB_OK

    string title = "2 - Title";
    int number = 2;
    int iret = MsgBox(MB_OKCANCEL | MB_ICONQUESTION,
               title.c_str(), ("Variable = " + to_string(number)).c_str());
    if (iret == 2)
    {
        cout << "Cancel pressed" << endl;
    }

    MsgBoxW(0, L"3 - Wide title", L"Wide text"); // 0 is MB_OK

    wstring title4 = L"4 - Wide Title";
    MsgBoxW(0, title4.c_str(), L"\u040A"); // 1034 dec
    wcout << L"\u040A" << endl; // nothing is displayed in my console

    wstring title5 = L"5 - Wide Title";
    wstring var5 = L"\u040A";
    MsgBoxW(0, title5.c_str(), var5.c_str());

    wstring title6 = L"6 - Wide Title";
    wstring var6 = var5;
    MsgBoxW(0, title6.c_str(), (L"Wide variable is: " + var6).c_str());
    
    return 0;
}

1067826807_part4.png.1bcd72543c6b0deaac66726c77ae24f1.png

Windows Console :

"Cancel pressed" (if click on cancel in MessageBox2)

The whole purpose of this post was to show that, not only OP's question about Chr(0) had a solution, but also the use of Scite Console while scripting a C++ dll (as long as it doesn't involve wide chars). If wide chars are required, then C++ MessageBox seems much more useful.

Thanks to @jchd in this post, where he indicates how an AutoIt script can display Unicode chars in Scite Console, by encoding the script in UTF-8 format (through Scite File menu), then using this line to display the unicode character :

; script to be saved as UTF-8 (+++)
ConsoleWrite(ChrW(1034) & @crlf) ; displays a bad question mark ?
ConsoleWrite(BinaryToString(StringToBinary(ChrW(1034), 4), 1) & @crlf) ; works, thanks jchd !

Mods: if you think this whole thread should be placed in the C/C++ section, please don't hesitate :bye:

Link to comment
Share on other sites

Yes, Windows consoles don't know how to deal with wide chars (UTF16-LE), only UTF8.

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

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...