DLL to be called with Unicode strings instead of ASCII

Pages: 12
Hi.

I am using the following DLL in a program that uses the ASCII format for strings :

#include <windows.h>
#include <string>

using namespace std;

void __stdcall MYFUNCTION(LPSTR string1, LPSTR string2)

SHELLEXECUTEINFOA command1;
ZeroMemory (&command1, sizeof (SHELLEXECUTEINFOA));
command1.cbSize = sizeof (SHELLEXECUTEINFOA);
command1.lpVerb = "open";
command1.lpFile = string1;
command1.lpParameters = string2;
command1.nShow = SW_HIDE;
command1.fMask = SEE_MASK_NOCLOSEPROCESS;
ShellExecuteExA(&command1);
WaitForSingleObject(command1.hProcess, INFINITE);

I would like to make changes to this DLL in order to call it from programs that store strings in Unicode format.
I guess I should use the 'W' form of the orders, something like :

SHELLEXECUTEINFOW command1;
ZeroMemory (&command1, sizeof (SHELLEXECUTEINFOW));
command1.cbSize = sizeof (SHELLEXECUTEINFOW);
command1.lpVerb = "open";
command1.lpFile = string1;
command1.lpParameters = string2;
command1.nShow = SW_HIDE;
command1.fMask = SEE_MASK_NOCLOSEPROCESS;
ShellExecuteExW(&command1);
WaitForSingleObject(command1.hProcess, INFINITE);

My question is :

how should the statement :

void __stdcall MYFUNCTION(LPSTR string1, LPSTR string2)

be changed?

Namely, what should be used instead of LPSTR?

Thanks in advance.
Use LPWSTR (or better yet LPCWSTR for const correctnes). See:

https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx
The most flexible way would be to have both and let the client decide what he wants to use.
1
2
void __stdcall MyFunctionA(char* string1, char* string2);
void __stdcall MyFunctionW(wchar_t* string1, wchar_t* string2);

Also by convention all uppercase names are very often used for macros, so it's better not use them for sth. else.
Thanks for both replies (coder777 and Thomas1965).
I have tried both methods but unfortunately this does not work.

The problem must be linked to the parameters (strings) passed to the DLL because when I hardcode these parameters in the DLL :

command1.lpFile = L"value of string1";
command1.lpParameters = L"value of string2";

everything works fine.

Any other ideas?

Can you show us the complete code?
Here is one of the versions tested :

#include <windows.h>
#include <string>

using namespace std;

void __stdcall MyFunction(wchar_t* string1, wchar_t* string2)

SHELLEXECUTEINFOW command1;
ZeroMemory (&command1, sizeof (SHELLEXECUTEINFOW));
command1.cbSize = sizeof (SHELLEXECUTEINFOW);
command1.lpVerb = L"open";
command1.lpFile = string1;
command1.lpParameters = string2;
command1.nShow = SW_HIDE;
command1.fMask = SEE_MASK_NOCLOSEPROCESS;
ShellExecuteExW(&command1);
WaitForSingleObject(command1.hProcess, INFINITE);

The parameter string1 holds something like "C:\\MyProgram.exe" and string2 holds some parameters to be used by MyProgram.exe.

When the DLL is called, MyProgram.exe should create some files. When used with parameters as described in the stdcall statement, no files are created. It looks like the string string1 (and probably also string2) are not meaningful for the SHELLEXECUTEINFO statement.

Note that the same problems occur with the other codings proposed for the string1 and string2 strings in the stdcall statement (char *, LPWSTR, LPCWSTR).

Here is the version of the DLL that works (Strings are hardcoded, so no parameters are to be passed to the DLL) :


#include <windows.h>
#include <string>

using namespace std;

void __stdcall MyFunction()

SHELLEXECUTEINFOW command1;
ZeroMemory (&command1, sizeof (SHELLEXECUTEINFOW));
command1.cbSize = sizeof (SHELLEXECUTEINFOW);
command1.lpVerb =L"open";
command1.lpFile = L"C:\\MyProgram.exe";
command1.lpParameters = L"abcd";
command1.nShow = SW_HIDE;
command1.fMask = SEE_MASK_NOCLOSEPROCESS;
ShellExecuteExW(&command1);
WaitForSingleObject(command1.hProcess, INFINITE);

When control is returned to the calling program, it is seen that MyProgram.exe has created the expected files.

I think that the problem occurs at the level of the strings (the calling program stores the strings in Unicode format) but I am not able to debug this problem.
Are you sure the strings contain what you think they contain? Do this before calling ShellExecuteExW():
1
2
3
4
std::ofstream file("strings.txt", std::ios::binary);
file.write(string1, (wcslen(string1) + 1) * sizeof(wchar_t));
file.write(string2, (wcslen(string2) + 1) * sizeof(wchar_t));
file.close();
and see what it contains. Keep in mind that the file will be in little endian UTF-16 (or UCS-2), so use an appropriate editor to look at it.
Thanks for your suggestion for debugging.

Unfortunately inserting this code prevents me from compiling the DLL successfully. I get a lot of error messages like :

1>MyFunctionDLL.cpp(37): error C2079: 'file' uses undefined class 'std::basic_ofstream<char,std::char_traits<char>>'
1>MyFunctionDLL.cpp(37): error C2440: 'initializing': cannot convert from 'initializer list' to 'int'
1>MyFunctionDLL.cpp(37): note: The initializer contains too many elements
1>MyFunctionDLL.cpp(38): error C2228: left of '.write' must have class/struct/union
1>MyFunctionDLL.cpp(38): note: type is 'int'
1>MyFunctionDLL.cpp(39): error C2228: left of '.write' must have class/struct/union
1>MyFunctionDLL.cpp(39): note: type is 'int'
1>MyFunctionDLL.cpp(40): error C2228: left of '.close' must have class/struct/union
1>MyFunctionDLL.cpp(40): note: type is 'int'

Correcting those compilation errors is far above the level of my skills in C++.
Add #include <fstream>
How do you call this dll functions from the main program?
I had already tried to compile with such an added statement, but then got the following messages :

1>MyFunctionDLL(38): error C2664: 'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::write(const _Elem *,std::streamsize)': cannot convert argument 1 from 'wchar_t *' to 'const char *'
1> with
1> [
1> _Elem=char
1> ]
1>MyFunctionDLL(38): note: Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
1>MyFunctionDLL(39): error C2664: 'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::write(const _Elem *,std::streamsize)': cannot convert argument 1 from 'wchar_t *' to 'const char *'
1> with
1> [
1> _Elem=char
1> ]
1>MyFunctionDLL(39): note: Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast

This DLL is called by Metatrader, which is a trading platform.

Within the Metatrader code (some kind of C language), the DLL is declared with statements like :

#import "MyFunctionDLL.dll"
int MyFunction(string,string);
#import

It is called by a statement like :

MyFunction(string1, string2)

In case I want to use the version of the DLL with hardcoded strings, the following statements are used :


#import "MyFunctionDLL.dll"
int MyFunction();
#import

MyFunction()

Note I am sure that within Metatrader strings are stored in Unicode format.
If you want to use Helio's code with widestrings you need to change it:
1
2
3
4
std::wofstream file("strings.txt", std::ios::binary);
file.write(string1, (wcslen(string1) + 1) * sizeof(wchar_t));
file.write(string2, (wcslen(string2) + 1) * sizeof(wchar_t));
file.close();
Thank you for this update.

Compilation of the DLL is now OK.

I shall test tomorrow, when financial markets are open.
.. strange ...

I cannot find any "strings.txt" file.
Maybe save it somewhere else - like C:\Temp or so.
Note that a full search of the C disk with the name of the file wasdone in order ton retrieve the file.
Something like this (see the bolded part):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include <fstream>
#include <experimental/filesystem>

int main()
{
    const wchar_t* string1 = L"this is string one" ; 
    const wchar_t* string2 = L"and this is string two" ; 
    
    std::wcout << L"The test strings are\n#1: " << string1 << L"\n#2: " << string2 << L'\n' ;
    
    std::string file_name = "strings.txt" ;
    
    {
        std::wofstream file(file_name) ;
        std::cout << "\n\nfile opened? " << std::boolalpha << file.is_open() << '\n' ;
        std::cout << "current working directory is: " << std::experimental::filesystem::current_path() << '\n' ;
        const auto full_path = std::experimental::filesystem::absolute(file_name) ;
        std::cout << "full path is: " << full_path << '\n' ;
        
        file << L"#1: " << string1 << L"\n#2: " << string2 << L'\n' ;
    }
    
    std::cout << "\n\ncontents of file '" << file_name << "'\n----------------------\n" << std::flush ;
    std::system( "type strings.txt" ) ;
}

http://rextester.com/QAG64259
Another option:
1
2
3
4
5
6
7
8
9
10
#pragma comment(lib, "User32")
void __stdcall MyFunction(wchar_t* string1, wchar_t* string2)
{
  std::wstringstream msg;
  msg << L"string1 = " << string1 << L"\n\n" << L"string2 = "
      << string2;

  MessageBoxW(0, msg.str().c_str(), L"Message", MB_OK|MB_ICONINFORMATION);

}
Last edited on
Hi.

This last update led to compilation errors I wasnot able to correct.

However, based on some items in a forum, I made some changes to the DLL, in order to check if parameters (strings) were correctly passed to the DLL. The puropose of these added statements was to show that the way the calling program (Metatrader) stores the strings is not the same as in C : before the string is a 4-bytes integer housing the length of the string :

extern "C" void __stdcall MyFunction(WCHAR * string1, WCHAR * string2)

{
int lStringLength1 = *(((int*)string1) - 1);
int lStringLength2 = *(((int*)string2) - 1);

WCHAR tmp[30];

swprintf_s(tmp, L"String1 length: %i", lStringLength1);

MessageBoxW(NULL, string1, tmp, 64);

swprintf_s(tmp, L"String2 length: %i", lStringLength2);

MessageBoxW(NULL, string2, tmp, 64);

(remaining code unchanged)

Within Metatrader, the DLL is called with this statement :

MyFunction("This is string1", "This is string2");

When the DLL is called, 2 boxes appear on the screen :

1st box :

String1 length: 24

This is the first string


2nd box :

String2 length: 25

This is the second string


It thus looks like the parameters (strings in this case) are correctly passed to the DLL.

The rest of the code of the DLL (SHELLEXECUTEINFOW) does not have any effect (no files are created).

I really do not understand what is wrong with this DLL.

Might there be a problem with the last character of the string, which should be a '\0'?

Thanks.


I think it's worth checking the return value form ShellExecuteExW function and use GetLastError if the call was not successful.

Is there any documentation about this Metatrader program?
Hi.

Metatrader is a trading platform. Thus the documentation is mainly related to the development of indicators and Expert Advisors (kind of robots executing buy/sells orders automatically). Documentation is rather weak about internals and language.

Description of the language (called MQL) can be found on https://docs.mql4.com.

The problem with Metatrader is that the internals are sometimes changed is an abrupt way (to say the least). For instance, from a specific build, strings that were previously stored in ASCII format were now stored in Unicode format. This caused all users having implemented DLL using strings to face severe problems with their DLLs.

The DLL that I tried "convert' to Unicode is also used on another trading Platform (TradeStation) that stores the strings in ASCII format and the DLL works perfectly (using SHELLEXECUTEA and the 'A' version of the other verbs).

Can you tell me how I should modify the DLL in order to check the return value of ShellExecuteExW?

Thanks.
Pages: 12