0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

mt4のメイン画面に新規メニューを追加する

0
Last updated at Posted at 2025-12-25

MT4のメニューバーに新規メニューを追加する方法【MQL4 + C++ DLL】

image.png

はじめに

MetaTrader 4(MT4)のメニューバーに独自のメニュー項目を追加する方法を解説します。
Windows APIを使ったC++のDLLを作成し、MQL4から呼び出すことで実現します。

完成イメージ

MT4のメインウィンドウのメニューバーに「カスタムツール」というメニューが追加され、サブメニューも展開できるようになります。

File  View  Insert  Charts  Tools  Window  Help  カスタムツール
                                                    └─ ファイル
                                                        └─ エクスポート

開発環境

  • Windows 10/11
  • Visual Studio 2022
  • MetaTrader 4(32bit版)

実装の流れ

  1. C++でDLLを作成(Windows APIでメニュー操作)
  2. MQL4からDLLを呼び出す
  3. MT4のメインウィンドウにメニューを追加

ステップ1: Visual StudioでDLLプロジェクトを作成

1-1. 新規プロジェクト作成

  1. Visual Studio 20xxを起動
  2. 新しいプロジェクトの作成
  3. ダイナミック リンク ライブラリ (DLL)」を選択
  4. プロジェクト名: mt4dlltest

image.png

1-2. プラットフォームをx86に設定

重要: MT4は32bitアプリケーションなので、DLLも32bitでビルドする必要があります。

  1. ツールバーの x64 をクリック
  2. x86 を選択(なければ構成マネージャーで追加)

image.png

1-3. プリコンパイル済みヘッダーを無効化

  1. プロジェクトを右クリック → プロパティ
  2. 構成: すべての構成
  3. プラットフォーム: すべてのプラットフォーム
  4. C/C++プリコンパイル済みヘッダー
  5. 使用しない に変更

image.png

ステップ2: DLLのソースコード作成

MenuHelper.cpp を作成し、以下のコードを記述します。

// MenuHelper.cpp
#include <windows.h>

// グローバル変数でメニュー情報を保持
#define MAX_MENUS 100
typedef struct {
    HWND hwnd;
    HMENU hMenu;
    int menuId;
    BOOL isValid;
} MenuInfo;

static MenuInfo g_menus[MAX_MENUS];
static int g_menuCount = 0;
static int g_nextMenuId = 10000;

// メニューを追加する関数
extern "C" __declspec(dllexport) int guiAddMenu(
    int hwnd,
    const wchar_t* caption,
    int parentHandle,
    int flags
)
{
    HWND hWnd = (HWND)(LONG_PTR)hwnd;

    if (!IsWindow(hWnd)) {
        return -1;
    }

    HMENU hMenuBar = GetMenu(hWnd);
    if (!hMenuBar) {
        return -2;
    }

    int newMenuId = g_nextMenuId++;
    HMENU hParentMenu = hMenuBar;

    if (parentHandle != 0) {
        for (int i = 0; i < g_menuCount; i++) {
            if (g_menus[i].menuId == parentHandle && g_menus[i].isValid) {
                hParentMenu = g_menus[i].hMenu;
                break;
            }
        }
    }

    if (wcscmp(caption, L"-") == 0) {
        AppendMenuW(hParentMenu, MF_SEPARATOR, 0, NULL);
        DrawMenuBar(hWnd);
        return 0;
    }

    HMENU hNewMenu = CreatePopupMenu();
    if (!hNewMenu) {
        return -3;
    }

    if (parentHandle == 0) {
        AppendMenuW(hMenuBar, MF_POPUP, (UINT_PTR)hNewMenu, caption);
    }
    else {
        AppendMenuW(hParentMenu, MF_POPUP, (UINT_PTR)hNewMenu, caption);
    }

    if (g_menuCount < MAX_MENUS) {
        g_menus[g_menuCount].hwnd = hWnd;
        g_menus[g_menuCount].hMenu = hNewMenu;
        g_menus[g_menuCount].menuId = newMenuId;
        g_menus[g_menuCount].isValid = TRUE;
        g_menuCount++;
    }

    DrawMenuBar(hWnd);
    return newMenuId;
}

// メニュー項目(実行可能な項目)を追加
extern "C" __declspec(dllexport) int guiAddMenuItem(
    int hwnd,
    const wchar_t* caption,
    int parentHandle,
    int flags
)
{
    HWND hWnd = (HWND)(LONG_PTR)hwnd;

    if (!IsWindow(hWnd)) {
        return -1;
    }

    HMENU hParentMenu = NULL;
    for (int i = 0; i < g_menuCount; i++) {
        if (g_menus[i].menuId == parentHandle && g_menus[i].isValid) {
            hParentMenu = g_menus[i].hMenu;
            break;
        }
    }

    if (!hParentMenu) {
        return -2;
    }

    int newMenuId = g_nextMenuId++;
    UINT menuFlags = MF_STRING;

    if (flags == 1) {
        menuFlags |= MF_UNCHECKED;
    }

    if (wcscmp(caption, L"-") == 0) {
        AppendMenuW(hParentMenu, MF_SEPARATOR, 0, NULL);
    }
    else {
        AppendMenuW(hParentMenu, menuFlags, newMenuId, caption);
    }

    DrawMenuBar(hWnd);
    return newMenuId;
}

// チェックボックスの状態を取得
extern "C" __declspec(dllexport) BOOL guiGetMenuCheckState(int hwnd, int menuId)
{
    for (int i = 0; i < g_menuCount; i++) {
        if (g_menus[i].isValid) {
            UINT state = GetMenuState(g_menus[i].hMenu, menuId, MF_BYCOMMAND);
            if (state != (UINT)-1) {
                return (state & MF_CHECKED) ? TRUE : FALSE;
            }
        }
    }
    return FALSE;
}

// チェックボックスの状態を設定
extern "C" __declspec(dllexport) BOOL guiSetMenuCheckState(int hwnd, int menuId, BOOL checked)
{
    HWND hWnd = (HWND)(LONG_PTR)hwnd;

    for (int i = 0; i < g_menuCount; i++) {
        if (g_menus[i].isValid) {
            UINT flags = checked ? MF_CHECKED : MF_UNCHECKED;
            UINT result = CheckMenuItem(g_menus[i].hMenu, menuId, MF_BYCOMMAND | flags);
            if (result != (UINT)-1) {
                DrawMenuBar(hWnd);
                return TRUE;
            }
        }
    }
    return FALSE;
}

// DLLエントリポイント
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        for (int i = 0; i < MAX_MENUS; i++) {
            g_menus[i].isValid = FALSE;
        }
        break;
    case DLL_PROCESS_DETACH:
        for (int i = 0; i < g_menuCount; i++) {
            if (g_menus[i].isValid) {
                DestroyMenu(g_menus[i].hMenu);
            }
        }
        break;
    }
    return TRUE;
}

// MT4のメインウィンドウを取得
extern "C" __declspec(dllexport) int guiGetMainWindow(int chartHwnd)
{
    HWND hWnd = (HWND)(LONG_PTR)chartHwnd;

    if (!IsWindow(hWnd)) {
        return 0;
    }

    // 親ウィンドウを辿ってメインウィンドウを探す
    HWND hParent = hWnd;
    HWND hTemp = hWnd;

    while (hTemp != NULL) {
        hParent = hTemp;
        hTemp = GetParent(hTemp);
    }

    // メインウィンドウにメニューバーがあるか確認
    HMENU hMenu = GetMenu(hParent);
    if (hMenu) {
        return (int)(LONG_PTR)hParent;
    }

    return 0;
}

// メインウィンドウにメニューを追加(改良版)
extern "C" __declspec(dllexport) int guiAddMenuToMain(
    int chartHwnd,
    const wchar_t* caption,
    int parentHandle,
    int flags
)
{
    // メインウィンドウを取得
    int mainHwnd = guiGetMainWindow(chartHwnd);
    if (mainHwnd == 0) {
        return -1;
    }

    // メインウィンドウにメニューを追加
    return guiAddMenu(mainHwnd, caption, parentHandle, flags);
}

// メインウィンドウにメニュー項目を追加
extern "C" __declspec(dllexport) int guiAddMenuItemToMain(
    int chartHwnd,
    const wchar_t* caption,
    int parentHandle,
    int flags
)
{
    int mainHwnd = guiGetMainWindow(chartHwnd);
    if (mainHwnd == 0) {
        return -1;
    }

    return guiAddMenuItem(mainHwnd, caption, parentHandle, flags);
}

ビルド

1 ビルドソリューションのリビルド(Ctrl + Shift + B)

image.png

2 Win32\Debug\mt4dlltest.dll が生成される


ステップ3: MQL4でDLLを呼び出す

3-1. DLLを配置

MetaTrader4/
└── MQL4/
    └── Libraries/
        └── mt4dlltest.dll  ← ここにコピー

3-2. インジケーターを作成

test_add_menu.mq4 を作成:

test_add_menu.mq4
#property copyright ""
#property link      ""
#property version   "1.00"
#property strict
#property indicator_chart_window


// DLLの関数をインポート
#import "mt4dlltest.dll"
  int guiGetMainWindow(int chartHwnd);  // メインウィンドウ取得
  int guiAddMenuToMain(int chartHwnd, string caption, int parentHandle, int flags);
  int guiAddMenuItemToMain(int chartHwnd, string caption, int parentHandle, int flags);
#import


int menuTools;
int menuFile;
int menuExport;


int OnInit()
{
  // チャートウィンドウのハンドルを取得
  int chartHwnd = WindowHandle(Symbol(), Period());
  Print("チャートウィンドウハンドル: ", chartHwnd);
  
  // MT4メインウィンドウのハンドルを取得
  int mainHwnd = guiGetMainWindow(chartHwnd);
  Print("MT4メインウィンドウハンドル: ", mainHwnd);
  
  if(mainHwnd == 0)
  {
    Alert("メインウィンドウの取得に失敗しました");
    return INIT_FAILED;
  }
  
  // メインウィンドウにメニューを追加
  menuTools = guiAddMenuToMain(chartHwnd, "メリークリスマス!!", 0, 0);
  Print("menuTools ID: ", menuTools);
  
  if(menuTools < 0)
  {
    Alert("メニュー追加に失敗しました");
    return INIT_FAILED;
  }
  
  // サブメニューを追加
  menuFile = guiAddMenuToMain(chartHwnd, "ファイル", menuTools, 0);
  Print("menuFile ID: ", menuFile);
  
  // メニュー項目を追加
  menuExport = guiAddMenuItemToMain(chartHwnd, "エクスポート", menuFile, 0);
  Print("menuExport ID: ", menuExport);

  Alert("menu add suucess");

  return(INIT_SUCCEEDED);
}

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(rates_total);
}

3-3. MT4で実行

  1. MetaEditorで test_add_menu.mq4 をコンパイル(F7)
  2. MT4を再起動
  3. チャートにインジケーターをドラッグ&ドロップ
  4. 「DLLの使用を許可する」にチェック
  5. OK

動作確認

MT4のメニューバーに「カスタムツール」が追加されます!

File  View  Insert  Charts  Tools  Window  Help  カスタムツール

クリックすると、サブメニュー「ファイル」→「エクスポート」が表示されます。

image.png


ハマりポイントと解決策

❌ エラー: 'mt4dlltest.dll' is not 32-bit version

原因: DLLが64bitでビルドされている

解決策: Visual Studioでプラットフォームを x86 に変更してリビルド

❌ メニューが追加されない

原因: チャートウィンドウではなく、メインウィンドウにメニューを追加する必要がある

解決策: guiGetMainWindow() で親ウィンドウを取得してからメニューを追加

❌ ビルドエラー: #include "pch.h" が見つからない

原因: プリコンパイル済みヘッダーが有効になっている

解決策: プロジェクトプロパティで「プリコンパイル済みヘッダーを使用しない」に設定


応用例

セパレータを追加

guiAddMenuItemToMain(chartHwnd, "-", menuFile, 0);  // 区切り線

チェックボックスメニュー

int menuCheck = guiAddMenuItemToMain(chartHwnd, "自動売買", menuTools, 1);  // flags=1

深い階層のメニュー

int menuAnalysis = guiAddMenuToMain(chartHwnd, "分析", menuTools, 0);
int menuTrend = guiAddMenuToMain(chartHwnd, "トレンド分析", menuAnalysis, 0);
int menuMA = guiAddMenuItemToMain(chartHwnd, "移動平均", menuTrend, 0);

まとめ

  • C++のDLLでWindows APIを使ってMT4のメニューを操作
  • MQL4から WindowHandle() でチャートウィンドウを取得
  • GetParent() でメインウィンドウを辿る
  • 32bit版DLLをビルドすることが重要

これで、MT4の機能を大幅に拡張できます!


参考リンク

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?