Z tym że to opisujesz wygląda mi na obejście nieprawidłowego przekazywania danych. EDIT: albo wcale nie - jest prawidłowym obejściem czegoś innego - zobacz niżej komentarz po przykładowym kodzie dll.
Zasada jest taka, że dane wejściowe należy najpierw skopiować przed dalszą obróbką w dll, a wyniki przekopiować na wyjście. A na końcu posprzątać po sobie czyli usunąć wszelkie alokacje które trzeba było zrobić w kodzie dll bo inaczej będą narastać śmieci.
I wtedy nie będzie to miało znaczenia co jest w tym stringu który ma być wyjściem, a nie wejściem.
Natomiast ma znaczenie w jakiej formie są przekazywane te dane.
Co innego string, co innego string[] i na dodatek w samym dll tez są przynajmniej dwie możliwości wyjścia, każda wymaga innego podejścia.
Ja wybrałem dane jednoelementowe czyli chcę mieć w MQL jeden string z kilku innych. Ale w przypadku tabelek stringów sprawa jest trudniejsza, bo to już jest struktura którą trzeba sobie odtworzyć w dll. I tutaj zaczynają się schody bo Metaquotes nie opisuje tej struktury… przynajmniej nie wprost ;-)
Tak samo jak nie opisuje jak alokowany jest string. To, że MQL alokuje automatycznie 64k dla tego co wychodzi z funkcji dll znalazłem grzebiąc rozpaczliwe po rożnych dziwnych miejscach.
Ale 64k to nie jest limit. Właśnie przed chwilą zrobiłem sobie string ponad 400MB, łącząc krótsze przez StringConcatenate() w pętelce.
W samym MQL są rozmaite dziwne rzeczy. Np piszą, że string constant nie może być dłuższy niż 255 znaków. To jest string constant:
Ale ten sam test można teraz użyć jako zmienną a nie stałą:
Kod: Zaznacz cały
for (int i=0;i<50000;i++) test = StringConcatenate(test,"1234567890");
I teraz może on być znacznie dłuższy. Uwaga, wykonanie tej pętli trochę trwa, podejrzewam że większość tego czasu jest potrzebna na zarządzanie pamięcią ;-)
Dalej, piszą o string constant:
Its internal representation is a structure of 8 bytes. The first element of the structure is a long integer that contains the size of the buffer distributed for the line. The second element of the structure is 32-order address of the buffer that contains the line.
Ale w takim razie skoro można przekazać ten string wprost choć przecież funkcja w dll odbiera to jako char* to oznacza, że MQL musi niejawnie podawć "The second element" wskaźnik do pierwszego znaku stringu. Bo to co dostaje dll jak byk jest c stringiem: ciągiem znaków zakończonych NULL.
A teraz, skoro wewnętrzna struktura stringu MQL jest dwuelementowa: {długość;wskaźnik} to jak będzie wyglądać tablica stringów w MQL, a jak będzie to wyglądać jak przekaże się taką tablicę do dll?
Dodano po 37 minutach:
Zresztą można się podeprzeć przykładowym dll - to jest przykład obrabiania tablic stringów - zwróćcie uwagę jak zdefiniowana jest ta tablica, jako tabelka struktur opisujących/wskazującyh stringi, a nie tabelka samych stringów rozumianych jako ciąg znaków (w nowomowie informatycznej to jest tabelka metadanych, a nie samych danych ;-) ):
Kod: Zaznacz cały
struct MqlStr
{
int len;
char *string;
};
MT4_EXPFUNC int __stdcall ProcessStringArray(MqlStr *arr,const int arraysize)
{
int len1,len2;
//----
if(arr==NULL)
{
printf("ProcessStringArray: NULL array\n");
return(-1);
}
if(arraysize<=0)
{
printf("ProcessStringArray: wrong arraysize (%d)\n", arraysize);
return(-1);
}
//----
for(int i=0; i<arraysize-1; i++)
{
if(arr[i].string==NULL) len1=0;
else len1=strlen(arr[i].string);
if(arr[i+1].string==NULL) len2=0;
else len2=strlen(arr[i+1].string);
//---- uninitialized string
if(arr[i+1].string==NULL) continue;
//---- destination string is uninitialized and cannot be allocated within dll
if(arr[i].string==NULL) continue;
//---- memory piece is less than needed and cannot be reallocated within dll
if(arr[i].len<len1+len2) continue;
//---- final processing
strcat(arr[i].string,arr[i+1].string);
}
//----
return(arraysize);
}
Aha, tutaj mamy to co opisuje
crn:
Kod: Zaznacz cały
//---- destination string is uninitialized and cannot be allocated within dll
if(arr[i].string==NULL) continue;
Jeżeli w tablece był string który nie był jeszcze niczym wypełniony, to sprawdzając jego długość mamy NULL, a sprawdzając pierwszy znak mamy adres NULL i w efekcie go po prostu nie ma. Jest puste miejsce w tabelce, nic za nim nie stoi i pisanie tam będzie dawało wszelkie możliwe efekty oprócz zamierzonych.
Ale teraz kłóci mi się to z tym co robi MQL ze stringami i pamięcią... jasne! MQL zarządza stringami automatycznie: dynamicznie dopasowuje wielkość stringu do danych jakie maja tam trafić. Dll nie ma takiej możliwości - może sie poruszać tylko w tym co MQL mu przekazał!
I w tym świetle:
Kod: Zaznacz cały
string example = "asdsfsddsfgdfggggggggggggggggggggggggggggggggggggggg";
nabiera sensu jako obejście ograniczenia możliwości zarządzania pamięcią w dll.
Inaczej mówiąc: jak w MQL zrobię:
to w efekcie dostaję: len=0;adres=0;
String jest ale pusty. Jak przekaże to jako bufor to dll to czego można się spodziewać jak zacznie tam coś bazgrać?
Jak napiszę:
to dostanę: len=10;adres=... coś tam będzie.
I będę miał buforek na 10 znaków. Jak to przekaże do dll będzie mógł tam wpisać DO 10-u znaków - to jest właśnie to o czym pisał
crn
Uwaga - w tym kodzie arr jest zarówno wejściem jak i wyjściem: przekazywana jest tablica i ta tablica jest modyfikowana w dll. Co jest nie jest zbyt bezpieczne gdyż a) niszczy się oryginalne dane, b) nie jest wcale tak łatwo kontrolować zakres pamięci w jakim można się poruszać. W tym przykładzie niby to jest proste ale tylko dlatego, że sam przykład jest prosty.
Lepiej jednak dane wejściowe przekopiować do jakiegoś wewnętrznego bufora, a na końcu sprawdzić czy wyjście pomieści wynik przed przekopiowaniem na wyście. I rozdzielić wejście od wyjścia w definicji funkcji.
Z drugiej strony, w prostych zastosowaniach metoda z tego przykładu będzie znacznie łatwiejsza i szybsza. I nie zostawia śmieci do sprzątania.
Jakże często ludzie mają już gotową opinię zanim zdążą pojąć istotę rzeczy.
A gdy już ta istota w pełni do nich dotrze, jakże często muszą zmagać się z konsekwencjami swojej opinii ;-)