UTM × Rust で Mac 上に Windows 環境を構築しアプリ開発

# 概要

本記事は、Mac上でRustを使ってWindowsアプリを開発するための環境構築を学ぶ目的で作成しました。

通常、Macで`windows`クレートを使用すると**rust-analyzer**が `*** が見つかりません` というエラーを表示します。

これは Windows環境が前提のため発生するものですが、ビルド時に**Windows向けターゲットを指定**すればexeの生成は可能です。

しかし、MacのVSCodeで開発する際にはエラーが邪魔になることがあります。

そこで、<font color="red">UTMでWindowsを構築し、VSCodeのリモート接続を活用すること</font>で`rust-analyzer`のエラーを回避し、スムーズにWindowsアプリを開発する方法を解説します。

また記事中の内容は下記のリポジトリで公開しています

https://github.com/BlackTanuTanuNinjas/tutorial_windows_rs

## 対象読者

- RustでWindowsアプリを開発したい人
- Mac上でWindows環境を整えて開発したい人

## この記事でできること

1. UTMでWindowsをセットアップし、起動できるようになる
2. Windows上でRustを使い、`windows`クレートでアプリを開発できるようになる

# 開発環境

記事中のコードは以下の環境で動作確認しています。  
RustのバージョンはWindows側も同じものを使用しています。

```zsh
% sw_vers
ProductName:		macOS
ProductVersion:		15.3.1
BuildVersion:		24D70

 % rustc --version 
rustc 1.87.0-nightly (30f168ef8 2025-03-05)
```


# 手順

本記事では、以下の2つのステップでMac上にWindows開発環境を構築し、RustでWindowsアプリを開発する方法を解説します。

1. **UTMでWindowsセットアップ** 
2. **RustでWindowsアプリ開発**  

## UTMでWindowsセットアップ

MacにUTMをインストールし、Windowsをセットアップする手順を解説します。

1. [UTMをインストールする](#utmをインストールする)
2. [WindowsのISOを取得する](#windowsのisoを取得する)
3. [ISOを使ってWindows仮想マシンを作成する](#isoを使ってwindows仮想マシンを作成する)
4. [Windowsの初期設定をする](#windowsの初期設定をする)
5. [Rustの開発環境を整える（RustとVSCodeをインストール）](#rustの開発環境を整える)

### UTMをインストールする

UTMをインストールするには、以下の公式サイトにアクセスし、「ダウンロード」をクリックしてください。

https://mac.getutm.app/

また、App Store版もあります。こちらは**有料**ですが、**自動アップデートの管理がしやすい**というメリットがあります。


### WindowsのISOを取得する

UTMをインストールしたら、以下の手順でWindowsのISOイメージを取得しましょう。

1. UTMを起動する
2. 「UTMギャラリーを開く」をクリック
3. Windows 11を選択し、「Guide」をクリック
4. 「Obtain Windows」から「Download on the App Store」をクリック
5. App Storeで**CrystalFetch ISO Downloader**をインストール
6. CrystalFetchを起動し、Windows 11のISOをダウンロード

CrystalFetchでWindowsのISOをダウンロードする際は、以下の項目を選択し、「ダウンロード」をクリックしてください。


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3455723/408827a6-11e1-4df2-b89b-90f62b98fed9.png" width="50%" />


### ISOを使ってWindows仮想マシンを作成する

UTMを起動し、以下の手順でWindows仮想マシンを作成しましょう。

1. 「新規仮想マシンを作成」を選択
2. 「仮想化」「Windows」を選択
3. 以下のオプションを有効にし、起動ISOイメージを指定
    - 「Windows 10以降をインストール」
    - 「ドライバとSPICEツールをインストール」
4. 各設定は以下のように指定
    - **メモリ**: デフォルト
    - **ストレージ**: デフォルト
    - **共有ディレクトリ**: 任意のディレクトリを指定

以下の設定を参考にし、問題なければ「保存」をクリックしてください。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3455723/e39b072d-e32e-445e-bf64-c157eb5f9901.png" width="50%" />


### Windowsの初期設定をする

仮想マシンを起動すると、Windowsのインストーラー画面が表示されます。  
詳細な手順は省略しますが、指示に従ってインストールを進めてください。

インストール後、再起動時に「Start Boot Options」と表示され、Windowsが正常に起動しない場合は、  

以下の画像のように**ドライブイメージのオプション**または仮想マシンの**編集タブ**からWindowsのISOイメージを取り外し、再度起動すると解決できます。

![スクリーンショット 2025-03-09 13.33.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3455723/374712f9-16d4-4741-a36d-935149cfcc0e.png)


### Rustの開発環境を整える

Rustで開発を行うために、以下の手順で環境を構築します。

1. [Rustのインストール](#rustのインストール)
2. [SSHの設定](#sshの設定)
3. [VSCodeのインストール](#vscodeのインストール)
4. [VSCodeでリモート接続する](#vscodeでリモート接続する)

#### Rustのインストール

Rustのインストーラーを公式サイトからインストールしてください。

https://www.rust－lang.org/ja/tools/install

Windowsではインストーラーを実行し、案内に従ってセットアップを進めます。
基本的には、`1) Proceed with standard installation`を選択すれば問題ありません。

インストール後、PowerShellを開き、`rustup`を使ってバージョンを設定しましょう。


### SSHの設定

Windowsで**OpenSSH Server**サービスを有効化し、リモート接続できるようにします。

下記の手順でOpen SSH Serverを起動できます。

1. 「設定」を開く
2. 「システム」を開く
3. 「オプション機能」を開く
4. 「機能を表示」を押して `OpenSSH Server` を検索し、インストール
5. サービス (`services.msc`) を開く
6. 「OpenSSH Server」を選択し、以下のように設定
    - スタートアップの種類: 自動
    - サービスの状態: 開始
7. [sshd_configの設定変更](#sshd_configの設定変更)
8. `Restart-Service sshd` を実行してSSHを再起動
9. [ファイアウォールの設定変更](#ファイアウォールの設定変更)

#### sshd_configの設定変更

以下のコマンドで `sshd_config` を開きます。

```powershell
notepad C:\ProgramData\ssh\sshd_config
```

以下の設定のコメントアウトを外し、保存してください。

```text:sshd_config
Port 22
AddressFamily any
ListenAddress 0.0.0.0
ListenAddress ::

PasswordAuthentication yes

PermitUserEnvironment no
```

#### ファイアウォールの設定変更

次のコマンドを実行し、SSH接続を許可します。

```powershell
New-NetFirewallRule -DisplayName "Allow SSH" -Direction Inbound -Protocol TCP -LocalPort 22 -Action Allow
```

### VSCodeのインストール

VSCodeをインストールしてください。
インストール後、ホスト側のVSCodeと同期し、同じ拡張機能を利用できるようにしましょう。

ただし、このままではビルド時に `リンカが見つかりません`という旨のエラーが発生します。
これは、<font color="red">Windowsが**ARM版**であるため発生する</font>エラーです。

この問題を解決するために、**Visual Studio Community**をインストールし、必要なコンポーネントを追加しましょう。

https://apps.microsoft.com/detail/xpdcfjdklzjlp8?launch=true&mode=full&hl=ja-jp&gl=jp&ocid=bingwebsearch

インストールする際は、「C++によるデスクトップ開発」に必ずチェックを入れましょう。

---

Visual Studio Communityは必要なコンポーネントを導入するためにインストールするだけで、開発では一切使用しません。**インストール後は無視して構いません。**

---

### VSCodeでリモート接続する

ホストのVSCodeに、`Remote - SSH`拡張機能をインストールしましょう。

インストール後、ホストの`~/.ssh/config`を作成または編集し、以下の内容を追記してください。

```ssh
Host windows
  HostName <IPアドレス>
  User <ユーザー名>
  Port 22
  # SSH接続が切れないように60秒ごとに通信
  ServerAliveInterval 60
```

設定が完了すると、「リモートエクスプローラー」タブに `windows`が表示されます。
`windows`を選択し、パスワードを入力するとリモート接続が完了します。

## RustでWindowsアプリ開発

Windows APIを呼び出すために、`windows`クレートを使用します。  

https://github.com/microsoft/windows-rs

今回は、Windows APIを利用してメッセージボックスを表示するアプリを実装しましょう。

### プロジェクトの作成

まず、Rustのプロジェクトを作成します。

```powershell
cargo new tutorial_windows_rs
```

次に、`windows`クレートをプロジェクトに追加します。
Cargo.tomlを編集し、以下の内容を追加してください。

```yaml:Cargo.toml
[package]
name = "tutorial_windows_rs"
version = "0.1.0"
edition = "2024"

[dependencies.windows]
# 記事投稿時の最新バージョン
version = "0.60.0"
features = [
    "Win32_UI_WindowsAndMessaging",
    "Win32_UI_Shell",
]
```

### コードの実装

`src/main.rs`に以下のコードを記述してください。

```rust:src/main.rs
// Windowsでコンソールを開かずに実行
#![cfg_attr(windows, windows_subsystem = "windows")]

use windows::{core::*, Win32::UI::Shell::*, Win32::UI::WindowsAndMessaging::*};

fn main() {
    unsafe {
        MessageBoxW(None, w!("末尾WはWin32APIのunicode対応!"), w!("タイトル"), MB_OK);
        
        ShellMessageBoxW(None, None, 
            w!("ShellMessageBoxではアイコンと効果音が設定可能!\n4つ目の引数で、アイコンと効果音が変わります"),
            w!("Win32 APIはw!でテキストを設定"), 
            MB_ICONERROR);

        ShellMessageBoxW(None, None, w!("MB_* で他のアイコンも設定可能"), w!("World"), MB_ICONWARNING);
    }
}
```

### ビルドと実行

記述が終わったら、ビルドしてください。

```powershell
cargo build

aarch64-pc-windows-msvc (installed)
```

デフォルトのターゲットが`aarch64-pc-windows-msvc`でない場合は、明示的に指定してビルドしてください。

```powershell
cargo build --target aarch64-pc-windows-msvc
```

ビルドが完了すると、以下のいずれかのディレクトリにexeファイルが生成されます。

* `target/debug/`
* `target/debug/aarch64-pc-windows-msvc/`

生成されたexeを実行すると、以下のようにメッセージボックスが表示されます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3455723/d259ca44-ac45-452a-b6d2-3d8dcf27ccf0.png" width="50%" />

これでメッセージボックスの実装が完了しました。

# まとめ

Windowsでの仮想環境構築に関する記事は多いですが、Windows自体を仮想環境として構築する記事は比較的少ないため、本記事が役に立てば嬉しいです。

今回の学習を通して、Windows上での開発環境のセットアップ方法や、Windows APIをプログラムから呼び出す具体的な実装方法を学ぶことができました。

また、Windowsのシステムを操作する方法を理解することで、新しいアイデアやインスピレーションも得られました。

さらに、`windows`クレートを使った他の実装方法について学びたい場合は、公式リポジトリのサンプルコードが参考になります。

https://github.com/microsoft/windows-rs/tree/master/crates/samples/windows