Technically Impossible

Lets look at the weak link in your statement. Anything "Technically Impossible" basically means we haven't figured out how yet.

PowerShellによるISOファイルのマウント、そしてドライブ・レターの変更

日本語、英語に関わらず、辞書を引くと言えば、ほぼweb検索と等しい行為となって久しい。定番は英語であれば英辞郎、国語辞典であればweblioだろうか。広告によるビジネス・モデルによって、ユーザーには基本的に無料で提供されている。
その広告表示があまりに過剰であり、辞書としての利用を損なうだけでなく、不愉快な気分にさせられることが増えた。その詳細は余談に譲るとして、中辞典程度で構わないから、辞書として信頼性のあるものをローカル環境に保持できないものかと考えていた。

そういえば90年代からゼロ年代にかけて、辞書はOffice系パッケージに添付されていた時代があった。例えばMicrosoft Officeには小学館Bookshelf Basicが添付されていたし、広辞苑もCD-ROMで販売されていた。
今やCD-ROMをISOファイルとしてローカル環境に保存しても、ストレージ容量は十分に余りある時代となった。ならばISOファイルをマウントして利用すれば良いではないか、と思い至った。
Windows 10にはISOファイルのマウント機能が備えられているものの、ドライブ・レターの割り当ては機械任せだ。ショートカット作成のために、その割当を固定できれば利便性も向上する。そこでPowerShellを活用する。

図らずも、少々勉強することになったのだが、問題なくアイデアを実現することができた。コマンドプロンプト・コマンドは用いず、PowerShellだけで完結することができる。

おさらい

PowerShellの説明、紹介に入る前に、ストレージについてのおさらい。

バイス
ディスク
ドライブ
物理機器、論理機器
それらの抽象化
オフィスのフロア
パーティション ディスク内の区切り、仕切り フロア内の仕切り
ボリューム パーティションによって区切られた空間 仕切られた部屋
会議室など
ドライブ・レター パーティション、ボリュームへのポインタ 会議室番号
部屋番号
座席番号など

まずデバイス、ディスクという概念がある。どちらも、ほぼ同義に用いられる。端的にはHDDや光学ドライブのような機器を指しているのだが、物理、論理の区別はなく抽象的な存在を指している。複数の物理機器をまとめて論理的に一つのドライブ形成する(つまりRAID)もあれば、仮想サーバーのディスクにマウントする仮想ディスク(つまりファイル)である場合もあるからだ。ここではディスクと呼ぶことにする。

ディスク内の使用領域は必要に応じて区切られる。これをパーティションと呼ぶ。パーティションは文字通り、仕切りや区切りのことを指している。区切られた空間は含まない。区切られた空間はボリュームと呼ばれる。
オフィスや部屋のパーティションを想像すると良い。それらは区切られた空間を指すのではなく、仕切りそのものを指している。

そしてドライブ・レターは、パーティションかボリュームに割り当てられる。それはマウント・ポイントとして機能する。オフィスの例に倣えば、それは部屋番号と言ったところだろうか。

考え方

フロアがパーティションで仕切られ、仕切られた空間に部屋番号を割り当てる例え話を続ける。
HDDやSSDのようなパーティションで仕切ることのできる空間というのは、オフィス・ビルの単一フロアだ。一方、光学メディアやISOファイルは、言うなればパーティション設置不可、仕切ることのできない単一フロア全体といえば、理解できるだろうか。
そのようなフロアに部屋番号(ドライブ・レター)を割り当てるならば、パーティションが存在しないため、次のいずれかに割り当てるしかない。

  • フロア空間をホストするもの(例えば光学ドライブ、その仮想ドライブ)
  • フロアそのもの(つまりボリューム)

そして、ISOファイルにドライブ・レターを割り当てるならば、それは後者になるということだ。

注意

ここで紹介するスクリプトを実行するには管理者権限が必要だ。もしPowerShell、あるいはVSCodeのターミナル上で動作させる場合には、それらが「管理者として実行」されている必要があることに注意。
用いているスクリプト・コード、並びにコマンドレットなどのリファレンスは投稿末尾にまとめている。
f:id:espio999:20201202232301p:plain

Bad pattern

PowerShellでは、ドライブ・レターの操作はパーティション・レベルで処理される。おさらいを踏まえれば、次の手順を実施することになるのだが、これは失敗する。パーティションが存在しないからだ。

  1. 目的のISOファイルをマウントする。
  2. 1のボリュームを取得する。
  3. 2のパーティションを取得する。
  4. 取得したパーティションのドライブ・レターを変更する。

f:id:espio999:20201202232409p:plain

CD-ROMはZドライブにマウントされている。そのディスク・イメージからボリューム情報は取得できるものの、パーティション情報は取得できない。念のためパーティションを一覧表示すると、Zドライブはパーティションとして存在していないことが分かる。
結果として、この方法では"Set-Partition"を利用してドライブ・レターを変更する手立てが通用しないことが分かる。

Good pattern

ボリュームへドライブ・レターを割り当てるには、"diskpart"や"mountvol"などのコマンドを用いるのも一案だが、ここではそれらは用いない。
UNX/Linux系のシェルはテキストの世界だ。いわゆるパイプ処理は、出力結果としてのテキストを引き継ぎながら処理していく。コマンドプロンプトも同様だ。
一方PowerShellはオブジェクトの世界だ。PowerShellでのパイプ処理はオブジェクトを引き継ぎながら処理していく。ISOファイル、それにまつわるボリュームもオブジェクトとして処理したいところだ。

ここではインスタンス化したWMIオブジェクトを用いることにした。おさらいを踏まえた上で、次の手順を実施することになる。そして、これは成功する。

  1. 目的のISOファイルをマウントする。
  2. 1のボリュームのドライブ・レターを特定する。
  3. 特定したドライブ・レターを持つWMIオブジェクトのドライブ・レターを目的にものへ変更する。

順番にコードを説明していく。まずmyISOにISOファイルの絶対パスを指定し、それをマウントする。

$myISO = 'E:\ISO\Microsoft Bookshelf Basic\BSBASIC2.ISO'
mount-diskimage $myISO

マウントしたISOファイルのボリュームを取得し、割り当てられたドライブ・レターを確認する。PowerShellではドライブ・レターは'X'、'Y'のように1文字だが、WMIでは'X:'のように「:」が必要なので付与する。
目的とする新しいドライブ・レターを定義する。

$vol = Get-DiskImage $myISO | Get-Volume
$old_drv = $vol.DriveLetter + ':'
$new_drv = 'X:'

まずボリューム情報の一覧をWMIオブジェクトとして取得する。取得したボリューム情報から、ISOファイルが割り当てられているボリュームを特定し、インスタンス化する。インスタンス化したボリュームのドライブ・レターを目的のものへ変更する。
この一連の流れをパイプを介して1行で実行している。

Get-WmiObject -Class Win32_Volume | Where-Object {$_.DriveLetter -eq $old_drv} | Set-WmiInstance -Arguments @{DriveLetter=$new_drv}

f:id:espio999:20201202232455p:plain
結果はこの通りだ。ISOファイルが割り当てられていたドライブは、Xドライブに変更された。
一連のコードをスクリプト・ファイルとして保存し、タスクスケジューラからログオン時、あるいはスタートアップ時にでも起動するようにしておくと良い。

余談

次のリンクはweblioの国語辞典、中国語辞典で「鍋」という言葉を検索するものだ。挿入される広告の量を比較してみてほしい。複数の辞典を横串検索した出力結果は共通の仕様だが、国語辞典の場合は、辞書ごとの出力結果に広告が挿入されている。
ユーザーには基本的にコンテンツを無料提供する以上、広告に依存しなければならないビジネス・モデルに文句はない。しかし度が過ぎていやしないかと思うのだ。
www.weblio.jp
cjjc.weblio.jp

コード

Bad pattern

gist.github.com

Good pattern

gist.github.com