Creating a pointer to a string for use with User32.dll's SendMessageA

 
Hello all,

I am in the process of writing a script that gets user input from an external application. My understanding is that to do this I Have to send the external application's edit control a WM_GETTEXT message. I found the following lines in the WinUser32.mqh file:

#import "user32.dll"  
//---- messages  
int SendMessageA(int hWnd,int Msg,int wParam,int lParam);

According to the documentation ( http://msdn2.microsoft.com/en-us/library/ms632627.aspx ) lParam is supposed to be a pointer to a buffer (string). However, I am not sure how to do this in MQL4. I seem to understand that only arrays can be passed by reference. Shold I pass a string array by reference? This is what i believe https://docs.mql4.com/basis/variables/extfunctions says.

Thanks in advance for any help, it would be much appreciated.
 

See example

#import "kernel32.dll"
  int GetModuleFileNameA
      ( int    Module,         // Идентификатоp модуля
        string FileName,       // Полное имя файла
        int    Size);          // Размер буфера
#import
 
//+------------------------------------------------------------------+
//| Возвращает полное имя папки МТ4                                  |
//+------------------------------------------------------------------+
string GetFolderNameMT()
{
  string res;
  string FolderName="                                                                                             ";
  GetModuleFileNameA(0, FolderName, StringLen(FolderName));
  res=StringSubstr(FolderName, 0,(StringLen(FolderName)-12));
  return(res);
}
 
//+------------------------------------------------------------------+
//| script program start function                                    |
//+------------------------------------------------------------------+
int start()
  {
//----
   Print("Working directory ",GetFolderNameMT());
//----
   return(0);
  }
//+------------------------------------------------------------------+

See also discussion https://www.mql5.com/en/forum/46512

You can pass as parameter constant string only. Because side effect

 
Thank You for your answer, I will try shortly.

By the way, by searching in the forums I found out that no one has been able to use the FindWindowA function from user32.dll -- do you have a workaround for that too?

Thanks
 
stringo: You can pass as parameter constant string only. Because side effect
ok, this thread is old. developers might be interested anyway, in fact i guess they are indeed very interested.

Stringos statement and his code example are wrong. instead the exact opposite is true.

you must *never* EVER pass a constant string parameter to a DLL function expecting a pointer to a string buffer for writing into it. because of side effects, also called golden MetaQuotes eggs (maybe they are rotten eggs...).
try the following example to understand:

/**
 * TestExpert
 */
#import "kernel32.dll"
   int  GetModuleFileNameA(int hModule, string lpBuffer, int bufferSize);
#import


string testVar1 = "i never touched testVar1                                                                   ";


/**
 * Initialization
 *
 * @return int - error status
 */
int init() {
   string fileName = "i never touched testVar1                                                                   ";
   Print("testVar1=\""+ testVar1 +"\"");

   // wrong use, not recommended
   GetModuleFileNameA(NULL, fileName, StringLen(fileName));

   Print("fileName=\""+ fileName +"\"");
   Print("testVar1=\""+ testVar1 +"\"");

   correctUse();

   return(0);
}


/**
 * Main function
 *
 * @return int - error status
 */
int start() {
   return(0);
}


/**
 *
 * @return int - error status
 */
int correctUse() {
   string testVar2 = "testVar2 will not get touched                                                              ";
   Print("testVar2=\""+ testVar2 +"\"");

   #define MAX_PATH  260      // for example the maximum path on drive D is "D:\some-256-character-path-string<NUL>"

   int    bufferSize = MAX_PATH;
   string fileName[]; InitializeStringBuffer(fileName, bufferSize);
   GetModuleFileNameA(NULL, fileName[0], bufferSize);

   Print("fileName=\""+ fileName[0] +"\"");
   Print("testVar2=\""+ testVar2 +"\"");

   return(0);
}


/**
 * Initialize a string buffer of a given length.
 *
 * @param  string buffer[]
 * @param  int    length
 *
 * @return int - error status
 */
int InitializeStringBuffer(string& buffer[], int length) {
   if (ArraySize(buffer) == 0)
      ArrayResize(buffer, 1);
   buffer[0] = CreateString(length);
   return(0);
}


/**
 * Create a string of the given length.
 *
 * @param  int length
 *
 * @return string
 */
string CreateString(int length) {
   #define MAX_STRING_LITERAL "..............................................................................................................................................................................................................................................................."

   string newStr = StringConcatenate(MAX_STRING_LITERAL, "");        // do not use the constant itself, create
   int    strLen = StringLen(newStr);                                // a copy instead or you will end up in mess

   while (strLen < length) {
      newStr = StringConcatenate(newStr, MAX_STRING_LITERAL);
      strLen = StringLen(newStr);
   }
   if (strLen != length)
      newStr = StringSubstr(newStr, 0, length);
   return(newStr);
}

see how in the first test the same constant is used for two variables, they are even in different scopes. i initialized both with the same literal value "i never touched this", could have been spaces of course but you wouldn't realize the effect as easy as with concrete text. the results are this:


testVar1="i never touched testVar1                                                                   "
fileName="F:\MetaTrader\3\terminal.exe"
testVar1="F:\MetaTrader\3\terminal.exe"

testVar2="testVar2 will not get touched                                                              "
fileName="F:\MetaTrader\3\terminal.exe"
testVar2="testVar2 will not get touched                                                              "

conclusion: never use constant strings as DLL parameters as recommended for this type of call. constant literals only exist once in memory, if you change on of them you change all. they get mixed and mangled somewhere in the terminal memory and mess up your code.

instead use the version of the second test with a dynamic string parameter (i wrote InitializeStringBuffer() and CreateString() only to have a general string solution).

using a string array element is the only solution i found to pass a pointer to a dynamic string buffer (not a pointer to a constant literal) a DLL can write to.


i wonder why this forum is polluted over and over with wrong code snippets, even from MetaQuotes itself. coincidence? i don't know.

Reason: