Subclassing a Metatrader chart window

 
The following was prompted by a query from fbj in 'Can I code in MQL to turn on and off an already running EA? '

Posting this as a new topic to give it slightly wider attention, because I don't think it's been done before and it's a fun little experiment.

It demonstrates using a DLL to subclass a MetaTrader chart window so that an EA or script can see keyboard and mouse events. The example script lets you do the following (and exactly the same principle would work in an EA instead):
  • Buy at market, by pressing Ctrl+Shift+B or by holding down Alt while double-clicking on the "Buy" text
  • Sell at market, by pressing Ctrl+Shift+S or by holding down Alt while double-clicking on the "Sell" text
  • Close all pending and open orders for the chart symbol, by pressing Ctrl+Shift+C.
  • Increase the number of lots used for buys and sells, by pressing Ctrl+Shift+Plus or by holding down Alt while double-clicking on the up arrow.
  • Decrease the number of lots used for buys and sells, by pressing Ctrl+Shift+Minus or by holding down Alt while double-clicking on the down arrow.
  • Terminate the script, by pressing Ctrl+Shift+X.
You can install the example by putting the .mq4 file in experts\scripts and compiling it, and putting MQLSubclassExample.dll in experts\libraries. You need to turn on the "Allow DLL Imports" option. (If the script doesn't appear to do anything, or create any text on screen, then the DLL probably isn't installed in the right place.)

For comparison: it's just about possible to monitor the keyboard in an EA or script using API calls such as GetAsyncKeyState(), rather than going to the lengths of subclassing. However, there are then issues around having to hold down a key combination until the EA/script gets round to polling the keyboard state, which in turn leads to the possibility of unintended repeat key presses. And it's not possible to detect mouse double-clicks without this kind of subclassing.

Finally, and most importantly, do not use this on a live account. It's a quick and dirty little experiment, and hasn't been properly tested. May require a recent-ish build of MetaTrader. Tested on build 220.


Files:
 

good for you - this is just what "the many" need!

I spent the weekend at MSDN and in the end... got into a GetAsyncKeyState() play loop, course the weekend is hardly the time to mess about since ClientTerminal is in it's weekly state of denial phase ;)

For comparison: it's just about possible to monitor the keyboard in an EA or script using API calls such as GetAsyncKeyState(), rather than going to the lengths of subclassing. However, there are then issues around having to hold down a key combination until the EA/script gets round to polling the keyboard state, which in turn leads to the possibility of unintended repeat key presses. And it's not possible to detect mouse double-clicks without this kind of subclassing.

The ramifications of above are legion from what my play time showed.

  1. Assuming I understand the specs, GetAsyncKeyState() will give you the last device input.
  2. eg, my test script created text label objects to shove out on screen - whatever key (of interest that is) was touched. Followng each script run, I'd study the screen o/p and then call up objects window via ctrl-B > ctrl-A > alt-D > esc. That sequence quickly removed all objects. Sometimes I just did the ctrl-B and then used mouse clicks to do same thing. What I'm trying to say is that when the script was next run, the first GetAsyncKeyState() picked up on VK_CONTROL, which my script dully recognised as of interest and hence created screen object o/p.
  3. ? does this imply that 'first off' GetAsyncKeyState() call should be a dummy to, as it were, flush the unwanted TOS item. Am assuming here that is a one element fifo stack, ie. only one: the last keystroke after/since the previous call to GetAsyncKeyState(). The spec seems to be saying as much...
  4. Time/polling/unintended key presses... my script showed that asap response totally depended on
  • the delay used between polls
  • other unknown interaction which caused objects to not appear on screen until the script was removed - as monitored by the Terminal > Experts tab
  • perhaps a simple Print() instead of objects to display whatever the script saw would be more realistic? - and less intensive regards ClientTerminal interactions...

TBH, DLL's are obviously a Windows I/F, using time honored mechanisms. But, I'd like to encapsulate ALL functionality within my ClientTerminal program, be it EA, Script or Indicator.

To that end, I'm certainly going to play with jjc's kind offering but...


jjc - a favor to ask of you

please can you publish the DLL source code?

why? well, transparency and perhaps more importantly for me at least, is the education/learning aspect.

As most are aware, it is easy to get various C/C++ compilers [and of course my favorite - MASM!!!] so that building DLL's could be seen as fair game for any intrepid and interested party, yes?

smry: with jjc's source code - perhaps in a [please... :) generic sort of C'esque syntax] and any other supporting? files needed to build the DLL (not my area but the name resource file popped up) etc.


edit:

WOW... ~500lines of education. Best part is the 'comments'. A feature I have not seen in public code for many moons! jjc, THANK YOU

 

fbj wrote >>

Assuming I understand the specs, GetAsyncKeyState() will give you the last device input.

That's more a description of GetKeyState() than GetAsyncKeyState(). The latter is supposed to be more reliable in terms of giving you the state at the time you actually call the function. However...


? does this imply that 'first off' GetAsyncKeyState() call should be a dummy to, as it were, flush the unwanted TOS item.

You may have missed out on part of the documentation - and there's a lot of very bad example code on the web, giving incorrect illustrations of how to use GetAsyncKeyState(). For example, a simple conditional test such as (GetAsyncKeyState(VK_CONTROL)) ... is wrong. Both GetKeyState() and GetAsyncKeyState() return a 16-bit value where the top bit is set if the key is currently pressed. Therefore, the test for Ctrl currently being held down should be ((GetAsyncKeyState(VK_CONTROL) & 32768) != 0)


Time/polling/unintended key presses... my script showed that asap response totally depended on [...]

As a matter of further interest... I wrote my example code while the market was closed. Having now seen what happens internally within MetaTrader when the market is open, I've found that it's possible to simulate market ticks. In other words, if a keyboard or mouse event happens over the weekend, the DLL could force the EA's start() function to run despite the market being closed. Obviously the EA wouldn't be able to trade, but it could at least handle the keyboard (or mouse) event immediately. That gives it an even bigger advantage over polling the keyboard state from within an EA/script.


jjc - a favor to ask of you

please can you publish the DLL source code?

I'm sorry, but I'm obliged to give a few clients first-refusal over this kind of thing before I make it publicly available.


I hope it's useful just to be aware that it is possible, and that it's actually pretty easy (given the requisite skill-set). Anyone with a solid background in Windows API development should be able to replicate my DLL very quickly. As compensation for not being able to publish the code (yet), I'll throw in a pointer about something which I wasted a lot of time over: it turns out that an EA/script's deinit() function gets called during the handling of WM_DESTROY. Therefore, you've have to work round MetaTrader unloading the DLL during deinit() while WM_DESTROY is being processed, in the DLL...

 

The conditional test with setting of intermediate bitmask prior <>0 is what I did not do... getting sloppy in old age. Thank you for highlighting this.

Observations about non-trade hours is going to spark lots of ideas about leveraging them.

Private code status understood.

Hints make one study hard to understand and finally locate path through the maze...

Armed with Petzold and numerous data sources on web and my predilection for assembler/C I'll forge ahead. Is appreciated, the solid background reference, yet that will never be nor is it wanted. Is enough for me to be completely in sync with the simple code aspects viz: fielding keystrokes to MQL program and requisite Windows API functionality to do such (how confident are these fledglings!). The functionality which you so kindly demonstrated is not required beyond the keystroke stuff.... so far :)


To see, to hear... that is often times enough to allow those with persistency to push beyond their current boundaries. Well... that's my theory anyway :)

 

Hi,

nice script ! tx u.

When i click, object settings window is appear. Why ? Anyway for hide this ?

Best regards.

S.T

 

jjc,

Is the subclassing DLL code still private? It would be really useful if you could publish it here.

thanks,

Vital

 

Looks interesting but if the warning messagebox from the DLL is not stripped out, I am afraid nobody coud make use of this piece of code.

So JJC it would be a good idea to remove it, as the standard ComCtl32 subclassing functions wouldn't work in MT4 for it seems a procedure pointer is needed.

Reason: