Wineの上からLinuxシステムコール。
ここでは、Windowsアプリケーションを他のOSで動かすためのオープンソースソフトウェアWineの、上で動作するWindowsアプリケーションから下のLinuxのシステムコールを、特別なDLLを介して呼び出す方法を、サンプルを交えながら解説します。
もっと短く言うと、Wineで動かしたWindowsアプリからLinuxのネイティブ機能を利用しよう!という趣旨の記事です。
1.呼び出すLinuxシステムコール
#include <sys/utsname.h>
int uname(struct utsname *buf);
このunameを呼び出し、Wine上のWindowsアプリからLinuxのカーネル情報を取得し、その内容を標準出力に表示してみたいと思います。
2.Dockerでwine環境を立ち上げる
今回はwineがインストールされたLinuxを使用しますので、Dockerを使って環境を立ち上げます。
C:\>docker run -it --rm --name wine scottyhardy/docker-wine
「Dockerなにそれ?」という人は、公式のドキュメントを参考にしながら、まずはDocker CEをインストールして使える状態にしてください。
環境が立ち上げられたら、wineがきちんと動作するかを確認してください。
wineuser@ca8fc30cc061:~$ wine cmd /c ver
...
Microsoft Windows 6.1.7601 (4.0.1)
3.追加のパッケージをインストールする
DLLをビルドするために必要なパッケージをインストールします。
これらのコマンドはDockerの外側、つまりのターミナルをもう一つ起動して、そこで実行するようにしてください。
C:\>docker exec -it wine apt-get update
C:\>docker exec -it wine apt-get install -y build-essential wine-stable-dev
Dockerの内側からだと、root権限がないためにコマンドが失敗してしまいます。
4.ソースファイルを準備する
普通のC言語のソースですが、windef.hをインクルードし、関数にWINAPIキーワードが付与されている点に注目してください。
/* ファイル名:wine2linux.c */
#include <windef.h>
#include <sys/utsname.h>
#include <stdio.h>
int WINAPI show_uname() {
struct utsname info;
int ret;
ret = uname(&info);
if (ret == 0) {
printf("%s %s %s %s %s\n", info.sysname,
info.nodename,
info.release,
info.version,
info.machine);
}
return ret;
}
sys/utsname.hをインクルードしてunameやutsnameを使用していますが、このソースはあくまでもWine専用のDLLをビルドするためのものです。
そして、Wine専用のDLLをビルドするために必要なspecファイルも用意します。
# ファイル名:wine2linux.spec
@ stdcall show_uname()
このspecファイルの書式に関しては、正直なところ自分もよくわかっていません。もしも「ここを見たらいいよ」という情報がありましたら是非とも教えてください。
最後に、これらのファイルをDockerの外側から内側に送り込みます。
C:\>docker cp wine2linux.c wine:/home/wineuser
C:\>docker cp wine2linux.spec wine:/home/wineuser
5.Wine専用DLLのビルド
コマンドにすると3つ、windef.hがインクルードできるようにパスを通し、gccでオブジェクトファイルを作成、それを元にwinegccでDLL(厳密にはsoファイル)を作成します。
wineuser@ca8fc30cc061:~$ export C_INCLUDE_PATH=/opt/wine-stable/include/wine/windows
wineuser@ca8fc30cc061:~$ gcc -fPIC -c wine2linux.c -o wine2linux.o
wineuser@ca8fc30cc061:~$ winegcc wine2linux.o wine2linux.spec -shared -o wine2linux.dll
ビルドしたDLLは、Wine上でロードしやすいようにsystem32の下にコピーしておきます。
wineuser@ca8fc30cc061:~$ cp wine2linux.dll.so .wine/drive_c/windows/system32/wine2linux.dll
6.Wine専用DLLの呼び出し
簡単にDLLを呼び出すために、まずはWine上で動くWindows版のPythonを準備します。
wineuser@ca8fc30cc061:~$ wget https://www.python.org/ftp/python/3.8.0/python-3.8.0-embed-amd64.zip
...
2019-10-21 12:04:57 (1.24 MB/s) - 'python-3.8.0-embed-amd64.zip' saved [8084795/8084795]
wineuser@ca8fc30cc061:~$ unzip python-3.8.0-embed-amd64.zip -d py38
Archive: python-3.8.0-embed-amd64.zip
inflating: py38/python.exe
...
そしてWineの上で、準備したPythonを起動します。
wineuser@ca8fc30cc061:~$ wine py38/python.exe
...
Python 3.8.0 (tags/v3.8.0:fa919fd, Oct 14 2019, 19:37:50) [MSC v.1916 64 bit (AMD64)] on win32
>>>
あとはctypesを使って作成したDLLをロード、そして呼び出すだけです。
>>> import ctypes
>>> dll = ctypes.windll.LoadLibrary("wine2linux.dll")
>>> dll.show_uname()
Linux 03a1ce15cc0d 4.9.184-linuxkit #1 SMP Tue Jul 2 22:58:16 UTC 2019 x86_64
Wineの上で動く、Windows版のPythonからLinuxのuname情報にアクセスすることができました。
7.あとがき
ここに書いたセオリーを理解し、そしてLinuxのシステムコールを呼び出す術をマスターすれば、Wineの上で動くMT4/MQL4からDLLを介して自在にLinuxの機能を呼び出せるようになります。
これが何を意味するかわかりますか?そう、アナタのMT4が、Wineの抽象レイヤやネットワークスタックをスキップして直接、WineをホストするOSの機能へアクセスできるようになるということです。つまりそのほうが断然速いんです。
ご健勝をお祈りします。
Let's きっちり納税。noteでの収益を励みに、皆さんへ有益な情報を届けます!