Questa soluzione funziona su Build 509, ma su Build 600. Sulla Build 600, l'output degli esperti è: "DLL says: ???????????x??????????????"
v600 usa stringhe Unicode, non stringhe Ansi. O avete bisogno di restituire una stringa Unicode dalla vostra DLL (via più facile), o dovete fare alcune chiamate abbastanza complesse nel codice MQL4 per manipolare il valore di ritorno della stringa (via più difficile).
In entrambi i casi, questo comporta una perdita di memoria perché si sta allocando un blocco di memoria per la stringa che MT4 non sa come liberare, e non lo farà. E' meglio passare un buffer alla DLL, e lasciare che la DLL copi la stringa in quel buffer. In altre parole, il modo migliore per restituire valori di stringa a MQL4 è usare lo stesso tipo di metodo delle chiamate API di Windows come GetWindowsDirectory() e GetTempPath().
oppure bisogna fare delle chiamate piuttosto complesse nel codice MQL4 per manipolare il valore di ritorno della stringa (percorso più difficile).
Per la cronaca, è ancora possibile usare DLL che restituiscono stringhe Ansi; questo è una sorta di seguito a https://www.mql5.com/en/forum/149321. Il problema è che restituire stringhe dalle DLL fa perdere memoria, a meno che il codice MQL4 non sappia come è stata allocata la memoria e sia in grado di liberarla. L'esempio seguente presuppone che la memoria della stringa sia stata allocata nella DLL usando LocalAlloc(), o qualche equivalente indiretto che si sovrapponga a LocalAlloc().
#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); }
gchrmt4:
Per la cronaca, è ancora possibile usare DLL che restituiscono stringhe Ansi; questo è una sorta di seguito a https://www.mql5.com/en/forum/149321. Il problema è che restituire stringhe dalle DLL fa perdere memoria, a meno che il codice MQL4 non sappia come è stata allocata la memoria e sia in grado di liberarla. Il seguente esempio presuppone che la memoria della stringa sia stata allocata nella DLL usando LocalAlloc(), o qualche equivalente indiretto che mappi su LocalAlloc().
Grazie! Ha funzionato anche per la mia libmysql.dll. Il mio MQL4 non può più parlare con MySQL dalla build 600
Finalmente posso almeno vedere cosa restituisce la dll MySql...
Potresti darci un indizio su come implementare la comunicazione opposta, cioè passare una stringa da MQL4 (build 600) alla dll che supporta solo ANSI? Ho provato la funzione UNICODE2ANSI() che si trova qui e qui, ma purtroppo non mi funziona.Potresti darci un indizio su come implementare la comunicazione opposta, cioè passare una stringa da MQL4 (build 600) a dll che supporta solo ANSI?
Grazie! Funziona perfettamente per semplici valori di stringa. Almeno MQL si connette a MySQL ora.
Tuttavia sembra che io non possa ottenere un array di stringhe da una DLL. Ecco la funzione che mi viene fornita (è questo wrapper MQL4 MySql):
#import "mysql_wrapper.dll" void MT4_mysql_fetch_row (int result, string& row[]);
Attualmente sto ricevendo l'errore "Access violation read to 0x0..." quando passo l'array di stringhe di MQL.
Qualsiasi aiuto è molto apprezzato.
Tuttavia sembra che non possa ottenere un array di stringhe da una dll. Ecco la funzione che mi viene fornita (è questo wrapper MQL4 MySql):
Simulare i vecchi array di stringhe Ansi è disordinato, ma ancora possibile. (Questo dipende dal fatto che la DLL si comporti bene, in particolare se passa i dati indietro a MQL4 alterando il contenuto dell'array. Ho testato questo solo contro l'esempio di codice C++ in basso, non contro qualcosa di più realistico come la libreria MySql).
#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]); } }
Per esempio, il codice di cui sopra funziona con la seguente DLL che fa una casella di messaggio per ogni stringa in un array e poi inverte la stringa prima di tornare a 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++; } }
Simulare i vecchi array di stringhe Ansi è disordinato, ma ancora possibile.
(Il codice di cui sopra può essere semplificato se MQL4 sta passando l'array di stringhe alla DLL solo per darle un buffer di stringhe in cui la DLL possa scrivere. Il codice sopra dovrebbe gestire questo scenario, ma è inutilmente complicato per una chiamata di sola ricezione ad una DLL).
Se una DLL Ansi prende un parametro array di stringhe solo per poter passare indietro un valore in arr[0], allora il codice può essere molto più semplice. Per esempio:
#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); }
Questo funziona con una DLL che sta semplicemente usando array[0] come un buffer in cui può scrivere, per esempio:
__declspec(dllexport) void WINAPI Test(MqlStr * arr) { lstrcpyA(arr->data, "The string return value from the DLL"); }
- App di trading gratuite
- Oltre 8.000 segnali per il copy trading
- Notizie economiche per esplorare i mercati finanziari
Accetti la politica del sito e le condizioni d’uso
Ciao codificatori!
Ho scritto una funzione DLL in Delphi. Questo codice restituisce solo un testo PChar all'ex4.
Il mio codice MQ4:
Questa soluzione funziona su Build 509, ma su Build 600. Su Build 600, l'output degli esperti è: "DLL dice: ???????????x??????????????"
Perché non funziona su B600? Avete qualche idea per far funzionare correttamente il mio codice?
Grazie in anticipo.
Relativo