DLL 문자열 기능은 Build 600에서 작동하지 않습니다.

 

안녕 코더들!

Delphi에서 DLL 함수 를 작성했습니다. 이 코드는 PChar 텍스트만 ex4에 반환합니다.

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 코드:

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

int init(){

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

}

이 솔루션은 Build 509에서 작동하지만 Build 600에서는 작동합니다. Build 600에서 Experts 출력은 다음과 같습니다. "DLL: ????????????x???????????????? ?"

B600에서 작동하지 않는 이유는 무엇입니까? 내 코드가 제대로 작동하도록 하는 아이디어가 있습니까?

미리 감사드립니다.

상대적인

 
Relative :

이 솔루션은 Build 509에서 작동하지만 Build 600에서는 작동합니다. Build 600에서 Experts 출력은 다음과 같습니다. "DLL은 ????????????x???????????????? ?"

v600은 Ansi 문자열이 아닌 유니코드 문자열을 사용합니다. DLL에서 유니코드 문자열을 반환해야 하거나(더 쉬운 경로) MQL4 코드에서 매우 복잡한 호출을 수행하여 문자열 반환 값을 조작해야 합니다(더 어려운 경로).

어느 쪽이든 MT4가 해제 방법을 모르고 해제하지 않는 문자열에 대한 메모리 블록을 할당하기 때문에 메모리 누수가 발생합니다. 버퍼를 DLL에 전달하고 DLL이 해당 버퍼에 문자열을 복사하도록 하는 것이 좋습니다. 즉, MQL4에 문자열 값을 반환하는 더 좋은 방법은 GetWindowsDirectory() 및 GetTempPath()와 같은 Windows API 호출과 동일한 종류의 메서드를 사용하는 것입니다.

 
gchrmt4 :

또는 문자열 반환 값(더 어려운 경로)을 조작하기 위해 MQL4 코드에서 매우 복잡한 호출을 수행해야 합니다.

기록을 위해 여전히 Ansi 문자열을 반환하는 DLL을 사용할 수 있습니다. 이것은 일종의 https://www.mql5.com/en/forum/149321 후속 조치입니다. 문제는 MQL4 코드가 메모리가 할당되고 해제할 수 있는 방법을 알지 못하는 경우 DLL에서 문자열을 반환하면 메모리가 누수된다는 것입니다. 다음 예제에서는 LocalAlloc() 또는 LocalAlloc()에 매핑되는 일부 간접 동등물을 사용하여 DLL에 문자열 메모리가 할당되었다고 가정합니다.

 #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 감사합니다!
 

gchrmt4
:

기록을 위해 여전히 Ansi 문자열을 반환하는 DLL을 사용할 수 있습니다. 이것은 일종의 https://www.mql5.com/en/forum/149321 후속 조치입니다. 문제는 MQL4 코드가 메모리가 할당되고 해제할 수 있는 방법을 알지 못하는 경우 DLL에서 문자열을 반환하면 메모리가 누수된다는 것입니다. 다음 예제에서는 LocalAlloc() 또는 LocalAlloc()에 매핑되는 일부 간접 동등물을 사용하여 DLL에 문자열 메모리가 할당되었다고 가정합니다.

고맙습니다! 내 libmysql.dll에서도 작동했습니다. 내 MQL4는 빌드 600 이후로 더 이상 MySQL과 통신할 수 없습니다.

마침내 나는 적어도 MySql dll이 반환하는 것을 볼 수 있습니다 ...

반대 통신을 구현하는 방법, 즉 MQL4(빌드 600)에서 ANSI만 지원하는 dll로 문자열을 전달하는 방법에 대한 단서를 제공해 주시겠습니까? 여기 저기 있는 UNICODE2ANSI() 함수를 시도했지만 불행히도 작동하지 않습니다.
 
lukins :

반대 통신을 구현하는 방법, 즉 MQL4(빌드 600)에서 ANSI만 지원하는 dll로 문자열을 전달하는 방법에 대한 단서를 주시겠습니까?

같은 페이지 아래에 있습니다! https://www.mql5.com/en/forum/149321 참조
 
gchrmt4 :
같은 페이지 아래에 있습니다! https://www.mql5.com/en/forum/149321 참조


고맙습니다! 단순한 문자열 값에 대해 완벽하게 작동합니다. 최소한 MQL은 이제 MySQL에 연결됩니다.

그러나 dll에서 문자열 배열을 가져올 수 없는 것 같습니다. 다음은 내가 제공한 기능입니다( 이 MQL4 MySql 래퍼 ).

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

현재 일반적인 MQL의 문자열 배열을 전달할 때 "액세스 위반 읽기 0x0..." 오류가 발생합니다.

도움을 주시면 감사하겠습니다.

 
유사한 오류(액세스 위반)가 "int & row[]"를 전달하고 채워진 후 내부의 각 요소를 변환하려고 할 때 발생합니다.
 
lukins :

그러나 dll에서 문자열 배열을 가져올 수 없는 것 같습니다. 다음은 내가 제공한 기능입니다( 이 MQL4 MySql 래퍼 ).

오래된 Ansi 문자열 배열을 시뮬레이션하는 것은 지저분하지만 여전히 가능합니다. (특히 배열의 내용을 변경하여 데이터를 MQL4로 다시 전달하는 경우 DLL이 올바르게 작동하는지 여부에 따라 달라집니다. 더 현실적인 것이 아니라 맨 아래에 있는 예제 C++ 코드에 대해서만 테스트했습니다. 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 ]);
   }
}

예를 들어 위의 코드는 배열의 각 문자열에 대해 메시지 상자를 수행한 다음 MT4로 돌아가기 전에 문자열을 뒤집는 다음 DLL과 함께 작동합니다.

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

오래된 Ansi 문자열 배열을 시뮬레이션하는 것은 지저분하지만 여전히 가능합니다.

(위의 코드는 MQL4가 DLL에 쓸 문자열 버퍼 를 제공하기 위해서만 문자열 배열을 DLL에 전달하는 경우 단순화할 수 있습니다. 위의 코드는 해당 시나리오를 처리해야 하지만 수신에는 불필요하게 복잡합니다. - DLL에 대한 호출만 가능)
 
gchrmt4 :
(위의 코드는 MQL4가 DLL에 쓸 문자열 버퍼를 제공하기 위해서만 문자열 배열을 DLL에 전달하는 경우 단순화할 수 있습니다. 위의 코드는 해당 시나리오를 처리해야 하지만 수신에는 불필요하게 복잡합니다. - DLL에 대한 호출만 가능)

Ansi DLL이 문자열 배열 매개변수 취하여 arr[0]의 값을 다시 전달할 수 있다면 코드는 훨씬 간단할 수 있습니다. 예를 들어:

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

이것은 단순히 array[0]을 쓸 수 있는 버퍼로 사용하는 DLL과 함께 작동합니다. 예:

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