Forex Factory (https://www.forexfactory.com/forum.php)
-   Platform Tech (https://www.forexfactory.com/forumdisplay.php?f=69)
-   -   Strange String behaviour (https://www.forexfactory.com/showthread.php?t=208506)

theorem Nov 26, 2009 12:31pm | Post# 1

Strange String behaviour
 
Has anyone come across the following

I have a script with the following declarations

string symbol = "XXXXXX";
string open = "open"; string stop = "stop"; string takeP = "take";
double Levels[3];
int Data[1];

I then call a DLL declared as follows

void GetParmQuery(string symbol, string open, string stop, string takeP,
double& Levels[], int& Data[]);

The values returned from the DLL are as expected ie the default values for symbol, open, stop and takeP have been changed However if I then do the following test

if (open != "open")
Print("NE");
else
Print("EQ");

the test fails and I get "EQ" printed

Any ideas what is happening and can it be fixed without a messy work around

Thanks

7bit Nov 26, 2009 3:25pm | Post# 2

if (open != "open")
Print("NE");
else
Print("EQ");

the test fails and I get "EQ" printed
Why don't you tell us what is contained in the variable open after the call? Are you sure that you can pass strings as pointers (as you obviously do it), have them changed in your DLL and then still working in MT4 without producing an access violation? Do you allocate new memory for the new string in your DLL? Why don't you use string& instead to be able to return the new pointer then? Who is responsible for freeing this newly allocated memory? And the most important question: Why on earth are you doing this with strings at all?

theorem Nov 26, 2009 6:13pm | Post# 3

Why don't you tell us what is contained in the variable open after the call?
Basically it can be anything eg a string of 4 characters "abcd"


Are you sure that you can pass strings as pointers (as you obviously do it), have them changed in your DLL and then still working in MT4 without producing an access violation?
Well I do not have any AV's. and all works fine provided I allow sufficient memory for the variables in my script (ie that is why I set up default values first) at least as far as the returned values are concerned It is the comparison function that is failing However something is obviously incorrect in my assumptions and would like to know if I can pass strings in this manner as I do with doubles and integers (as arrays however )

Do you allocate new memory for the new string in your DLL?
If I understand correctly I am copying the data to the memory allocated by the script

Why don't you use string& instead to be able to return the new pointer then? Who is responsible for freeing this newly allocated memory?
Using string& does give an AV and as I understand all memory is managed by the script



And the most important question: Why on earth are you doing this with strings at all?
A bit confusing I agreebut I am usiing the Levels array to pass the actual values The strings are for another purpose altogether

7bit Nov 26, 2009 10:36pm | Post# 4

If I understand correctly I am copying the data to the memory allocated by the script
You cannot be sure how metatrader handles strings internally. Maybe your DLL will only receive a pointer to a C compliant (null terminated) copy of the string that will be discarded after the call and internally it stores strings in an entirely different way. Maybe it's heap manager will manage reference counted objects and create clones for certain operations, maybe internally they are Pascal strings and not C strings, or they used a sophisticated string library that operates entirely with unicode or something like that. Who knows? It isn't documented anywhere! You are trying to directly manipulate metatrader's own undocumented data structures via an undocumented interface!

What are you trying to do that can't be done the conventional way?

A bit confusing I agreebut I am usiing the Levels array to pass the actual values The strings are for another purpose altogether
Can't it be all encoded in integer values? If these strings represent a finite number of commands or states then just encode them as integers. Define a constant for every possible command or mode or state or whatever it is for better readability, just like it is done in metatrader too: (OP_BUY, OP_SELL, MODE_FOO, MODE_BAR, etc.). Using strings for things like that looks very strange. There is a reason why this is never done this way, anywhere else. (except PHP maybe, because the PHP kiddies don't know what a constant is)

theorem Nov 27, 2009 6:48am | Post# 5

What are you trying to do that can't be done the conventional way?
So what is the conventional way to pass multiple string values back from a DLL (I use Delphi7 by the way) I accept I can do a work around as you describe using integers but I had understood that strings could be passed as pointers to the memory block see

http://docs.mql4.com/basis/variables/extfunctions

Thanks

7bit Nov 27, 2009 7:16am | Post# 6

So what is the conventional way to pass multiple string values back from a DLL (I use Delphi7 by the way) I accept I can do a work around as you describe using integers but I had understood that strings could be passed as pointers to the memory block see

http://docs.mql4.com/basis/variables/extfunctions

Thanks
There is something written about pointers to memory blocks and something that sounds like Pascal strings, it doesnt really make sense (maybe it makes more sense in the russian original) and they dont talk about modifying the string for later use by the mql4 script at all.

What i can tell from my own experience is that strings passed to the DLL will simply be pointers to nullterminated strings. In Pascal you would use a PChar type for this. I have had only one instance where i ever felt the need to return a string from the dll to the script and that was when i wrote my python binding. The function returning the value of a python string object to the calling mql4 script looks like this:

Inserted Code
function PyGetString(item : PPyObject) : [color="Red"]PChar[/color]; stdcall;
var
  gs : TGILState;
begin
  gs := PyGILState_Ensure();
  PyGetString := PyString_AsString(item);
  PyGILState_Release(gs);
end;

(You can find the full source code (Lazarus/FPC) attached as a .rar file at the end of this site: http://sites.google.com/site/prof7bi...on-integration)

The above will return a pointer to the string (created by the python DLL), simply using the fuction return value. MT4 will immediately copy the string from the returned pointer into it's own memory.

When i recommended the conventional way i was referring to the conventional way for solving the problem, not the conventional way of returning strings. The conventional way of solving the problem (whatever it may be) would most likely not include any usage of strings returned to the script. Therefore here the question again: what are you trying to do?

rangebound Nov 27, 2009 7:41am | Post# 7

As 7Bit states you are manipulating memory that you (DLL) havent allocated and you dont know how MQL deals with allocating memory to that string or how it stores its dynamic size (length)

MQL

String tstr;

tstr = DLLreturnNullString();

This works because MQL is given a pointer to a nullTerm string (that your DLL allocated memory for) MQL then deals with allocating its own memory and internal allocation lists / string size list and copies the data into this new MQL memory. Everythings good because you let MQL build its own copy of the string.



MQL

String tstr;

DLLchangethisString(tstr);

This is dangerous for several reasons :

A) you dont know how much memory (if any) MQL has allocated to the pointer your DLL recieves.
B) If you change the string then MQL wont know it about it and if you change the length of it then MQL wont have updated its own 'knowlegde' of the length of the string
C) if you try to change or read the string and over step the memory that MQL allocated for it then you have a protection fault
D) In my experience whilst MQL will allow strings to be upto 64KB in size Im sure the memory is not always alocated as one contiguous block of memory.


If you do persist down this route (and id advise not to ) the best way is


MQL

int len;
String tstr=" " ;
// pad upto 255 characters at definition time so that memory is allocated

DLLchangethisString(len,tstr);
Print(StringSubstr(tstr,0,len));

This way your DLL passes back the new length of the string so you at least have some knowledge of its new length.



I've seen a C header that details the MQL string structure (but have never used it) which alludes to the fact that the string length is stored in an INT (4 Bytes) before The start of the string.
So in your DLL you might try decrementing the String pointer supplied by MQL (by 4 bytes) and writing an integer to this memory location to define the new length

HOWEVER
Ive never tried it / dont know if im correct on this & and I wouldnt advise doing this because you are messing with MQL's knowledge of what its done with this String if you change its length from 255 to 10 then MQL may well leak the memory that your DLL has made it ignorant of.

theorem Nov 27, 2009 8:08am | Post# 8

OK Guys thanks for the help it seems the MQL docs are somewhat unclear. I will now focus on returning new strings from the DLL (as a Function call in Delphi) rather than changing existing ones And I will look at limiting the use of strings in future as 7Bit suggests


© Forex Factory