MT4をLinuxで動かす[Debian9, MT4DLL]

## 概要
MT4をWindowsVPS風にLinuxで動かしました。
ついでにDLLの動作確認とかもしました。

## とりあえずコード
[https://github.com/jacob327/linuxMT4](https://github.com/jacob327/linuxMT4)

## 概要・動機とか
別段、特別なことはしていません。Windowsで動かせるものがLinuxで動かない訳がないので動かせるようにした備忘録です。
ただ、MT4という2005年リリースのソフトが未だに使われていて(MT5がだいぶ前にリリースされたにもか関わらず！)、まだまだニーズもあるようなので、とりあえず動くものの雛形を作りました。

※サーバはメモリ2GB以上推奨です。0.6GBで実験したらapt installすらできなくなりました。
ちなみにMT4一台あたりのメモリ上限は2GBです。

## コード

### mql4

5秒毎に`TestMessage`をDLLに渡して、DLLから`::from DLL`を追記して返してもらうプログラムです。

```c:TestAlert.mq4
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict
#property indicator_chart_window

#import "MT4DllTester.dll"
    string TestFunction(string);
#import

int OnInit() {
    EventSetTimer(5);
    return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason) {
    EventKillTimer();
}

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

void OnTimer() {
    Alert(TestFunction("TestMessage "));
}
```

### DLL

MT4のstringは`WCHAR*`型で渡してあげないとうまく解釈してくれないので`WCHAR*`にしています。
ただ、今回のプログラムはランダムで文字化けが発生します。これはメモリ云々の話なんでしょうが、今回は割愛します。文字列を正確に渡してあげたい場合は素直に`char`の方が良いかもしれません。
この辺の事情は[フォーラム](https://www.mql5.com/en/forum/156814)が詳しいです

```cpp:MT4DllTester.cpp
#include "pch.h"
#include "MT4DllTester.h"
#include <codecvt>

MT4_EXPFUNC const WCHAR* __stdcall TestFunction(WCHAR* input)
{
        std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> cv;

        std::wstring w_inp(input);
        std::string s_out = cv.to_bytes(w_inp);

        s_out.append("::from DLL");

        std::wstring w_out = cv.from_bytes(s_out);

        const WCHAR* ret = w_out.c_str();
        return ret;
}
```

エクスポートする関数を教えてあげます。

```cpp:Source.def
LIBRARY MT4DllTest
EXPORTS
        TestFunction
```

VSC++で最初からプロジェクトを作る場合は`x86`, `release`モードにするのを忘れないでください。
ref) [https://www.mql5.com/en/forum/96970](https://www.mql5.com/en/forum/96970)

(※C++はよく分かってないのでコードおかしかったら教えてください(´・ω・｀))

### サーバ側
debian 9(CUIのみ)をインストールした段階を前提とします。

MT4を動かすためのwineとxfce4(デスクトップ環境・xfceは軽いらしいので)、
リモートから接続するためのvnc周りをインストールします。
rootで実行してください

```bash:init.sh
#!/usr/bin/env bash

dpkg --add-architecture i386 &&\
apt install -y software-properties-common apt-transport-https &&\
wget -qO - https://dl.winehq.org/wine-builds/winehq.key|apt-key add - &&\
apt-add-repository https://dl.winehq.org/wine-builds/debian/ &&\
apt update &&\
apt install -y --install-recommends winehq-stable winbind &&\
DEBIAN_FRONTEND=noninteractive apt install -y keyboard-configuration &&\
#apt install -y task-xfce-desktop &&\
apt install -y xfce4 xfce4-goodies gnome-icon-theme &&\
apt install tightvncserver &&\
echo '[ALL DONE]
```

インストールが終わったら以下コマンドでvncサーバが立ちます。
色々聞かれるのでパスワードとかを設定しましょう。

```bash:start.sh
vncserver :1 -geometry 800x600 -depth 24
```

止めるときはこっちです。

```bash:stop.sh
vncserver -kill :1
```

### クライアント側
VNCクライアントで接続します。
krdcなら
`{ユーザ名}@{IP_OR_HOST名}:{ポート名}`
です。
例）`test_user@xxx.xxx.xxx:5901` (5901: VNCのデフォルトポート)

### こんな感じ

![Screenshot_20190514_233659.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206426/8a8e4e88-120f-4b69-8f50-fe8dde4d271f.png)

※右半分は`dstat`の出力です。メモリ1.7GBのサーバでこのぐらいでした。

### 確認したこと
 - DLLは読み込める
 - VNC接続を落としても稼働し続ける
 - VNCサーバを落とすと一緒に落ちる(この辺はよく分かってませんが`ps`でも落ちてました)

### 商用利用時に気になること
 - wineの(おなじみの)大量のwarning
 - localhostでDB接続できるか
 - localhostでWebSocket通信できるか