这个方案在Build 509上可行,但在Build 600上却不行。在Build 600上,专家的输出是:"DLL说:???????????x??????????????"
v600 使用 Unicode 字符串,而不是 Ansi 字符串。你要么需要从你的DLL中返回一个Unicode字符串(更简单的方法),要么你需要在MQL4代码中做一些相当复杂的调用来操作字符串的返回值(更难的方法)。
无论哪种方式,这都会泄漏内存,因为您为字符串分配了一个内存块,而MT4不知道如何释放,也不会释放。最好的办法是给 DLL 传递一个缓冲区,并让 DLL 将字符串复制到该缓冲区。换句话说,向MQL4返回字符串值的更好方法是使用与Windows API调用相同的方法,如GetWindowsDirectory()和GetTempPath()。
或者你需要在MQL4代码中做一些相当复杂的调用来操作字符串的返回值(更难的途径)。
为了记录在案,仍然可以使用返回Ansi字符串的DLLs;这是一种对https://www.mql5.com/en/forum/149321 的跟进。 问题是,从DLLs返回字符串会泄漏内存,除非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字符串的DLLs;这是对https://www.mql5.com/en/forum/149321 的一种跟进。 问题是,从DLLs返回的字符串会泄漏内存,除非MQL4代码知道内存是如何分配的,并且能够释放它。下面的例子假设字符串内存是在DLL中使用LocalAlloc()分配的,或一些间接的等价物映射到LocalAlloc()。
谢谢你!这对我的libmysql.dll也有效。我的MQL4从600版开始就不能与MySQL对话了。
最后,我至少可以看到MySql dll返回的内容...
你能给我们一个线索,如何实现相反的通信,即从MQL4(Build 600)传递一个字符串到只支持ANSI的dll? 我试过 这里 和 这里 的UNICODE2ANSI()函数 ,但不幸的是它对我不起作用。你能给我们一个线索,如何实现相反的通信,即从MQL4(build 600)向只支持ANSI的dll传递一个字符串?
谢谢你!"。对于简单的字符串值来说,它完全可以工作。至少MQL现在可以连接到MySQL。
然而,我似乎不能从一个dll中获得一个字符串数组。这是提供给我的函数(它是这个MQL4 MySql包装器)。
#import "mysql_wrapper.dll" void MT4_mysql_fetch_row (int result, string& row[]);
目前我收到 "Access violation read to 0x0... "的错误,当传递通常MQL的字符串阵列时。
非常感谢任何帮助。
模拟旧的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]); } }
例如,上面的代码与下面的DLL一起工作,它为数组中的每个字符串做一个消息框,然后在返回到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++; } }
(如果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); }
这适用于一个DLL,它只是把array[0]作为一个可以写入的缓冲区,例如。
__declspec(dllexport) void WINAPI Test(MqlStr * arr) { lstrcpyA(arr->data, "The string return value from the DLL"); }
编码员们好!
我在Delphi中写了一个DLL函数。这段代码只返回一个PChar文本到ex4。
我的MQ4代码。
这个方案在Build 509上有效,但在Build 600上就不行了。在Build 600上,专家的输出是:"DLL说:???????????x??????????????"
为什么在B600上不工作?你有什么办法能让我的代码正常工作吗?
谢谢你的帮助。
相对的