MT4のメニューバーに新規メニューを追加する方法【MQL4 + C++ DLL】
はじめに
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版)
実装の流れ
- C++でDLLを作成(Windows APIでメニュー操作)
- MQL4からDLLを呼び出す
- MT4のメインウィンドウにメニューを追加
ステップ1: Visual StudioでDLLプロジェクトを作成
1-1. 新規プロジェクト作成
- Visual Studio 20xxを起動
- 新しいプロジェクトの作成
- 「ダイナミック リンク ライブラリ (DLL)」を選択
- プロジェクト名:
mt4dlltest
1-2. プラットフォームをx86に設定
重要: MT4は32bitアプリケーションなので、DLLも32bitでビルドする必要があります。
- ツールバーの x64 をクリック
- x86 を選択(なければ構成マネージャーで追加)
1-3. プリコンパイル済みヘッダーを無効化
- プロジェクトを右クリック → プロパティ
- 構成: すべての構成
- プラットフォーム: すべてのプラットフォーム
- C/C++ → プリコンパイル済みヘッダー
- 使用しない に変更
ステップ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)
2 Win32\Debug\mt4dlltest.dll が生成される
ステップ3: MQL4でDLLを呼び出す
3-1. DLLを配置
MetaTrader4/
└── MQL4/
└── Libraries/
└── mt4dlltest.dll ← ここにコピー
3-2. インジケーターを作成
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で実行
- MetaEditorで
test_add_menu.mq4をコンパイル(F7) - MT4を再起動
- チャートにインジケーターをドラッグ&ドロップ
- 「DLLの使用を許可する」にチェック
- OK
動作確認
MT4のメニューバーに「カスタムツール」が追加されます!
File View Insert Charts Tools Window Help カスタムツール
クリックすると、サブメニュー「ファイル」→「エクスポート」が表示されます。
ハマりポイントと解決策
❌ エラー: '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の機能を大幅に拡張できます!




