Mac上でWindowsアプリを開発する技術

この記事は[ex-mixi Advent Calendar 2018](https://qiita.com/advent-calendar/2018/ex-mixi)の14日目の記事です。

前回は @imaizume さんの[AppStore Connectのメール記録から審査にかかる時間を可視化](https://qiita.com/imaizume/items/95992523067dd1bf830f)でした。
キチンとデータの見える化と、そこからの予測ができていて素晴らしいですね！

# ex-mixiな自己紹介

2014年に新卒でミクシィに入社して、2018年の夏までいました。


# まえおき

いわゆるweb系の業界でエンジニアをしているとMacやLinuxなど、xNIXのOSを普段使いにすることが多いかと思います。

私も普段使いにMac、時々Ubuntuを入れたVPSを使う程度で、私物では開発に使えるWindows機がありません。
厳密には家族が持っているので使える機械が全く無いわけではないのですが、普段使いはできません。

そして個人でお仕事を受けていると、時々Windows向けのアプリケーションの依頼が来ることがあります。

同じコミュニティに属する方からの依頼で、そのコミュニティのドメイン知識が要求されるプログラムの要望です。
そしてぶっちゃけ私もそんなの欲しかった。

そんな感じで、MacしかないのにWindowsのアプリを開発することになった場合にお役立てください。

それぞれの要素技術の説明はここでは目的としません。
キーワードを散りばめておくので、気になったものを調べてみてください。


# 前提

以下の環境で開発するものとします。

* macOS 10.14

また、開発するWindowsアプリとは、Windows デスクトップアプリケーションで、何かしらのユーティリティ系を想定しています。
ゲーム系ではありません。



# 実行環境・コンパイラ

## Wine

[Wine](https://www.winehq.org/)はPOSIX環境下でWindowsのAPIを提供してくれる実行環境です。
Windows向けゲームだとかをmacでやりたい人はきっと使ったことがあるはず。

作ったプログラムの動作確認だとかはwineで行うのが基本となります。

厳密にはWindows環境ではないので「Wineで動いたから安心」というわけではないのですが、 `Wineで動くがWindowsで動かない` 可能性より `Windowsで動くがWineで動かない` 可能性のほうが圧倒的に高いので、とりあえずWineで動作確認できれば良いと思います。

macであればHomebrewで一発インストールできます。

```
$ brew install wine
```

もしMac環境の方にも配布するとなれば[WineBottoler](http://winebottler.kronenberg.org/)を使えばWine環境と一緒にexeを配布できます。

## VirtualBox + IE/Edgeの動作確認用仮想マシン

Microsoftは、非Windows機でもIE/EdgeでのWebサイト動作確認用に、無料のWindows仮想マシンを配布しています。

https://developer.microsoft.com/en-us/microsoft-edge/tools/vms/

これを使うことでWindows上での確実な動作確認ができます。
もっと気合を入れれば、この仮想マシン上に開発環境をつくることも可能でしょう。

ただ、仮想マシンの有効期限は90日に設定されていますし、そもそも目的外の使用なので推奨はできません。
変な使い方して怒られても私は知りませんよ！

## MinGW

[MinGW](http://www.mingw.org/)はGNUなWindows開発用ツールチェインです。
コンパイラやライブラリ、Windowsアプリケーションに特有なリソースファイルを扱うツールなどが含まれます。
なんとWindowsだけでなく数々のプラットフォーム向けにバイナリが提供されています。

macなら[Homebrew](https://brew.sh/)で一発で導入が可能。

```
$ brew install mingw-w64
```

あとはC/C++でWin32APIを叩きまくればいいだけです。シンプル！

ただ、フレームワークなどに頼らずMinGWだけで開発しようとすると、本当にWin32API（とSTL）くらいしか使えないので、お世辞にも簡単とは言えません。
MFC(Microsoft Foundation Class)みたいな抽象化されたものがないので、ウィンドウ生成したければ自分でウィンドウクラスを定義してメッセージ受け取って…的なことを全部自前でする必要があります。
やりましたが相当大変でした。もうやりたくない。

あとSTLの `std::thread` はpthreadに依存しているらしく、MinGWの環境ではそのままでは `std::thread` は使えません。
[mingw-std-threads](https://github.com/meganz/mingw-std-threads)などを導入する必要があります。

ちなみにCMakeでMinGWのインクルードパスを指定しておくと、[CLion](https://www.jetbrains.com/clion/)でプロジェクトを開いた際にきちんとコード補完を効かせてくれます。
CLionめっちゃ賢い。





# ライブラリ

クロスプラットフォーム開発を行うためのライブラリや手法は昔から求められていたためか、かなりの種類があります。
用途によって何が良いのかが変わるので、要件に合わせて使い分けると良いでしょう。

主に気になるのは以下のポイントです。

* 環境構築、開発、動作確認は楽か
    * 大変だと開発コストが高くなってしまうため
    * 特にデスクトップアプリケーションはGUIを求められるので、GUIの開発が簡単だといい
* Win32APIは使えるか
    * たまに名前付きパイプが必要だったりするんです
* コンピュータリテラシーがそんなに高くない日本人への配布は簡単か
    * 英語が読めない人多いので
    * アプリと別に何かインストールする必要があるともう無理だったり

以下、私が実際に使ったもの、使えると思って検討しているものを並べます。
他にも良いものがあればコメントでお教えください。

## Java (ランタイム)

Write once, run anywhere.の代表格Javaですね。
各主要OS用にランタイムが提供されており、アプリケーションはそのランタイム上で動作します。
そのため、基本的にはワンソースでアプリケーションを用意することができます。

### 開発は楽か

簡単です。

ネットワークやファイルシステム、DB操作といったインフラチックな部分から、GUIまでクロスプラットフォームです。
H2などを使えばRDBまでアプリ内に持つことができます。

また言語もJavaだけでなく、Kotlin, Scala, ClojureなどJVMで動作するものであれば何でも使えます。

ライブラリも豊富で、JCenterやMaven Centralに行けば、長いJavaの歴史の中で生き残ってきた高品質なものを探すことができるでしょう。

### Win32APIは使えるか

Win32APIを使いたい場合も、JNAやJNIを使えば何とかなります。
メジャーなラッパーはまだ見つけられていないですが、欲しい機能だけラップしたりすればきっとなんとかなる。はず。

### 配布は簡単か

至れり尽くせりに見えますが、ランタイムが大きいのが難点です。

依頼品がちょっとしたものである場合にランタイムのインストールを要求したりするのはハードルが高いですし、Win32APIを使ったときに手元で動作検証をする際にWine環境にJavaを入れるというなかなか倒錯したことをしなければなりません。

また、作ったJarとJavaランタイムをパッケージ化する [`javapackager`](https://docs.oracle.com/javase/jp/8/docs/technotes/tools/unix/javapackager.html)でWindows向けのインストーラーを生成すると、UIの言語が英語になってしまいます。
内部では[Inno Setup](http://www.jrsoftware.org/isinfo.php)が使われているので、設定さえ変えれば日本語のインストーラーも生成できるはず。
が、　`javapackager`のオプションには設定ファイルを変更するオプションもなければ、言語を変更するオプションもありません。
設定に使うファイルのテンプレートがJDKの`javapackager.jar`の中に入っているので、結局そちらを書き換えて解決した記憶があります。


## Go言語（のコンパイラ）

近年でクロスプラットフォーム開発と言われれば候補の上位に入るのはGo言語でしょうか。

### 開発は楽か

デフォルトでクロスコンパイルが可能で、パッケージ管理も簡単、ライブラリも豊富。
goroutineで非同期処理も簡単にでき、至れり尽くせりです。

クロスコンパイルの仕方はこちらのQiita記事に詳しくまとまっているので、こちらを参照してください。
ただ、Go 1.5以上であれば、紹介されているような `make.bash` を実行する必要はありません。デフォルトですべてのOS/アーキテクチャの組み合わせがコンパイルできるようになっています。

* [Go のクロスコンパイル環境構築](https://qiita.com/Jxck_/items/02185f51162e92759ebe)

欠点はGUIの扱いが貧弱だということ。
各プラットフォーム用のGUIコンポーネントを抽象化して、OSなどを気にせずに使用できるようなライブラリは今の所存在しない様子？
Windows対応だけを考えればよいのであれば、[WALK](https://github.com/lxn/walk)を使うという手もあります。

[Qt](https://www.qt.io/)や[wxWidget](http://www.wxwidgets.org/)など、歴史のあるクロスプラットフォームGUIライブラリをラップしたものはあります。
なので、Qtなどについて詳しければこれらのラッパーを使うのが手っ取り早いでしょう。

### Win32APIは使えるか

`syscall` パッケージを使ったFFIも可能ですし、最悪CGOを使えばなんとかなります。

Win32APIラッパーの開発も一部では盛んで、UI系をラップした[WALK](https://github.com/lxn/walk)、名前付きパイプをSocketやHTTPなどと同じAPIで扱えるようにした[go-winio](https://github.com/Microsoft/go-winio)[^go-winio]などがあります。

[^go-winio]: ただ、Go 1.11とWine 3.0.2の組み合わせでは、名前付きパイプのサーバのAccept時に `Call not implemented.` などエラーが出て使用できませんでした。 https://gist.github.com/kikuchy/5d54e0374e925ee87f62b11704984268

### 配布は簡単か

ビルドすればexeが出来上がるので、あとは任意のインストーラーで包んであげるなりzipに固めるなりすればすぐ配れます。


## Electron

Web系のエンジニアならSlackやChatworkで使っている人が多そうです。
[Electron](https://electronjs.org/)は、言わずとしれた、WebKitでHTMLを表示するタイプのアプリケーションを構築するフレームワーク。

ただ、私はまだ試せていないので、 ⚠以下の情報は正確ではないかもしれません⚠。

### 開発は楽か

JavaScriptが得意だったりHTMLでUIを構成するのが得意だったりする場合、またはすでにWebアプリケーションが存在していてこれをデスクトップアプリケーションとして見せたい場合などは非常に有効な選択肢になるでしょう。
ただ、私のようにHTMLでUIを組むのが苦手な人間にはちょっとつらいかもしれません。

パッケージ管理もnpmがあるので簡単、種類も豊富です。

### Win32APIは使えるか

Win32APIを使用したい場合も、多分[win32-api](https://www.npmjs.com/package/win32-api)みたいな、node用ラッパーを使えばどうにかなるんじゃないでしょうか…
試したことがないのでわかりませんが。

### 配布は簡単か

Electronで作るとアプリケーションサイズが大きくなりがちなのは欠点かも？
とはいえ、そこが気にならなければ特に問題はなさそうです。


## Qt

[Qt](https://www.qt.io/)は老舗のC++用マルチプラットフォームライブラリです。
通信やDB操作などの抽象化だけでなく、イベントドリブンなしくみの提供、各プラットフォームに合わせたルック＆フィールのGUIの提供もしてくれます。

### 開発は楽か

難しいです。

C++なので言語の学習コストがそもそも高いですし、パッケージマネージャーもデファクトが存在せずライブラリが選びにくいです。


ただ一方で、インストーラーからインストールすれば専用のエディタも付いていて、すぐにコードを書き始められます。

### Win32APIは使えるか

macでのクロスコンパイルを試していないのでわかりませんが、MinGWを使えばおそらく可能なのではないでしょうか。
確かQtのビルドツールでバックエンドのコンパイラを指定できるので…[^qtmingw]

[^qtmingw]: http://doc.qt.io/qtcreator/creator-tool-chains.html


### 配布は楽か

ビルドすればexeが出来上がるので、あとは任意のインストーラーで包んであげるなりzipに固めるなりすればすぐ配れます。

アプリケーションサイズも比較的小さくなるので配布も楽。


## .Net（系実行環境）

Windows向けアプリ開発でお馴染みの.Netですが、ランタイムは[mono](https://www.mono-project.com/)などがあるので、mac上でも開発して動作させることが可能です。

Javaと動作原理がほぼ同じであるため、macでコンパイルしてできたexeはWindowsでも問題なく動作します。そのはず。

この方法もまだ試せていないので、 ⚠以下の情報は正確ではないかもしれません⚠。

### 開発は楽か

おそらく楽だと思います。

C#を使うなら、ファイルやネットワークを扱う標準ライブラリが強力ですし、async/awaitによる非同期処理も簡単に扱えます。

[Visual Studio for Mac](https://visualstudio.microsoft.com/ja/vs/mac/)にはNuGetもついてくるようなので[^nuget]、パッケージマネージャーで困ることも無いでしょう。
どれくらいライブラリが充実しているかはわかりませんが…

[^nuget]: https://docs.microsoft.com/ja-jp/visualstudio/mac/nuget-walkthrough

GUIも[Gtk#](https://www.mono-project.com/docs/gui/gtksharp/)があるので、クロスプラットフォームなことがたぶんできるはず。


### Win32APIは使えるか

C#ならば `DllImport` アノテーションを使ったFFIがあるので比較的簡単に扱えると思います。
Win32APIをC#から使用する例も、探せばたくさん見つかりますね。

微妙な欠点としては、Win32APIを呼び出すコードの動作検証には、Wine環境にmonoランタイムをインストールしたものを用意する必要があるとかでしょうか。
ただ最近のwineでは、.Netランタイムが必要になったときに自動でmonoをwine環境内にインストールできるらしいで、そんなに悩むこともないかと思います[^wine-mono]。

[^wine-mono]: https://wiki.winehq.org/Mono#Installing


### 配布は楽か

ランタイムに関しても、大抵のWindowsにはランタイムが入っていると予想されるので、追加のインストールを要求することもないでしょう。
至れり尽くせり！

ただ、クロスプラットフォームなアプリを要求されている場合は、Windows以外の環境の人にランタイムのインストールを要求することになります。


## その他

C/C++開発する場合、ネットワークのサポートやGUIのサポートなど、単機能でなら手厚くクロスプラットフォーム開発をサポートしてくれるライブラリがいくつかあったりします。
[awesome-cpp](https://github.com/fffaraz/awesome-cpp)を見ると、欲しいものが見つかるかもしれません。

また、それらの他言語バインディングも存在していれば使うことができるでしょう。


<hr>

**Mac上でWindowsアプリの開発をする必要がある**だなんてレアケースだと思いますのですぐに役立つ情報ではないかもしれません。
もしそんなレアケースに遭遇したら、ここに挙げた技術などを思い出してあげてください。