他人のアプリをDLLを使って改造する仕組み予想 例題:MMD

あるWindowsアプリケーションに対して、不特定多数のユーザーがそのアプリのディレクトリにDLLファイルを置くだけで機能を拡張するツールを公開している。

単独のDLLファイルを作成するだけで干渉できるならばアプリ全体の知識がなくても作成できるのでは？
と考えた。結果は迷走ののち異なったが。

# 結論

**想像なので全部末尾に「だろう」や「と思われる」と付きますが、冗長なので省略します**

***

* ディレクトリ直下に置くだけでDLLファイルが何でも読み込まれるわけではない。
 * アプリケーションがもともとDLLの追加を想定していれば(MODのように？)別である。


***

* 第三者のカスタマイズを想定していないアプリへの干渉へはちょっとしたハックが必要。
* 具体的にはDLLの読み込み順を利用した既存DLLの改造？
 * DLLの探索順は(体感多くの場合)「exeファイルと同一ディレクトリ以下」 → 「WindowsがインストールしているDLL群」(それでもなければエラー)
 * なので、アプリが読み込むDLLを改造して他者が作成した追加のDLLを読み込む記述を追加する。
  * 改造DLLがアプリ同梱なら上書き、Windowsに事前インストールを前提としているDLLならexeのディレクトリに同名で新規設置として、インストールされているDLLより先に読み込ませる。[^1]
  * たとえば、`d3d9.dll`を使うアプリに自作の`my.dll`を呼ばせたい場合は、`my.dll`をloadする記述を追加した`d3d9.dll`を同時に用意してやればいい。  
     これで、そのアプリは直接は`my.dll`を読み込まないが、依存する`d3d9.dll`経由で`my.dll`を呼び出すことになる。

[^1]: 同一ディレクトリに入れればWindowsのそのDLLを参照する別アプリに対しては影響なしにできる

***

* 不特定多数がそれぞれ、アプリがもともと読み込むDLLを改造していくと、どこかで同一DLLを改造してしまう競合が発生する。  
  回避方法としていくつか考えられる
  1. それぞれの拡張機能の作者が別々の標準DLLを改造する。  
     基本的には後発が先発の操作DLLを調査して避けることになるか。
  2. 後発機能作者が、先発機能作者の改造記述を引き継ぐ。  
     後発が共存できるように配慮して、自分と他者のDLLの読み込み両方を記述してあげる。  
     同一DLLファイルを編集する場合に後発が慮る方法。ただし先発の更新で齟齬の発生やメンテナンスコストの増大が考えられるか
  3. 配下の任意のDLLをすべて読み込む「拡張機能管理」の拡張機能を作り、他をその配下にする。
     ゲームで言うMOD管理ツールのようなもの。ユーザー側で上下関係を作ってやれば、改造DLLは最小ひとつで済むはず。

# 前提 筆者知識

* C++
* C#
* .NET Framework
* WIN32API
* DLL

このあたりの知識がまったくありません。
9割を妄想で調査しています。

起動時のDLL不足程度なら自力でDLして解決できるかな？
程度です。

# [MMD](https://ja.wikipedia.org/wiki/MikuMikuDance) (MikuMikuDance) の例

(拡張文化が目に入ったことからの調査なのでMMD自体は無知です)

MMDという3Dモデルを動かすツールがあり、その各種拡張ツールについて調べます。

## [MME](https://www6.atwiki.jp/vpvpwiki/pages/219.html) (MikuMikuEffect)

おそらくもっとも導入されていそうな拡張です。

![mme.jpg](https://qiita-image-store.s3.amazonaws.com/0/62354/c9b84127-c59b-4036-5c52-2d62a2eddfff.jpeg)

導入することでMMDの何もなかったメニュー右端に新たなメニューが追加されます。

![mmefile.JPG](https://qiita-image-store.s3.amazonaws.com/0/62354/3edaf4a1-3cae-d9fe-671f-3545a80fb663.jpeg)

ファイル構成は三つのDLLで、exeと同一の場所に置くことを想定されています。
仕組みは分かりませんが置くだけでインストール完了とはMMDは柔軟なツールなのだなあと当時は思っていました。

この機能は、(勝手に)拡張機能の大本だと見ましたので、競合回避のどれにも当てはまりません。
他の機能がMMEと競合しないように回避していきます。とします。

## [MMAccel](https://www6.atwiki.jp/vpvpwiki/pages/220.html#id_b2fdca9c)

>DLLインジェクション方式。MMEと併用可能なd3d9.dllが同梱されている。

**MMEと併用可能なd3d9.dllが同梱されている。**
この記述で`d3d9.dll`という共通のdllがキーなのかなと予想しました。
(ちなみにDLLインジェクション方式と解説されていますが、`d3d9.dll`の変更のことなのか機能の実現方法なのかが読み取れませんでした。おそらく後者だと思われますが)

![mma.JPG](https://qiita-image-store.s3.amazonaws.com/0/62354/4df81d88-4644-4320-d7eb-988de83fff54.jpeg)
【[【MMDツール】MMDにショートカットキーを割り当てるツールを作ってみた](https://www.nicovideo.jp/watch/sm23307058)より】


[MMAccelは幸いにもソースコードが公開されているので](https://github.com/LNSEAB/mmaccel)適当に見てみると、DLLへの変換前の`d3d9`ファイルらしきものがありました。
逆アセンブルできないので助かります。

「MMEと併用可能」がキーワードなので、ヒストリーを眺めたりmmeで検索したりしてみます。

[mmaccel/d3d9.cpp at master · LNSEAB/mmaccel](https://github.com/LNSEAB/mmaccel/blob/master/d3d9/d3d9.cpp#L41-L44)

```d3d9.cpp
void load_mme() 
{
	winapi::load_library( winapi::get_module_path() + "\\MMHack.dll" );
}
```

ありました。

MMEで配布されている`MMHack.dll`を読み込む記述が、MMAccelでなされています。

これが結論の回避方法その2ですね。
後発のMMAccelがMMEと同一DLLを編集しつつも、後から上書きしても大丈夫なように配慮しています。

ここから逆説的に、MMEの機能自体は`MMHack.dll`であり`d3d9.dll`の重要な役目は別のdllのloadである。という仮説を立てました。

## [MMDPlugin](https://bowlroll.net/file/126059)

>MMDのプラグイン各種を簡単にインストールできるようにするツールです。基本的にこのツールを使用することを推奨します。 

ということで、anyenvのように各種ツールを簡単にインストールしてくれるツールです。

MMDPluginを含めインストールを補助する[MMDPluginInstallManager](https://github.com/oigami/MMDPluginInstallManager)は公開されていますが、MMDPlugin本体のコードは公開されていないのかな？

予想ではMMDPluginInstallManagerがインストールの自動化のみを行い、それで導入するMMDPluginが配下の各拡張(DLL)を読み込む機能を持っているのかなと思います。

MMDPluginのファイル構成は以下

![mmdp.JPG](https://qiita-image-store.s3.amazonaws.com/0/62354/b502d807-94c8-e5c1-68c9-7ad4c368d5a7.jpeg)

またも`d3d9.dll`がMMEと競合しています。
が、readmeを読むと

>
・インストール方法
d3d9.dllとMMDPlugin.dllをMikuMikuDance.exeがあるフォルダに入れるだけです。
このツール単体では何も動作しません。
>
・MMEも使う場合
  MMEをインストールするときに、plugin/mmeフォルダに入れるようにしてください

と回避方法が指示されています。
MMDPluginを入れるならば、全てMMDPluginで管理したほうがよさそうですね。

これが回避方法のその3、「拡張機能管理」の拡張機能かなと考えました。

MMDPluginInstallManagerが対応しているツール以外もMMDPluginが読み込んでくれるなら、自作のプラグインはこの機能経由で適用できるかもしれません。

## [MikuMikuPlus](https://www.nicovideo.jp/watch/sm34761249) (MMPlus) (旧: AVI出力前に座標軸兄貴を消し忘れてたら教えてくれるプラグイン)

これがたまたま目に入ったのでMMD周りをいろいろ調べだした動画です。

>機能を追加してMMDPluginを使わなくてもインストール可能なツールに生まれ変わりました！

前述のMMDPlugin依存からMMEのように単独でも動作するように昇格？された例です。(MMDPlugin配下でなくても共存できる(はず))

ではMikuMikuPlusのファイル構成を見ます。

![mmp.JPG](https://qiita-image-store.s3.amazonaws.com/0/62354/d0b68ee1-b674-2eb9-95de-114a12e55a94.jpeg)

今までの`d3d9.dll`と異なり、標準っぽいdllの同梱が`MSIMG32.dll`になっていますね。
回避方法その1、競合しないように改造DLLをずらす。がこれに当たるのかなと考えています。

利用者側としては単独で動くほうが嬉しいですが、裏側を知ろうとすると苦労が見えてきます。か？

機能開発がものすごく活発というわけではないので触る`dll`には困らなそうですが、逆に先の三つの機能がそろって`d3d9.dll`を触っている理由が気になりますね。
よく見かけるdllですし、各拡張機能によく絡むため挙ってこのファイルを操作しているのでしょうか？


おまけでMMDPluginが必要だったときのファイル(AVI出力前に座標軸兄貴～)も落としてみてみました。

![mmda.JPG](https://qiita-image-store.s3.amazonaws.com/0/62354/67ce2241-488a-13b8-e281-0ae35fe81231.jpeg)

MMDPluginを使う場合はやはりDLLは単体でよさそうですね。


## ということで

4つのツールからDLLを使った機能の仕組みを結論の項のように予想しました。
手がかりはないかといろいろ探した結果、ひとつ仮定ができるとあれがそーなってとそれぞれの仕組みがドミノ式に分かった(気になって)調べものの楽しさを味わいました。

今回は末端の末端、インストール方法のみを手がかりに知識なしで調べたため信頼度は薄く役立つ情報でもないですが、無知な状態から取っ掛かりを探すことはなかなか難しいので考えかたの記録にでもなればいいかなと思います。
とりあえずGitHubで探してみるは大事。


# 調査時困ったこと

MMDのことを調べたくてもモデルデータの記事ばかりが引っかかり、
MMEのことを調べたくてもエフェクトファイル(fx)の記事ばかりが引っかかり。
プログラムの方向から調べることが難しかった。(そもそもDLLの題材として不適切か)
本体含めコード非公開だったが、いくつかの拡張がGitHubで管理されていたため、そこから予想を立ててみた。

性質上動画での説明が多くてそこもちょっと大変でした。が、見ていて華やかなので楽しかったです。

***

あとDLLやWIN32APIの解説記事を見ても全然作れそうになさそうなことですね…なにを見て入門すればいいのやら…