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での収益を励みに、皆さんへ有益な情報を届けます!