
DLLを経由したメタトレーダーターミナルの管理
タスクの定義
4つ以上の通知用アドレスを含むクウォーツIDがあります。SendNotification の関数はオプションの通知タブでIDをセットするだけで使えます。MQLを使えば一度に4つまで設定したIDへプッシュメール通知を送ることができます。これを使えるようにしましょう。
設定には2つの方法があります。ターミナルの設定を変えたり標準の関数を利用したりすることでプッシュメール通知機能を開発することができます。最初の方法はとても時間がかかり汎用性を欠いています。したがって2番目の方法を選択しましょう。ターミナルの設定はまた、色々な方法で変えることができます。私の経験上、ユーザーインターフェイスを経由するかプロセスメモリを置き換えるかして設定しています。ウィンドウがフラッシュすることが避けられるのでメモリの働きは良く見えます。しかし、全体のターミナルの機能が僅かなミスで中断されてしまします。UIを経由して稼働している時にウィンドウやボタンが消えてしまう最悪の事態が起こり得ます。
本稿で、補助DLLライブラリを使ったユーザーインターフェイス経由のターミナルを扱いましょう。特に、設定の変更を見ていきます。ターミナルについては通常のやり方でやっていきます。ターミナルについては通常のやり方でやっていきます。それはウィンドウズとコンポーネントを使うということです。ターミナルプロセスの中断はありません。この方法は他の問題を解決することにも使えます。
1. DLLを作る
ここでは主にWinAPIについて見ていきます。どのようにしてダイナミックなライブラリがDelphiで開発できるか手短に試してみましょう。
library Set_Push; uses Windows, messages, Commctrl, System.SysUtils; var windows_name:string; class_name:string; Hnd:hwnd; {$R *.res} {$Warnings off} {$hints on} function FindFunc(h:hwnd; L:LPARAM): BOOL; stdcall; begin ... end; function FindFuncOK(h:hwnd; L:LPARAM): BOOL; stdcall; begin ... end; function Find_Tab(AccountNumber:integer):Hwnd; begin ... end; function Set_Check(AccountNumber:integer):boolean; export; stdcall; var HndButton, HndP:HWnd; i:integer; WChars:array[0..255] of WideChar; begin ... end; function Set_MetaQuotesID(AccountNumber:integer; Str_Set:string):boolean; export; stdcall; begin ... end; //--------------------------------------------------------------------------/ Exports Set_Check, Set_MetaQuotesID; begin end.
ご覧の通り、Set_CheckとSet_MetaQuotesIDがエクスポートされています。FinfFuncは必要なウィンドウ(以下に記述)を探します。Find_Tabは必要なタブを探します。ウィンドウズとメッセージ、コマンドコントロールライブラリはWinAPIを利用することで使えるようになります。
1.1. アプリケーションツール
このタスクを解決する原則はDelphi XE4環境におけるWinAPIを利用することです。WinAPI文法とほとんど同じなので、C++言語も使われます。コンポーネントのネームとクラスの検索は、Visual Studioのデリバリーに含まれるSpy++のユーティリティーの利用か、以下の一覧表によって機能します。
1.2. メタトレーダーウィンドウをの検索
どんなプログラムウィンドウもそのタイトルで探すことができます(図1参照)。
図1. ウィンドウタイトル
メタトレーダーウィンドウのタイトルは口座番号を含んでおり、タイトル自体はセレクトされたシンボルや時間軸に基づき変えられています。したがって検索は口座番号のみでなされます。. 後の方でオプションウィンドウについても見ていきます。それは書き換えできないタイトルを持っています。
最初のケースにおいて、全ての利用可能なウィンドウを列挙するEnumWindowsの関数を利用しましょう。列挙されたウィンドウを処理する関数はEnumWindossのパラメーターとしてパスされます。それはFindFunc関数です。
function FindFunc(h:hwnd; L:LPARAM): BOOL; stdcall; var buff: ARRAY [0..255] OF WideChar; begin result:=true; Hnd := 0; GetWindowText(h, buff, sizeof(buff)); if((windows_name='') or (pos(windows_name, StrPas(buff))> 0)) then begin GetClassName(h, buff, sizeof(buff)); if ((class_name='') or (pos(class_name, StrPas(buff))> 0)) then begin Hnd := h; result:=false; end; end; end;
この関数をもっと詳しくみていきましょう。関数のヘッダーは、関数と変数名をのぞいて変えらていません。新しいウィンドウが検知されると、EnumWindows関数が指定の関数を呼び出しそれをウィンドウハンドルに渡します。もし指定の関数がtrueならば、列挙する作業は続きます。そうでなければ、作業は完了します。
受け取ったハンドルを回路にコピーてウィンドウタイトル(GetWindowText)とクラスネーム(GetClassName)を調べます。. 次にウィンドウ抱いとるとクラスを必要としているもの比較します。. もし適合したら、ハンドル(これが最も重要)を記憶しfalseを返して列挙を終了します。
ここで、EnumWindows関数の呼び出しについてみていきます。
windows_name:=IntToStr(AccountNumber); class_name:='MetaTrader'; EnumWindows(@FindFunc, 0);
必要とするクラスと部分的なウィンドウタイトルの値を割り当てます。. すべての利用可能なウィンドウを列挙する関数を呼び出します。その結果、Hnd グロバール変数で主なウィンドウタイトルを受け取ります。
まだ見つかっていないウィンドウズを探す関数をみていきます。ターミナルの設定を変える必要があるので、適切なオプションメニューを選択し新しいオプションウィンドウをみていかなければなりません。ウィンドウを探す別の方法があります。
hnd:=FindWindow(nil, 'Options');
クラスネームとウィンドウタイトルは関数のパラメーターとして扱われます。返された値は必要なハンドルか見つからなければ0になります。前のケースでは、関数は一連の出現の代わりに正確に適合する名前を探します。
1.3. メニューの稼働
全ての他のコンポーネントと同様に、メニューの稼働は親ハンドル(特定のウィンドウ)が見つかるとスタートします。対応するメニューアイテムとサブアイテムをを探しセレクションを機能させましょう。
注釈: ターミナルメニューアイテムはチャートウィンドウが展開されているかいないかによって変わります(図2参照)。アイテムの列挙は0からスタートします。アイテムの列挙は0からスタートします。
/p>
図2メニューアイテムの変更
メニューアイテムが変更さっると、ツールアイテムのインデックス番号も変更されます。したがって、メニューハンドルがパスされるGetMenuItemCount(Hnd:Hmenu)関数を利用するすべてのポイントを考慮すべきです。
以下の例をみていきましょう。:
function Find_Tab(AccountNumber:integer; Language:integer):Hwnd; var HndMen :HMenu; idMen:integer; ... begin ... //_____working in the menu________ HndMen:=GetMenu(Hnd); if (GetMenuItemCount(HndMen)=7) then HndMen:=GetSubMenu(HndMen,4) else HndMen:=GetSubMenu(HndMen,5); idMen:=GetMenuItemID(HndMen,6); if idMen<>0 then begin PostMessage(Hnd,WM_COMMAND,idMen,0); ...
この例では、メインメニューのハンドルをその親ハンドルを経由して探します。次に適切なサブメニューをメニューハンドルで探します。サブメニューインデックス番号はGetSubMenu関数の2番目のパラメーターとして使われます。そして、適切なサブメニューアイテムを探します。選別を機能させるため、適切なメッセージを送る必要があります。メーセージを送った後、オプションウィンドウ待たねばなりません。
for i := 0 to 10000 do hnd:=FindWindow(nil, 'Options');
永久ループを設定することは推奨しません。どんなにプログラムを素早く機能することができてもウィンドウを閉じるとターミナルクラッシュの原因になるからです。
1.4. コンポーネントの検索
オプションウィンドウを得て、次はコンポーネントまたは(WinAPIを使って)子ウィンドウを宣言する必要がります。しかし最初に、それらはハンドルを使って探すべきでしょう。「子ウィンドウ」という言葉は、ウィンドウを探す同じやり方で探すことからそう呼ばれています。
windows_name:='ОК'; class_name:='Button'; EnumChildWindows(HndParent, @FindFunc, 0);
又は
Hnd:=FindWindowEx(HndParent, 0, 'Button', 'OK');
結果、コンポーネントを検索する主な例を得ました。このステージでは、変更した変数名や親ハンドルの追加パスなどの複雑な物事には出会いません。通常、困難なことが現れるのは、検索が行なわれることでコンポーネントの特性を考慮しコンポーネントタイトルやクラスを知る必要があるかどうかの場合です。. そのようなケースでは、Spy++ユーティリティーが助けになります。全ての値をディスプレイすることをフォローして全ての親ウィンドウコンポーネントを列挙することと同じです。これを達成するために、パス関数(FindFunc)を少し変更しなければいけません。すべてのケースで返された値をtrueにセットし、ウィンドウネームとそれたのクラス(例えばファイルに書き込むなどして)保存します。
コンポーネント検索の特徴の一つをみていきましょう。: OKはシステムボタンです。これはボタンテキストは英語バージョンのウィンドウズOSではラテン語で書かれていることを意味します。ロシア語バージョンはキリル文字で書かれています。そのため、この解決策は普遍的なのです。
検索はネームの長さは(少なくともラテン文字とキリル文字が使われる言語では)二つの文字からなっています。これはライブラリをより多様にしてくれます。このコードの検索関数は以下です。:
function FindFuncOK(h:hwnd; L:LPARAM): BOOL; stdcall; var buff: ARRAY [0..255] OF WideChar; begin result:=true; Hnd := 0; GetClassName(h, buff, sizeof(buff)); if (pos('Button', StrPas(buff))> 0) then begin GetWindowText(h, buff, sizeof(buff)); if(Length(StrPas(buff))=2) then begin Hnd := h; result:=false; end; end; end;
OKボタンの検索は以下の方法です。:
EnumChildWindows(HndParent, @FindFuncOK, 0);
1.5. コンポーネントの稼働
全てのアクションの出力結果として以下のウィンドウを受け取るでしょう。(図3):
図3オプションウィンドウ
タブコントロール
ウィンドウはマルチタブを含んでおり、要求されたものが選ばれているか知ることができません。タブの設定によったコンポーネントはタブコントロールです。このケースでは、そのクラスを示すSysTabControl32です。そのハンドルを探しましょう。オプションウィンドウは親ウィンドウとして使われています。:
Hnd:=FindWindowEx(Hnd, 0, 'SysTabControl32', nil);
次にタブ変更メッセージをこのコンポーネントに送ります。:
SendMessage(Hnd, TCM_SETCURFOCUS, 5, 0);
上の例では、5は必要なタブの索引番号です(通知)。ここで必要なタブを探しましょう。:
Hnd:=GetParent(Hnd); Hnd:=FindWindowEx(Hnd, 0, '#32770', 'Notifications');
オプションウィンドウはアクティブタブにとって親として扱われます。TabControlハンドルをもっているので、その親ハンドル(ウィンドウ)をもつことができます。その後、要求されたタブの検索が行なわれます。ここではタブのクラスは#32770です。
チェックボックス
オプションウィンドウは「Enable Push Notifications」を持ちます。もちろん、ユーザーが全て正確に設定できるとは思えません。有効化/無効化によるコンポーネントはボタンクラスを持ち、そのコンポーネントのタイプのため特別にデザインされたメーッセージがあります。
最初に、コンポーネントを検索しましょう。通知タブは親として振る舞います。コンポーネントは見つかったら、通知が許可されるかどうか(オプションがチェクされたかどうか)チェックします。チェックされていなければチェックします。全てのオブジェクトとのアクションはメッセージを送ることによって行なわれます。
Hnd:=FindWindowEx(Hnd, 0, 'Button', 'Enable Push Notifications'); if(Hnd<>0) then begin if (SendMessage(Hnd,BM_GETCHECK,0,0)<>BST_CHECKED) then SendMessage(Hnd,BM_SETCHECK,BST_CHECKED,0); ...
エディット
このコンポーネントはメタクウォーツIDのアドレスを入力するためのフィールドです。親は通知タブで、クラスはエディットです。オペレーション規則は同じです。コンポーネントを探しメッセージを送ります。
Hnd:=FindWindowEx(Hnd, 0, 'Edit', nil); if (Hnd<>0) then begin SendMessage(Hnd, WM_Settext,0,Integer(Str_Set));
where Str_Set is a list of string addresses.
ボタン
ここでオプションウィンドウのボタンにおいて、標準のOKボタンをみていきます。このコンポーネントはどのタブにも属していません。つまりその親はウィンドウそれ自体だということです。全ての必要なアクションを終えた後、ボタンを押したメッセージを送ります。
EnumChildWindows(HndParent, @FindFuncOK, 0); I:=GetDlgCtrlID(HndButton); if I<>0 then begin SendMessage(GetParent(HndButton),WM_Command,MakeWParam(I,BN_CLICKED),HndButton); ...
2. MQL4でスクリプトを作成する
ここでの作業の結果は、プッシュメール通知を送り呼び出したリストからメタクウォーツIDを入力させる2つの外部関数、Set_CheckとSet_MetaQuotesIDを使ったDLLです。. 全てのターミナルウィンドウとコンポーネントは関数で見つけることができます。それらはtrueを返します。どのようにスクリプトに使われているかみてみましょう。
//+------------------------------------------------------------------+ //| Send_Push.mq4 | //| Copyright 2015, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2015, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #property strict #property show_inputs #import "Set_Push.dll" bool Set_Check(int); bool Set_MetaQuotesID(int,string); #import extern string text="test"; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void OnStart() { if(StringLen(text)<1) { Alert("エラー:送れたテキストはありません"); return; } if(!Set_Check(AccountNumber())) { Alert("エラー: プッシュを送れませんでした。ターミナル言語をチェックしてください"); return; } string str_id="1C2F1442,2C2F1442,3C2F1442,4C2F1442"; if(!Set_MetaQuotesID(AccountNumber(),str_id)) { Alert("エラー:dll実行エラー!プロセス中断の可能性があります"); return; } if(!SendNotification(text)) { int Err=GetLastError(); switch(Err) { case 4250: Alert("注意: プッシュを送れませんでした。", str_id); break; case 4251: Alert("エラー: 無効なメッセージテキストです", text); return; break; case 4252: Alert("注意:無効なIDリストです。", str_id); break; case 4253: Alert("Err: Too frequent requests! "); return; break; } } } //+------------------------------------------------------------------+
まとめ
DLLを経由したターミナルウィンドウを設定する基本原則についてみてきました。全てのターミナルの機能をより効果的に使えるようになるでしょう。しかし。この方法は、一般的な手法では解決できない場合の最後の手段とするべきです。いくらか欠点があり、選択されたターミナル言語にも依存し、ユーザーの介入や実行の複雑さにも依るからです。もし失敗したら致命的なエラーかさもなくばプログラムのクラッシュを招くことになるでしょう。
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/1903





- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索