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


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

# アラートとは？

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531802/0f48f7b8-b9f7-5b9a-1a73-f0304ce9ccb8.png)


# アラートの正体

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

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

https://learn.microsoft.com/ja-jp/visualstudio/debugger/introducing-spy-increment?view=vs-2022

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

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



# MQL4

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

```C
#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を呼びます。
```C
#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
```

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

```C
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
```

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

```C
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の中身を取りに行けます。
共有メモリへのアクセスには，
```C
GetSharedMem(int mypid, int memSize, int  ahProc)
```

アクセスしたあとは，手を離さないといけないので，
```C
FreeSharedMem(int ahProc,int MemAddress,int memSize)
```
を読んでいます。

それぞれの実装は次になります。
```C
//+------------------------------------------------------------------+
//| 共有メモリ確保                                                        |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+

```

あとは
```C
myAlert(string msg)
```
で，煮るなり焼くなりすればいいわけです。

```C
//+------------------------------------------------------------------+
//| チャート送ったり通知したりするところのまとめ                                      |
//+------------------------------------------------------------------+
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が固まります！

# コード全部

```C

#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

https://learn.microsoft.com/ja-jp/visualstudio/debugger/introducing-spy-increment?view=vs-2022

http://dqmacro.blog.fc2.com/blog-category-57.html

# さいごに

投資は自己責任！

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

https://qiita.com/advent-calendar/2022/mql

Have a good MQL Life!!!