DLL to be called with Unicode strings instead of ASCII

Pages: 12
I think I could do it. The message is :

Box :

This is the first string

Windows cannot find 'This is the first string'. Make sure you typed the name correctly
and try again.


Hi.

What I have done to display GetLastError() is wrong.

I am thus still interested in getting the correct code for that purpose.

Thanks.

Try this:
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <windows.h>
#include <tchar.h>

void ShowLastWinError()
{
  DWORD err = GetLastError();
  if (err == ERROR_SUCCESS)
  {
    MessageBox(NULL, _T("The was no error :)"), _T("Notice"), MB_OK);
    return;
  }
  TCHAR *msg;
  FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | 
                FORMAT_MESSAGE_FROM_SYSTEM, 
                NULL, err, 0, (LPTSTR)&msg, 1024, NULL);
  MessageBox(NULL, msg, _T("Windows Error"), MB_OK | MB_ICONERROR);
  LocalFree((HLOCAL)msg);
}

void __stdcall MyFunction(LPCWSTR string1, LPCWSTR string2)
{
  if (string1 == NULL || string2 == NULL)
  {
    MessageBoxW(NULL, L"Invalid Parameter", L"ERROR", MB_OK);
    return;
  }
  MessageBoxW(NULL, string1, L"First param", MB_OK);
  MessageBoxW(NULL, string2, L"Second param", MB_OK);
  SHELLEXECUTEINFOW command1 = {0};
  command1.cbSize = sizeof(SHELLEXECUTEINFOW);
  command1.lpVerb = L"open";
  command1.lpFile = string1;
  command1.lpParameters = string2;
  command1.nShow = SW_HIDE;
  command1.fMask = SEE_MASK_NOCLOSEPROCESS;
  if (!ShellExecuteExW(&command1))
  {
    ShowLastWinError();
    return;
  }
  WaitForSingleObject(command1.hProcess, INFINITE);
}
Last edited on
Hi.

Thank you for this update.

When the DLL is called, it displays successively 2 boxes :

1st box :

First param

This is the first string


2nd box :

Second param

This is the second string


The statement ShowLastWinError() does not cause any value to be displayed.
If I understand your code, it would mean ShellExecuteEx executes correctly.
However, no files are generated by the program (string1) called by ShellExecuteEx.

Note that when I run the test with real values for the strings, the strings displayed by the 2 boxes are exactly the ones used when calling the DLL (and as stated previously, are exactly the same as the ones used in the 'hardcoded' version of the DLL).

Very strange ...




What is actually the program called from the program dll?
Is it part of the MetaTrader or sth. you have to provide?
Last edited on
Hi.

The program called from the DLL is R, which is used for statistical computing (info on https://www.r-project.org/). This has nothing to do with Metatrader but can be called from Metatrader, as any other program (using the DLL).

So the full value of 'string 1' is something like :

C:/Program Files/R/R-3.3.2/bin/RScript.Exe

The value of 'string 2' is the full name of a file housing what is called a R script, which will be executed by the program RScript.exe.

Note that the first string is relatively static (only changing in case a new release of R is tested) but 'string 2' is build dynamically by code I developped for Metatrader.
The name of this file takes into account the currency pair that is traded, the timeframe and also the algorithm used to make predictions. Due to the way Metatrader works, the file name might also depend on the Metatrader in use (Metatrader used on several computers at the same time might use different file name). This is the main reason why I try to avoid hardcoding file names in the DLL.

Note that the C-like language implemented by Metatrader allows for some Windows verbs to be directly embedded in the code. This is what I did in the first version of my Metatrader code : I used directly a statement like :

ShellExecuteW(0, "Open", string1, string2, "", 0);

This worked perfectly well : files were correctly created and their content was OK when the program described by string1 (R) executed the R script described by string2. At least it allowed me to check that the code of the R script was OK.

However, this implementation was not usable, because control was immediately given back to Metatrader code, while the actions done by of ShellExecuteW were occuring asynchronously.

This is why I had to add a WaitForSingleObject statement, but this could only be done with a DLL and a ShellExecuteInfoW statement.

I hope this clarifies the use of the DLL.



Can you try using CreateProcess instead of ShellExecuteEx so that we might possibly get a different response or error message due to ShellExecuteEx having something specific within its environment that is perhaps causing issues or the output to be placed somewhere else (ie. your R script file may be processed but the output of it is created in some other place due to the settings of ShellExecuteEx).

Hi.

Thank you for your update.

Here are 2 remarks :

1- I could finally successfully compile the following statements that you provided me with previously for debugging purpose :

#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);

}

I just added the statement #include <sstream> and compilation was OK.

After calling the DLL, your code displayed the 2 parameters passed to the DLL corectly.
We can thus be sure that parameters are passed correctly to the DLL.

2- Just like you do, I suspected there might be something wrong with the ShellExecuteExW statement.
I found this item :

https://stackoverflow.com/questions/42155265/shellexecuteex-doesnt-pass-long-command

The problem described in there might be similar to my problem : it is related to something that doesn't work with ShellExecuteEx, but which is OK when ShellExecute is used. The problem is related to a very long lpFile value in the SHELLEXECUTE info structure.

My 'string'2' value being also rather long (more than 100 characters). I copied the file described by 'string2' into another location, in order to make the lpFile parameter shorter (the new value of string2 is now something like C:/name.R).

With this change, the DLL runs fine : the R script that is executed creates the wished files.
But this causes another problem : the DLL is only executed once, because when control is given back to Metatrader, some message like 'stack is damaged' are issued, and the DLL is no longer called (the calling program runs once per minute). I have to investigate the problem on the Metatrader side but at least this test shows that there is something wrong occuring with ShellExecuteExW, as you suspect.

Based on the results of the numerous tests so far, I have got the impression that when lpFile is 'long', the ShellExecuteExW does nothing and gives a zero return code (?) whilst when
lpFile is 'short', ShellExecuteExW works fine.
On the other side, ShellExecuteExW works fine when lpFile is assigned a hardcoded value (even a 'long' one).
I also found that there were some limitations about the length of the value assigned to lpFile (I found 2K, or 8K, depending on the 'author'), but anyway those limitations are far beyond the value that I am using.

I shall try to persue the track you suggest, using CreateProcess instead of ShellExecuteEx.

Thanks.




Hi.

Now the DLL works and provides the expected results.

Thanks to all people who helped me with this problem!
Topic archived. No new replies allowed.
Pages: 12