DLL dize işlevi Build 600'de çalışmıyor

 

Merhaba kodlayıcılar!

Delphi'de bir DLL işlevi yazdım. Bu kod yalnızca ex4'e bir PChar metni döndürür.

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.

MQ4 kodum:

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

int init(){

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

}

Bu çözüm Yapı 509'da, ancak Yapı 600'de çalışır. Yapı 600'de, Experts çıktısı şöyledir: "DLL şunu söylüyor: ???????????x????????????? ?"

Neden B600'de çalışmıyor? Kodumun düzgün çalışmasını sağlamak için bir fikriniz var mı?

Şimdiden teşekkür ederim.

Akraba

 
Relative :

Bu çözüm Yapı 509'da, ancak Yapı 600'de çalışır. Yapı 600'de, Experts çıktısı şöyledir: "DLL şunu söylüyor: ???????????x????????????? ?"

v600, Ansi dizelerini değil, Unicode dizelerini kullanır. DLL'nizden bir Unicode dizesi döndürmeniz (daha kolay yol) veya dize dönüş değerini (daha zor yol) değiştirmek için MQL4 kodunda oldukça karmaşık çağrılar yapmanız gerekir.

Her iki durumda da, bu bellek sızdıracak çünkü MT4'ün nasıl serbest bırakılacağını bilmediği ve serbest bırakmayacağı dize için bir bellek bloğu ayırıyorsunuz. DLL'ye bir arabellek geçirmek ve DLL'nin dizeyi bu arabelleğe kopyalamasını sağlamak daha iyidir. Diğer bir deyişle, dize değerlerini MQL4'e döndürmenin daha iyi yolu, GetWindowsDirectory() ve GetTempPath() gibi Windows API çağrılarıyla aynı türde bir yöntemi kullanmaktır.

 
gchrmt4 :

veya dize dönüş değerini (daha zor yol) değiştirmek için MQL4 kodunda oldukça karmaşık çağrılar yapmanız gerekir.

Kayıt için, Ansi dizelerini döndüren DLL'leri kullanmak hala mümkündür; bu, https://www.mql5.com/en/forum/149321 adresinin bir tür takibidir. Sorun şu ki, MQL4 kodu belleğin nasıl ayrıldığını bilmiyorsa ve onu serbest bırakamıyorsa, DLL'lerden dizelerin döndürülmesi belleği sızdırıyor. Aşağıdaki örnek, dize belleğinin DLL'de LocalAlloc() veya LocalAlloc() ile eşleşen dolaylı bir eşdeğer kullanılarak ayrıldığını varsayar.

 #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);
}
 
Teşekkürler gchrmt4!
 

gchrmt4
:

Kayıt için, Ansi dizelerini döndüren DLL'leri kullanmak hala mümkündür; bu, https://www.mql5.com/en/forum/149321 adresinin bir tür takibidir. Sorun şu ki, MQL4 kodu belleğin nasıl ayrıldığını bilmiyorsa ve onu serbest bırakamıyorsa, DLL'lerden dizelerin döndürülmesi belleği sızdırıyor. Aşağıdaki örnek, dize belleğinin DLL'de LocalAlloc() veya LocalAlloc() ile eşleşen dolaylı bir eşdeğer kullanılarak ayrıldığını varsayar.

Teşekkür ederim! Ayrıca libmysql.dll'im için de çalıştı. MQL4'üm 600'ü kurduğumdan beri MySQL ile artık konuşamıyor

Sonunda en azından MySql dll'nin ne döndürdüğünü görebiliyorum ...

MQL4'ten (derleme 600) sadece ANSI'yi destekleyen bir diziyi dll'ye geçirmek gibi karşıt iletişimin nasıl uygulanacağı konusunda bize bir ipucu verebilir misiniz? Burada ve burada bulunan UNICODE2ANSI() işlevini denedim ama ne yazık ki benim için çalışmıyor.
 
lukins :

MQL4'ten (derleme 600) sadece ANSI'yi destekleyen bir diziyi dll'ye geçirmek gibi karşıt iletişimin nasıl uygulanacağı konusunda bize bir ipucu verebilir misiniz?

Aynı sayfanın devamı! Bkz. https://www.mql5.com/en/forum/149321
 
gchrmt4 :
Aynı sayfanın devamı! Bkz. https://www.mql5.com/en/forum/149321


Teşekkür ederim! Basit dize değerleri için mükemmel bir şekilde çalışır. En azından MQL şimdi MySQL'e bağlanıyor.

Ancak, bir dll'den bir dizi dizi alamıyor gibiyim. Bana sağlanan işlev şudur ( bu MQL4 MySql sarmalayıcıdır ):

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

Şu anda normal MQL'nin dize dizisini geçerken "Erişim ihlali 0x0'a okundu..." hatası alıyorum.

Herhangi bir yardım çok takdir edilmektedir.

 
"int & row[]" ifadesini geçtiğimde ve doldurulduktan sonra içindeki her öğeyi dönüştürmeye çalıştığımda benzer bir hata (Erişim ihlali) oluşuyor.
 
lukins :

Ancak, bir dll'den bir dizi dizi alamıyor gibiyim. Bana sağlanan işlev şudur ( bu MQL4 MySql sarmalayıcıdır ):

Eski Ansi dize dizilerini simüle etmek dağınıktır, ancak yine de mümkündür. (Bu, özellikle dizinin içeriğini değiştirerek verileri MQL4'e geri iletirse, DLL'nin iyi durumda olmasına bağlı olacaktır. Bunu daha gerçekçi bir şeye karşı değil, yalnızca alttaki örnek C++ koduna karşı test ettim. MySql kütüphanesi gibi.)

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

Örneğin, yukarıdaki kod, bir dizideki her dize için bir mesaj kutusu yapan ve ardından MT4'e dönmeden önce dizeyi tersine çeviren aşağıdaki DLL ile çalışır:

__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 :

Eski Ansi dize dizilerini simüle etmek dağınıktır, ancak yine de mümkündür.

(MQL4, dize dizisini yalnızca DLL'nin yazacağı bir dize arabelleği vermek için DLL'ye iletiyorsa, yukarıdaki kod basitleştirilebilir. Yukarıdaki kod bu senaryoyu işlemelidir, ancak bir alma için gereksiz yere karmaşıktır. -yalnızca bir DLL'ye çağrı yapın.)
 
gchrmt4 :
(MQL4, dize dizisini yalnızca DLL'nin yazacağı bir dize arabelleği vermek için DLL'ye iletiyorsa, yukarıdaki kod basitleştirilebilir. Yukarıdaki kod bu senaryoyu işlemelidir, ancak bir alma için gereksiz yere karmaşıktır. -yalnızca bir DLL'ye çağrı yapın.)

Bir Ansi DLL yalnızca dizi dizi parametresini arr[0] içindeki bir değeri geri iletebilmesi için alırsa, kod çok daha basit olabilir. Örneğin:

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

Bu, dizini [0] içine yazabileceği bir arabellek olarak kullanan bir DLL ile çalışır, örneğin:

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