Die DLL-String-Funktion funktioniert nicht auf Build 600

 

Hallo Programmierer!

Ich habe eine DLL-Funktion in Delphi geschrieben. Dieser Code gibt nur einen PChar-Text an die ex4 zurück.

ibrary get_text;

uses
  SysUtils,
  Math,
  idHttp;

{$R *.res}

var

function get_text(): PChar; stdcall;
begin
  result := PChar('Hello world!');
end;


exports
  get_text;

begin
end.

Mein MQ4-Code:

#import "get_text.dll"
   string get_text();
#import

int init(){

   Print("DLL says: ",get_text());

}

Diese Lösung funktioniert auf Build 509, aber auf Build 600. Auf Build 600 lautet die Ausgabe der Experten: "DLL sagt: ???????????x??????????????".

Warum funktioniert es nicht auf B600? Haben Sie eine Idee, wie mein Code richtig funktioniert?

Ich danke Ihnen im Voraus.

Relativ

 
Relative:

Diese Lösung funktioniert mit Build 509, aber nicht mit Build 600. Unter Build 600 lautet die Ausgabe der Experten: "DLL sagt: ???????????x??????????????".

v600 verwendet Unicode-Zeichenfolgen, keine Ansi-Zeichenfolgen. Sie müssen entweder eine Unicode-Zeichenkette von Ihrer DLL zurückgeben (einfacher Weg), oder Sie müssen einige recht komplexe Aufrufe im MQL4-Code durchführen, um den String-Rückgabewert zu manipulieren (schwieriger Weg).

In jedem Fall wird dadurch Speicherplatz verloren gehen, da Sie einen Speicherblock für den String zuweisen, den MT4 nicht freigeben kann und auch nicht freigeben wird. Es ist besser, einen Puffer an die DLL zu übergeben und die DLL den String in diesen Puffer kopieren zu lassen. Mit anderen Worten, der bessere Weg, String-Werte an MQL4 zurückzugeben, ist die Verwendung der gleichen Art von Methoden wie Windows-API-Aufrufe wie GetWindowsDirectory() und GetTempPath().

 
gchrmt4:

oder Sie müssen einige recht komplexe Aufrufe im MQL4-Code durchführen, um den String-Rückgabewert zu manipulieren (schwierigerer Weg).

Übrigens ist es immer noch möglich, DLLs zu verwenden, die Ansi-Strings zurückgeben; dies ist eine Art Nachfolge von https://www.mql5.com/en/forum/149321. Das Problem ist, dass bei der Rückgabe von Strings aus DLLs Speicher verloren geht, es sei denn, der MQL4-Code weiß, wie der Speicher alloziert wurde und ist in der Lage, ihn freizugeben. Das folgende Beispiel geht davon aus, dass der Stringspeicher in der DLL mit LocalAlloc() oder einem indirekten Äquivalent, das auf LocalAlloc() abbildet, zugewiesen wurde.

#import "SomeDllWhichReturnsAnsiStrings.dll"
   // Declare the Ansi function as returning int rather than string 
   int Test();  // NOT  string Test();
#import

#import "kernel32.dll"
   int lstrlenA(int);
   void RtlMoveMemory(uchar & arr[], int, int);
   int LocalFree(int); // May need to be changed depending on how the DLL allocates memory
#import

void OnStart()
{
   // Call the DLL function and get its block of string memory as an int pointer to the
   // memory rather than as a string 
   int ptrStringMemory = Test();
   
   // Get the length of the string 
   int szString = lstrlenA(ptrStringMemory);
   
   // Create a uchar[] array whose size is the string length (plus null terminator)
   uchar ucValue[];
   ArrayResize(ucValue, szString + 1);
   
   // Use the Win32 API to copy the string from the block returned by the DLL
   // into the uchar[] array
   RtlMoveMemory(ucValue, ptrStringMemory, szString + 1);
   
   // Convert the uchar[] array to a MQL string
   string strValue = CharArrayToString(ucValue);

   // Free the string memory returned by the DLL. This step can be removed but, without it,
   // there will be a memory leak.
   // The correct method for freeing the string *depends on how the DLL allocated the memory*
   // The following assumes that the DLL has used LocalAlloc (or an indirect equivalent). If not,
   // then the following line may not fix the leak, and may even cause a crash.
   LocalFree(ptrStringMemory);   
   
   // Done...
   Print(strValue);
}
 
Vielen Dank, gchrmt4!
 

gchrmt4
:

Es ist übrigens immer noch möglich, DLLs zu verwenden, die Ansi-Strings zurückgeben; dies ist eine Art Nachfolgeprojekt von https://www.mql5.com/en/forum/149321. Das Problem ist, dass bei der Rückgabe von Strings aus DLLs Speicher verloren geht, es sei denn, der MQL4-Code weiß, wie der Speicher zugewiesen wurde, und ist in der Lage, ihn freizugeben. Das folgende Beispiel geht davon aus, dass der Stringspeicher in der DLL mit LocalAlloc() oder einem indirekten Äquivalent, das auf LocalAlloc() abbildet, zugewiesen wurde.

Vielen Dank an dieser Stelle! Es hat auch bei meiner libmysql.dll funktioniert. Mein MQL4 kann seit Build 600 nicht mehr mit MySQL kommunizieren.

Endlich kann ich wenigstens sehen, was MySql dll zurückgibt...

Könnten Sie uns einen Hinweis geben, wie man die umgekehrte Kommunikation implementiert, d.h. wie man einen String von MQL4 (Build 600) an eine dll übergibt, die nur ANSI unterstützt? Ich habe es mit der Funktion UNICODE2ANSI() versucht, die hier und hier zufinden ist , aber leider funktioniert sie bei mir nicht.
 
lukins:

Könnten Sie uns einen Hinweis darauf geben, wie die umgekehrte Kommunikation zu implementieren, d.h. eine Zeichenfolge von MQL4 (Build 600) an dll, die nur ANSI unterstützt übergeben?

Es ist weiter unten auf der gleichen Seite! Siehe https://www.mql5.com/en/forum/149321
 
gchrmt4:
Es ist weiter unten auf der gleichen Seite! Siehe https://www.mql5.com/en/forum/149321


Ich danke Ihnen! Es funktioniert für einfache String-Werte einfach perfekt. Zumindest MQL verbindet sich jetzt mit MySQL.

Jedoch scheint wie ich kann nicht ein Array von Zeichenfolgen aus einer DLL erhalten. Hier ist die Funktion, die ich mit (es ist diese MQL4 MySql-Wrapper) bereitgestellt werden:

#import "mysql_wrapper.dll"
void     MT4_mysql_fetch_row   (int result, string& row[]);

Derzeit erhalte ich "Zugriffsverletzung lesen zu 0x0..." Fehler bei der Übergabe von üblichen MQL String-Array.

Jede Hilfe ist sehr geschätzt.

 
Ein ähnlicher Fehler (Zugriffsverletzung) tritt auf, wenn ich "int & row[]" übergebe und versuche, jedes Element darin zu konvertieren, nachdem es ausgefüllt wurde.
 
lukins:

Jedoch scheint wie ich kann nicht ein Array von Zeichenfolgen aus einer DLL erhalten. Hier ist die Funktion, die ich mit (es ist diese MQL4 MySql-Wrapper) bereitgestellt werden:

Simulieren alte Ansi String-Arrays ist chaotisch, aber immer noch möglich. (Das hängt davon ab, dass die DLL sich gut benimmt, insbesondere wenn sie Daten an MQL4 zurückgibt, indem sie den Inhalt des Arrays ändert. Ich habe dies nur mit dem C++-Beispielcode unten getestet, nicht mit etwas Realistischerem wie der MySql-Bibliothek).

#import "SomeDllWhichTakesAStringArray.dll"
   // Old Ansi function which takes a string array plus a parameter telling it 
   // the size of the array
   void Test(int & arr[], int);  // NOT  void Test(string & arr[], int)
#import

#import "kernel32.dll"
   int LocalAlloc(int,int);
   int LocalFree(int);
   int lstrcpyA(int,uchar & arr[]);
   int lstrlenA(int);
   void RtlMoveMemory(uchar & arr[], int, int);
#import

void OnStart()
{
   // Example array of strings...
   string MyStringArray[] = {"String 1", "String 2"};
   int SizeOfStringArray = ArraySize(MyStringArray);
   
   
   
   // A string array (received by the DLL as an array of MqlStr structures) corresponds
   // to an int array where the even-numbered members are string lengths, and the odd-numbered
   // members are pointers to string memory.
   // We start by creating an int array, which needs to have twice as many members as the
   // string array
   int i;
   int arrMqlStr[];
   ArrayResize(arrMqlStr, SizeOfStringArray * 2);
   
   // Populate the array which simulates MqlStr[]. For each string, we allocate 
   // a block of memory using LocalAlloc() and copy the MQL4 string into that
   for (i = 0; i < SizeOfStringArray; i++) {
      // Get the length of the string and store it
      int szString = StringLen(MyStringArray[i]);
      arrMqlStr[i * 2] = szString;
   
      // Allocate a block of memory to hold the string 
      int ptrMem = LocalAlloc(0x40 /* LPTR */, szString + 1);
      arrMqlStr[(i * 2) + 1] = ptrMem;

      // Convert the MQL4 string to a uchar[] array
      uchar ucString[];
      StringToCharArray(MyStringArray[i], ucString);
 
      // Copy the uchar[] array into the block of memory allocated above
      lstrcpyA(ptrMem, ucString);     
   }

   // Call the DLL function
   Test(arrMqlStr, SizeOfStringArray);

   // We now need to free the memory which was allocated above to hold
   // a copy of each string. In addition, DLLs can alter strings which
   // are passed to them in arrays. Therefore, for completeness, we
   // should read the strings back and put them into the original
   // array
   for (i = 0; i < SizeOfStringArray; i++) {
      // Get the size of the string now contained in the memory block
      int NewSizeofString = lstrlenA(arrMqlStr[(i * 2) + 1]);
      
      // Copy the contents of the memory block into a uchar[] array
      uchar ucReceive[];
      ArrayResize(ucReceive, NewSizeofString + 1);
      RtlMoveMemory(ucReceive, arrMqlStr[(i * 2) + 1], NewSizeofString + 1);

      // Put the uchar[] back into the original string array
      MyStringArray[i] = CharArrayToString(ucReceive);
      
      // Free the memory for the string allocated above
      LocalFree(arrMqlStr[(i * 2) + 1]);
   }
}

Zum Beispiel funktioniert der obige Code mit der folgenden DLL, die eine Message-Box für jede Zeichenfolge in einem Array tut und dann kehrt die Zeichenfolge vor der Rückkehr zu MT4:

__declspec(dllexport) void WINAPI Test(MqlStr * arr, int sz)
{
        for (int i = 0; i < sz; i++) {
                MessageBoxA(NULL, arr->data, "String", 48);
                strrev(arr->data);
                arr++;
        }
}
 
gchrmt4:

Die Simulation der alten Ansi-String-Arrays ist zwar unübersichtlich, aber dennoch möglich.

(Der obige Code kann vereinfacht werden, wenn MQL4 das String-Array nur an die DLL weitergibt, um ihr einen String-Puffer zu geben, in den die DLL schreiben kann. Der obige Code sollte dieses Szenario handhaben, aber er ist unnötig kompliziert für einen reinen Empfangsaufruf an eine DLL).
 
gchrmt4:
(Der obige Code kann vereinfacht werden, wenn MQL4 das String-Array nur an die DLL weitergibt, um ihr einen String-Puffer zu geben, in den die DLL schreiben kann. Der obige Code sollte dieses Szenario handhaben, aber er ist unnötig kompliziert für einen reinen Empfangsaufruf an eine DLL).

Wenn eine Ansi-DLL einen String-Array-Parameter nur deshalb annimmt, damit sie einen Wert in arr[0] zurückgeben kann, dann kann der Code viel einfacher sein. Zum Beispiel:

#import "AnsiDllWhichPassesBackAStringValueViaAnArray.dll"
   // Old Ansi function which takes a string array parameter **solely** so that 
   // it can **pass back** a value in arr[0]
   void Test(int & arr[]);  // NOT  void Test(string & arr[])
#import

#import "kernel32.dll"
   int LocalAlloc(int,int);
   int LocalFree(int);
   void RtlFillMemory(int, int, int);
   int lstrlenA(int);
   void RtlMoveMemory(uchar & arr[], int, int);
#import

void OnStart()
{
   // Maximum size of string which the DLL is expected to return in arr[0]
   int MaxReturnValueLength = 10000;
   
   // Allocate a block of memory of the desired size
   int ptrMem = LocalAlloc(0x40 /* LPTR */, MaxReturnValueLength + 1);
   
   // Fill the memory with spaces so that the length is <whatever> if 
   // the DLL checks it by doing strlen()
   RtlFillMemory(ptrMem, MaxReturnValueLength, 32);

   // Create a pseudo-MqlStr array which corresponds to a string array with one member
   int arrMqlStr[2];
   arrMqlStr[0] = MaxReturnValueLength;
   arrMqlStr[1] = ptrMem;
   
   // Call the DLL
   Test(arrMqlStr);

   // Get the size of the string contained in the memory block
   int NewSizeofString = lstrlenA(ptrMem);
      
   // Copy the contents of the memory block into a uchar[] array
   uchar ucReceive[];
   ArrayResize(ucReceive, NewSizeofString + 1);
   RtlMoveMemory(ucReceive, ptrMem, NewSizeofString + 1);
   
   // Free the memory for the string allocated above
   LocalFree(ptrMem);
   
   // Convert the uchar[] array to a string 
   string strReturnValueFromDLL = CharArrayToString(ucReceive);
}

Dies funktioniert mit einer DLL, die einfach array[0] als Puffer benutzt, in den sie schreiben kann, z.B.:

__declspec(dllexport) void WINAPI Test(MqlStr * arr)
{
        lstrcpyA(arr->data, "The string return value from the DLL");
}