2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

MetaTraderAdvent Calendar 2022

Day 10

MetaTraderのアラートを盗む方法(MQL4

Posted at

目的

他のインジケータやEAのアラートを盗む。

アラートとは?

この鈴がついたウィンドウです!

image.png

アラートの正体

結論から言うと,MetaTraderのアラートの正体は,ListViewです。
なぜListViewとわかったかというのは,spy++というソフトを使えばわかります。

ググればすぐでてきますが,VisualStudioに同梱されている便利ツールです。

こいつをつかって,アラートは何者なのか,スクロールできるあの窓は何で構成されているのか調べることができます。

環境によって違うのかもしれませんが,時間と共に表示されているのはListViewで,アラート自身のウィンドウは,#32770というのがキーです。

MQL4

このあたりのマクロ定義は決まったお作法のようなものなので・・・。

#define LVM_GETITEMCOUNT               0x1004
#define LVM_GETITEMW                   0x1005
#define LVM_GETHEADER                  0x101F
#define LVIF_TEXT                      0x0001

#define HDM_GETITEMCOUNT               0x1200

#define PROCESS_VM_OPERATION           0x0008
#define PROCESS_VM_READ                0x0010
#define PROCESS_VM_WRITE               0x0020

#define MEM_RESERVE                    0x2000
#define MEM_DECOMMIT                   0x4000
#define MEM_RELEASE                    0x8000
#define MEM_COMMIT                     0x1000

#define PAGE_READWRITE                 0x0004

#define BM_CLICK                       0x00F5//ユーザーがボタンをクリックした。

次に,MQL側からWin32API用のDLLを呼びます。

#import "user32.dll"
int  SendMessageA(int hWnd,int Msg,int wParam,int lParam);
int  SendMessageW(int hWnd,int Msg,int wParam,int lParam);
int  PostMessageW(int hWnd,int Msg,int wParam,int lParam);
int  FindWindowW(string lpClassName,string lpWindowName);
int  FindWindowExW(int hwndParent,int hwndChildAfter,string lpClassName,string lpWindowName);
int  GetWindowThreadProcessId(int hWnd, int &lpProcessId[]);

#import "kernel32.dll"
int OpenProcess(int dwDesiredAccess,bool bInheritHandle,int dwProcessId);
int ReadProcessMemory(int hProcess,int lpBaseAddress,char &lpBuffer[],int nSize,int lpNumberOfBytesRead);
int  WriteProcessMemory(int hProcess, int lpBaseAddress, int &lpBuffer[], int size, int &written);

//メモリ展開 解放
int VirtualAllocEx(int hProcess,int lpBaseAddress, int dwSize, int flAllocationType,int flProtect);
int VirtualFreeEx(int hProcess,int lpAddress,int dwSize,int dwFreeType);
int CloseHandle(int hObject);
int GetCurrentProcessId();
int GetCurrentThreadId();
#import

不要なアラートを拾わないようにメンバーに以下を宣言しておきます。

input string C5="---- アラート設定 ----";//---- アラート設定 ----
input bool mtAlertEnable = false; //MetaTrader(スマホアプリ)の通知可否
input bool myAlertEnable = false; //標準アラートを自動で閉じるかどうか
input string C6="---- フィルタ設定 ----";//---- フィルタ設定 ----
input bool fileterEnable =true;//フィルタ有効?(true:有効/false:無効)
input string filter1="Line";//フィルタ1
input string filter2="";//フィルタ2
input string filter3="";//フィルタ2

まずは,アラートを取得する関数です。

void getAlert()
  {

   if(drows > 0)
     {
      hwnd = FindWindowW("#32770","アラート");
      hwndList = FindWindowExW(hwnd,0,"SysListView32","List1");
      rows = SendMessageA(hwndList,LVM_GETITEMCOUNT,0,0);//リストビューの行数の取得
      myhwndListHeader = SendMessageA(hwndList, LVM_GETHEADER,0,0);//リストビューヘッダのハンドルを取得
      colums = SendMessageA(myhwndListHeader,HDM_GETITEMCOUNT, 0, NULL);//リストビューの列数の取得

      //Print("ListViewの行数の取得"+(string)rows+" 列数は"+(string)colums);
      int LVITEM[10];
      int qid[1];
      tid = GetWindowThreadProcessId(hwnd,qid);
      pid=qid[0];

      //プロセスハンドラを取得する。
      hProc = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, False, pid);

      ////共有メモリ確保
      //int lpShared1 = GetSharedMem(pid, sizeof(LVITEM), hProc);
      //int lpShared2 = GetSharedMem(pid, 255, hProc);
      //int lWritten=0;

      //printf("OpenProcess %d",hProc);
      //printf("共有メモリ確保 lpShared1_%d ,lpShared2_%d hProc %d",lpShared1,lpShared2,hProc);
      uchar strBuffer[255];
      for(int i=rows-drows-1; i>=0; i--)
        {
         string qtr="";
         for(int j=1; j<colums; j++)
           {
            //共有メモリ確保
            int lpShared1 = GetSharedMem(pid, sizeof(LVITEM), hProc);
            int lpShared2 = GetSharedMem(pid, 255, hProc);
            int lWritten=0;


            LVITEM[0] = LVIF_TEXT;
            LVITEM[1] = i;
            LVITEM[2] = j;//項目のインデックス(列
            LVITEM[5] = lpShared2;
            LVITEM[6] = 255; // textmask

            int intRc= WriteProcessMemory(hProc, lpShared1,LVITEM, sizeof(LVITEM), lWritten);//書込み
            if(intRc ==0)
               continue;

            intRc = SendMessageW(hwndList,LVM_GETITEMW,0,lpShared1);//取得依頼
            if(intRc ==0)
               continue;

            intRc = ReadProcessMemory(hProc,lpShared2, strBuffer, 255, 0); //読込
            if(intRc ==0)
               continue;

            qtr += CharArrayToString(strBuffer,0,255,   CP_THREAD_ACP)+" ";
            //qtr = ShortArrayToString(strBuffer,0,255);

            //メモリ解放
            FreeSharedMem(hProc, lpShared1, sizeof(LVITEM));
            FreeSharedMem(hProc, lpShared2, 255);

           }
         int f1,f2,f3;
         int ff1,ff2,ff3;
         ff1 = StringLen(filter1);
         ff2 = StringLen(filter2);
         ff3 = StringLen(filter3);
         f1 = StringLen(filter1) > 0 ? StringFind(qtr,filter1,0):-1;
         f2 = StringLen(filter2) > 0 ? StringFind(qtr,filter2,0):-1;
         f3 = StringLen(filter3) > 0 ? StringFind(qtr,filter3,0):-1;
         if(fileterEnable)
           {
            if(f1>=0||f2>=0||f3>=0)
              {
               myAlert(qtr);
               Print("アラートをスティールしました→,",qtr);
              }
           }
         else
           {
            myAlert(qtr);
            Print("アラートをスティールしました→,",qtr);
           }
        }

      ////メモリ解放
      //FreeSharedMem(hProc, lpShared1, sizeof(LVITEM));
      //FreeSharedMem(hProc, lpShared2, 255);
     }
   drows = rows;
//Print("END");
  }

ListViewの中身は,共有メモリ内にあるようで,そこにアクセスできればListViewの中身を取りに行けます。
共有メモリへのアクセスには,

GetSharedMem(int mypid, int memSize, int  ahProc)

アクセスしたあとは,手を離さないといけないので,

FreeSharedMem(int ahProc,int MemAddress,int memSize)

を読んでいます。

それぞれの実装は次になります。

//+------------------------------------------------------------------+
//| 共有メモリ確保                                                        |
//+------------------------------------------------------------------+
int GetSharedMem(int mypid, int memSize, int  ahProc)
  {
   return VirtualAllocEx(ahProc, 0, memSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
  }

//+------------------------------------------------------------------+
//|  共有メモリ解放                                                       |
//+------------------------------------------------------------------+
int FreeSharedMem(int ahProc,int MemAddress,int memSize)
  {
   VirtualFreeEx(ahProc, MemAddress, memSize, MEM_RELEASE);
   return CloseHandle(ahProc);
  }
//+------------------------------------------------------------------+

あとは

myAlert(string msg)

で,煮るなり焼くなりすればいいわけです。

//+------------------------------------------------------------------+
//| チャート送ったり通知したりするところのまとめ                                      |
//+------------------------------------------------------------------+
void myAlert(string msg)
  {
   datetime now =TimeLocal();
   MqlDateTime now_struct;
   TimeToStruct(now, now_struct);
   string myNow = StringFormat("%4d-%02d-%02d_%02d-%02d-%2d",
                               now_struct.year,
                               now_struct.mon,
                               now_struct.day,
                               now_struct.hour,
                               now_struct.min,
                               now_struct.sec);


   if(mtAlertEnable)
     {
      SendNotification(myNow+" "+ msg);
     }

   if(myAlertEnable)
     {
      int hwndButton = FindWindowExW(hwnd,0,"Button","OK");//アラートのウィンドウを閉じる。
      PostMessageW(hwndButton,BM_CLICK,0,0);
     }

  }
//+------------------------------------------------------------------+

注意事項

アラートの量にもよりますが,1週間も稼働させればMetaTraderが固まります!

コード全部


#property strict
#property indicator_chart_window

#define LVM_GETITEMCOUNT               0x1004
#define LVM_GETITEMW                   0x1005
#define LVM_GETHEADER                  0x101F
#define LVIF_TEXT                      0x0001

#define HDM_GETITEMCOUNT               0x1200

#define PROCESS_VM_OPERATION           0x0008
#define PROCESS_VM_READ                0x0010
#define PROCESS_VM_WRITE               0x0020

#define MEM_RESERVE                    0x2000
#define MEM_DECOMMIT                   0x4000
#define MEM_RELEASE                    0x8000
#define MEM_COMMIT                     0x1000

#define PAGE_READWRITE                 0x0004

#define BM_CLICK                       0x00F5//ユーザーがボタンをクリックした。



//

input string C5="---- アラート設定 ----";//---- アラート設定 ----
input bool mailAlertEnable = false; //メールの通知可否
input bool mtAlertEnable = false; //MetaTrader(スマホアプリ)の通知可否
input bool myAlertEnable = false; //標準アラートを自動で閉じるかどうか
input string C6="---- フィルタ設定 ----";//---- フィルタ設定 ----
input bool fileterEnable =true;//フィルタ有効?(true:有効/false:無効)
input string filter1="AUTO";//フィルタ1
input string filter2="";//フィルタ2
input string filter3="";//フィルタ2

input string C7="---- 通知除外時間はパソコンの時間です。 ----";//---- 通知除外時間 ----
input string C8="---- trueなら除外 falseなら通知します。 ----";//---- 通知除外時間 ----
input bool exclosion00 = false;//00時
input bool exclosion01 = false;//01時
input bool exclosion02 = false;//02時
input bool exclosion03 = false;//03時
input bool exclosion04 = false;//04時
input bool exclosion05 = false;//05時
input bool exclosion06 = false;//06時
input bool exclosion07 = false;//07時
input bool exclosion08 = false;//08時
input bool exclosion09 = false;//09時
input bool exclosion10 = false;//10時
input bool exclosion11 = false;//11時
input bool exclosion12 = false;//12時
input bool exclosion13 = false;//13時
input bool exclosion14 = false;//14時
input bool exclosion15 = false;//15時
input bool exclosion16 = false;//16時
input bool exclosion17 = false;//17時
input bool exclosion18 = false;//18時
input bool exclosion19 = false;//19時
input bool exclosion20 = false;//20時
input bool exclosion21 = false;//21時
input bool exclosion22 = false;//22時
input bool exclosion23 = false;//23時

#import "user32.dll"
//---- messages
int      SendMessageA(int hWnd,int Msg,int wParam,int lParam);
int      SendMessageW(int hWnd,int Msg,int wParam,int lParam);
int      PostMessageW(int hWnd,int Msg,int wParam,int lParam);
//lpClassName・・・クラス名
//lpWindowName・・・ウィンドウ名
//return ウィンドウハンドル
int      FindWindowW(string lpClassName,string lpWindowName);

//hwndParent・・・親Windowのハンドル
//hwndChildAfter・・・検索を開始する子Windowのハンドル。「0」の場合、最初の子Windowから検索
//lpClassName・・・クラス名
//lpWindowName・・・ウィンドウ名
//return ウィンドウハンドル
int      FindWindowExW(int hwndParent,int hwndChildAfter,string lpClassName,string lpWindowName);

int  GetWindowThreadProcessId(int hWnd, int &lpProcessId[]);

#import "kernel32.dll"
int OpenProcess(int dwDesiredAccess,bool bInheritHandle,int dwProcessId);
int ReadProcessMemory(int hProcess,int lpBaseAddress,char &lpBuffer[],int nSize,int lpNumberOfBytesRead);
int  WriteProcessMemory(int hProcess, int lpBaseAddress, int &lpBuffer[], int size, int &written);

//メモリ展開 解放
int VirtualAllocEx(int hProcess,int lpBaseAddress, int dwSize, int flAllocationType,int flProtect);
int VirtualFreeEx(int hProcess,int lpAddress,int dwSize,int dwFreeType);
int CloseHandle(int hObject);
int  GetCurrentProcessId();
int  GetCurrentThreadId();
#import


int hwnd ;
int hProc;//ぷろせすはんどる
int hwndList;
int rows;
int myhwndListHeader;
int colums;
int drows;
int pid;//プロセスID
int tid;//スレッドID

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping

   Alert("TEST_TEST_TEST_TEST_TEST_TEST_TEST");
   drows=99999999;
   getAlert();
//---

   EventSetTimer(5);
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   EventKillTimer();

  }

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+



//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
   getAlert();

  }


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void getAlert()
  {

   if(drows > 0)
     {
      hwnd = FindWindowW("#32770","アラート");
      hwndList = FindWindowExW(hwnd,0,"SysListView32","List1");
      rows = SendMessageA(hwndList,LVM_GETITEMCOUNT,0,0);//リストビューの行数の取得
      myhwndListHeader = SendMessageA(hwndList, LVM_GETHEADER,0,0);//リストビューヘッダのハンドルを取得
      colums = SendMessageA(myhwndListHeader,HDM_GETITEMCOUNT, 0, NULL);//リストビューの列数の取得

      //Print("ListViewの行数の取得"+(string)rows+" 列数は"+(string)colums);
      int LVITEM[10];
      int qid[1];
      tid = GetWindowThreadProcessId(hwnd,qid);
      pid=qid[0];

      //プロセスハンドラを取得する。
      hProc = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, False, pid);

      ////共有メモリ確保
      //int lpShared1 = GetSharedMem(pid, sizeof(LVITEM), hProc);
      //int lpShared2 = GetSharedMem(pid, 255, hProc);
      //int lWritten=0;

      //printf("OpenProcess %d",hProc);
      //printf("共有メモリ確保 lpShared1_%d ,lpShared2_%d hProc %d",lpShared1,lpShared2,hProc);
      uchar strBuffer[255];
      for(int i=rows-drows-1; i>=0; i--)
        {
         string qtr="";
         for(int j=1; j<colums; j++)
           {
            //共有メモリ確保
            int lpShared1 = GetSharedMem(pid, sizeof(LVITEM), hProc);
            int lpShared2 = GetSharedMem(pid, 255, hProc);
            int lWritten=0;


            LVITEM[0] = LVIF_TEXT;
            LVITEM[1] = i;
            LVITEM[2] = j;//項目のインデックス(列
            LVITEM[5] = lpShared2;
            LVITEM[6] = 255; // textmask

            int intRc= WriteProcessMemory(hProc, lpShared1,LVITEM, sizeof(LVITEM), lWritten);//書込み
            if(intRc ==0)
               continue;

            intRc = SendMessageW(hwndList,LVM_GETITEMW,0,lpShared1);//取得依頼
            if(intRc ==0)
               continue;

            intRc = ReadProcessMemory(hProc,lpShared2, strBuffer, 255, 0); //読込
            if(intRc ==0)
               continue;

            qtr += CharArrayToString(strBuffer,0,255,   CP_THREAD_ACP)+" ";
            //qtr = ShortArrayToString(strBuffer,0,255);

            //メモリ解放
            FreeSharedMem(hProc, lpShared1, sizeof(LVITEM));
            FreeSharedMem(hProc, lpShared2, 255);

           }
         int f1,f2,f3;
         int ff1,ff2,ff3;
         ff1 = StringLen(filter1);
         ff2 = StringLen(filter2);
         ff3 = StringLen(filter3);
         f1 = StringLen(filter1) > 0 ? StringFind(qtr,filter1,0):-1;
         f2 = StringLen(filter2) > 0 ? StringFind(qtr,filter2,0):-1;
         f3 = StringLen(filter3) > 0 ? StringFind(qtr,filter3,0):-1;
         if(fileterEnable)
           {
            if(f1>=0||f2>=0||f3>=0)
              {
               myAlert(qtr);
               Print("アラートをスティールしました→,",qtr);
              }
           }
         else
           {
            myAlert(qtr);
            Print("アラートをスティールしました→,",qtr);
           }
        }

      ////メモリ解放
      //FreeSharedMem(hProc, lpShared1, sizeof(LVITEM));
      //FreeSharedMem(hProc, lpShared2, 255);
     }
   drows = rows;
//Print("END");
  }



//+------------------------------------------------------------------+
//| 共有メモリ確保                                                        |
//+------------------------------------------------------------------+
int GetSharedMem(int mypid, int memSize, int  ahProc)
  {
   return VirtualAllocEx(ahProc, 0, memSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
  }

//+------------------------------------------------------------------+
//|  共有メモリ解放                                                       |
//+------------------------------------------------------------------+
int FreeSharedMem(int ahProc,int MemAddress,int memSize)
  {
   VirtualFreeEx(ahProc, MemAddress, memSize, MEM_RELEASE);
   return CloseHandle(ahProc);
  }
//+------------------------------------------------------------------+


//+------------------------------------------------------------------+
//| チャート送ったり通知したりするところのまとめ                                      |
//+------------------------------------------------------------------+
void myAlert(string msg)
  {
   datetime now =TimeLocal();
   MqlDateTime now_struct;
   TimeToStruct(now, now_struct);
   string myNow = StringFormat("%4d-%02d-%02d_%02d-%02d-%2d",
                               now_struct.year,
                               now_struct.mon,
                               now_struct.day,
                               now_struct.hour,
                               now_struct.min,
                               now_struct.sec);


   if(mailAlertEnable)
     {
      SendMail(myNow+" "+ msg, myNow+" "+ msg);
     }
   if(mtAlertEnable)
     {
      SendNotification(myNow+" "+ msg);
     }

   if(myAlertEnable)
     {
      int hwndButton = FindWindowExW(hwnd,0,"Button","OK");
      PostMessageW(hwndButton,BM_CLICK,0,0);
     }

  }
//+------------------------------------------------------------------+

苦労したところも残しています!!!

REFERENCEs

さいごに

投資は自己責任!

カレンダー参加者募集してます!

Have a good MQL Life!!!

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?