この解決策はBuild 509ではうまくいきますが、Build 600ではうまくいきません。Build 600では、Expertsの出力は、"DLL says: ?????x??????" となります。
v600では、Ansi文字列ではなく、Unicode文字列を使用しています。DLLからUnicode文字列を返す必要があるか(簡単な方法)、MQL4コードの中でかなり複雑な呼び出しをして文字列の戻り値を操作する必要があるか(難しい方法)です。
どちらにしても、文字列のためにメモリブロックを確保し、MT4が解放する方法を知らず、解放もしないため、メモリリークを起こすことになります。DLLにバッファを渡し、DLLがそのバッファに文字列をコピーする方がよいでしょう。言い換えれば、MQL4に文字列値を返すには、GetWindowsDirectory()やGetTempPath()などのWindows APIコールと同じ種類のメソッドを使用するのがよいのです。
または、MQL4のコード内で非常に複雑な呼び出しを行い、文字列の戻り値を操作する必要があります(より困難な方法)。
ちなみに、Ansi文字列を返すDLLを使用することは可能です。これは、https://www.mql5.com/en/forum/149321 のフォローアップのようなものです。問題はDLLから文字列を返すと、MQL4コードがどのようにメモリが割り当てられたかを知っていて、それを解放することができない限り、メモリがリークすることです。以下の例では、文字列メモリはDLL内でLocalAlloc()、または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:
ちなみに、Ansi文字列を返すDLLを使用することは可能です。これは、https://www.mql5.com/en/forum/149321 に対するフォローアップのようなものです。問題は、DLLから文字列を返すと、MQL4コードがどのようにメモリが割り当てられたかを知っていて、それを解放することができない限り、メモリがリークするということです。以下の例では、文字列メモリはDLL内でLocalAlloc()、またはLocalAlloc()にマッピングされる間接的な同等のものを使用して割り当てられたと仮定しています。
ありがとうございました。また、私のlibmysql.dllでも動作しました。私のMQL4は、ビルド600以降、MySQLと話すことができなくなりました。
ようやく、少なくとも MySql dll が何を返すか見ることができるようになりました...
逆の通信、つまりMQL4(ビルド600)からANSIのみをサポートするDLLに文字列を渡す方法を実装するためのヒントを教えていただけませんか? ここと ここにあるUNICODE2ANSI()関数を試して みましたが、残念ながら私にはうまく いきません。逆の通信、つまりMQL4(ビルド600)からANSIのみをサポートするDLLに文字列を渡す実装のヒントを教えていただけないでしょうか。
ありがとうございます。単純な文字列値に対しては、まさに完璧に動作します。少なくとも、MQLはMySQLに接続するようになりました。
しかし、DLLから文字列の配列を取得することはできないようです。以下は、私が提供されている関数です(このMQL4 MySqlラッパー です)。
#import "mysql_wrapper.dll" void MT4_mysql_fetch_row (int result, string& row[]);
現在、通常のMQLの文字列配列を渡すと、「Access violation read to 0x0...」というエラーが表示されます。
どんなヘルプでも非常に感謝されます。
古いAnsiの文字列配列をシミュレートするのは面倒ですが、まだ可能です。(これはDLLがお行儀よくしているか、特に配列の内容を変更することでMQL4にデータを渡すか どうかにかかっています。このテストは一番下にあるサンプルの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++; } }
(上記のコードは、MQL4が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"); }
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索
コーダーの皆さん、こんにちは。
私はDelphiでDLL関数を 書きました。このコードは、ex4にPharテキストを返すだけです。
私のMQ4コードです。
このソリューションは、Build 509では動作しますが、Build 600では動作しません。Build 600では、Expertsの出力は、"DLL says: ???????x???????" です。
なぜB600で動作しないのでしょうか?私のコードを正しく動作させるためのアイデアはありますか?
よろしくお願いします。
相対的