見出し画像

RustでMT4のDLLを自作する方法

MQL4上級者向け記事です。

収支アプリ作っててwinnetAPIで限界を感じたのでDLLを自作しています😉

備忘録でまとめておきますね。

事前知識:
MQL4がだいたいわかっている人


Rustってなに?

Rustは、CやC++に近しい存在ですが、特定の目的のために設計されています。それは、「高パフォーマンス」と「メモリ安全性」を両立することです。Mozilla Researchによって開発され、2010年に公開されました。

Rustの名前は、「さび」を意味し、C/C++のような古い言語の欠点を克服することを目指して命名されたとされています。

Rustの特徴

Rustは、いくつかの点でCやC++と類似していますが、独自の特徴を持っています。

1. パフォーマンス

Rustはコンパイル言語であり、C/C++と同様に、ネイティブコードを生成します。そのため、高いパフォーマンスを実現できます。リアルタイムアプリケーションやシステムプログラミングに適しています。

2. メモリ安全性

Rustは、メモリ安全性を重視しています。従来のC/C++では、ダングリングポインタやバッファオーバーフローなどのメモリ関連のバグが発生しやすいですが、Rustはそのようなバグを防ぐための機能を備えています。

  • 所有権と借用: Rustでは、メモリの所有権と借用の概念があり、これによりメモリの安全な管理が可能です。所有権は、特定の変数やデータが誰に属しているかを示し、借用は、一時的にデータを他のスコープに貸し出す仕組みです。

  • ライフタイム: Rustは、ライフタイムという概念を用いて、データの寿命を管理します。これにより、メモリリークや未初期化変数の使用を防止します。

3. 並行処理

Rustは、並行処理や並列処理のサポートに優れています。スレッドや非同期プログラミングを安全に扱えるため、高パフォーマンスなアプリケーションを構築できます。

4. クロスプラットフォーム

Rustは、さまざまなプラットフォームで動作します。Windows、Linux、macOSなど、主要なオペレーティングシステムに対応しており、クロスプラットフォームでの開発が可能です。

5. コミュニティとエコシステム

Rustには活発なコミュニティがあり、数多くのライブラリやツールが提供されています。Cargoというパッケージマネージャがあり、プロジェクトのビルドや依存関係の管理が容易です。

Rustを使う理由

Rustを使う理由は、主に次の点にあります。

  • 高パフォーマンスと安全性: C/C++に匹敵するパフォーマンスと、メモリ安全性を両立できます。

  • 並行処理のサポート: 安全な並行処理が可能で、複雑なアプリケーションを構築できます。

  • クロスプラットフォーム: さまざまなプラットフォームで動作するDLLを作成できます。


RustでDLLを作る目的・狙い

  • MQL4の限界を超えたい

  • バックエンドアプリとの連携を強めたい

  • 出来ると色々と便利

DLLを作るときの課題

  • DLLがそもそもよくわからない

  • MT4の制限がある

すべては出来ないので出来る範囲でやっていこう

ポインターとかは使えない

  • データ型がMQL4で定義されているもののみが有効

ではさっさくやっていこう

機能定義

  • DLLで呼び出してHello Worldをテキストファイルで保存する

Rustで32ビットのDLLを作成する方法

ステップ 1: Rustのインストール

まず、Rust開発環境をインストールします。Rustupというツールを使うと、簡単にインストールできます。


  1. Rustの公式サイトにアクセス: Rustの公式サイトにアクセスし、Windowsの場合はインストーラーをダウンロードします。

  2. インストーラーの実行: ダウンロードしたインストーラーを実行し、Rustをインストールします。デフォルトの設定で問題ありません。

  3. インストールの確認: インストールが完了したら、ターミナルまたはコマンドプロンプトを開いて、Rustが正しくインストールされたか確認します。

rustc --version  # Rustコンパイラのバージョンを確認
cargo --version  # Cargoのバージョンを確認

ステップ 2: MinGW-w64のインストール

Rustで32ビットのDLLを作成するために、MinGW-w64の32ビットツールチェーンをインストールします。


  1. MSYS2のダウンロード: MSYS2の公式サイトからインストーラーをダウンロードします。

  2. MSYS2のインストール: ダウンロードしたインストーラーを実行し、MSYS2をインストールします。

  3. MSYS2のセットアップ: MSYS2を開いて、パッケージリストを更新します。

  4. MinGW-w64の mingw32 ツールチェーンをインストール:


pacman -Sy  # パッケージリストの更新
pacman -S mingw-w64-i686-gcc  # 32ビットのMinGW-w64ツールチェーンをインストール

ステップ 3: 環境変数の設定

MinGW-w64のツールチェーンをRustから利用できるようにするため、環境変数 PATHmingw32bin フォルダを追加します。

  1. 環境変数の編集:

    • Windowsで「環境変数」を検索し、「システムのプロパティ」を開きます。

    • 「詳細設定」タブから「環境変数」を選択します。

    • 「システム環境変数」の PATH を選択して「編集」をクリックします。

  2. PATH に mingw32\bin を追加:

  3. 変更を保存: 環境変数の変更を保存し、必要に応じてシステムを再起動します。

環境変数に追加


パスに追加します

編集です


追加しました

C:\msys64\mingw32\bin

ステップ 4: Rustで32ビットのDLLをビルド

1. プロジェクトの作成

Rustプロジェクトを作成し、DLLをビルドするための環境を設定します。

1 プロジェクトの作成: cargo new コマンドを使用して新しいRustプロジェクトを作成します。

cargo new my_rust_dll --lib  # プロジェクトをライブラリ形式で作成
cd my_rust_dll  # プロジェクトフォルダに移動

2 Cargoファイルの設定


プロジェクトフォルダに生成された Cargo.toml ファイルで、DLLを作成するための設定を行います。

`Cargo.tml`

[package]
name = "my_rust_dll"  # プロジェクト名
version = "0.1.0"  # バージョン
edition = "2021"  # Rustエディション

[lib]
crate-type = ["cdylib"]  # ライブラリ形式としてDLLを作成

3. 32ビットターゲットの追加

`Bash`

rustup target add i686-pc-windows-gnu  # 32ビットターゲットを追加

4 lib.rsを編集する


use std::fs::File;
use std::io::Write;
use std::ffi::CStr;

// Rustでファイルにデータを書き込む関数
#[no_mangle]
pub extern "C" fn write_hello_to_file(file_path: *const i8) -> i32 {
    let path = unsafe { CStr::from_ptr(file_path) }.to_str().unwrap_or("");  // ファイルパスを取得

    // ファイルを開いて書き込む
    match File::create(path) {
        Ok(mut file) => {
            if file.write_all(b"Hello, world!").is_err() {
                return -2;  // 書き込みエラー
            }
            0  // 成功
        },
        Err(_) => -1,  // ファイル作成エラー
    }
}

5 プロジェクトファイルのビルド

cargo build --release --target i686-pc-windows-gnu  # 32ビットDLLのビルド
my_rust_dll/                 # プロジェクトのルートフォルダ
├── src/                    # ソースコードフォルダ
│   └── lib.rs              # ライブラリとしてのエントリポイント
├── Cargo.toml              # Cargo設定ファイル
├── Cargo.lock              # Cargoのロックファイル
└── target/                 # ビルドされたアーティファクトを格納するフォルダ


DLLはターゲットに入ってます

target/                     # ビルドされたアーティファクトが格納されるフォルダ
├── i686-pc-windows-gnu/    # 32ビットのターゲットフォルダ
│   ├── release/            # リリースビルドのアーティファクト
│   │   ├── my_rust_dll.dll  # ビルドされたDLL
│   │   ├── my_rust_dll.dll.exp  # エクスポート情報
│   │   ├── my_rust_dll.dll.lib  # ライブラリファイル
│   │   ├── my_rust_dll.pdb  # デバッグ情報
│   │   ├── my_rust_dll.d  # コンパイル関連の中間ファイル
│   │   ├── .cargo-lock   # Cargoのロックファイル
│   │   └── ...            # その他のビルド関連ファイル
│   ├── build/             # ビルドプロセスに関連するファイル
│   ├── deps/              # ビルドされた依存関係
│   └── incremental/       # インクリメンタルビルド関連ファイル
└── ...                     # 他のターゲットやビルド関連フォルダ

あとはDLLをMT4の`Libraries`にコピペします。

`C:\Users<YourUsername>\AppData\Roaming\MetaQuotes\Terminal<YourTerminalID>\MQL4\Libraries`

6 MQL4で呼び出す

//+------------------------------------------------------------------+
//|                                                         rust.mq4 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict

#import "my_rust_dll.dll"  // DLLの名前
int write_hello_to_file(const char &file_path[]);  // DLL関数のインポート
#import

int OnInit() {
    // Filesディレクトリのパスを取得
    string files_path = TerminalInfoString(TERMINAL_DATA_PATH) + "\\MQL4\\Files";  // Filesディレクトリ
    string full_path = files_path + "\\hello.txt";  // ファイル名を指定
    char path_array[256];
    StringToCharArray(full_path, path_array, 0);  // ファイルパスをchar配列に変換

    // DLL関数を呼び出してファイルに書き込む
    int result = write_hello_to_file(path_array);

    if (result == 0) {
        Print("File written successfully to ", full_path);
    } else {
        Print("Failed to write to file with code: ", result);
    }

    return(INIT_SUCCEEDED);
}


//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   
  }
//+------------------------------------------------------------------+

コンパイルでおわりです。

テストしてみると

動作確認しましょう。

普通にEAをチャートにセットすればOKです。

DLLを許可して起動する

Filesにhello.txtが保存されていると思います。


出来た!


よろしければサポートお願いします! いただいたサポートはクリエイターとしての活動費に使わせていただきます!