ブラウザ上で動作する.NETデコンパイラ

少し前にブラウザ上で動作する.NETデコンパイラ Kani を作成したのでその紹介です。

ブラウザさえあれば簡単に試すことができます。

Kani.gif

PWAに対応しているのでメニューからインストールすることでローカルで動作させることも可能です。

ソース:yaegaki/Kani


解説

KaniではBlazordnSpyを内部で使用しています。

これらを使用することで簡単にブラウザ上で動作する.NETデコンパイラを作成することができました。


Blazorについて

Blazorはブラウザ上で.NETアプリを動作するためのフレームワークです。

以下の記事がqiitaでも人気になっていたので知っている人も多いのではないでしょうか。

C# で Single Page Web Application が書ける Blazor が凄かった件

Blazorの完成度は結構高く特に意識することなくC#で書いたコードが動作しました。


dnSpyについて

dnSpyはWindows向けの.NETデコンパイラでとにかく完成度が高いです。

凄すぎて大草原不可避な.NET デコンパイラdnSpyを使ってみる

dnSpy自体はWindows向けのアプリケーションですがすべてがC#で書かれています。

なのでGUIなどのWindowsに依存する部分を除けばBlazorで動かすことができるようになります。

幸いなことにdnSpyは機能ごとにプロジェクトが分かれており簡単に再利用できました。


苦労したポイント

2019/7/9時点での情報です


Blazorアプリのビルドに失敗する

Windows上でBlazorアプリをビルドすると特におかしいところがなくてもエラーになる場合がありました。

エラーログは以下のようなものでした。

  Unhandled Exception: Mono.Cecil.AssemblyResolutionException: Failed to resolve assembly: 'C:\Users\xx\.nuget\packaes\system.threading.tasks.parallel\4.3.0\lib\netstandard1.3\System.Threading.Tasks.Parallel.dll, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' ---> Mono.Cecil.AssemblyResolutionException: Failed to resolve assembly: 'C:\Users\xx\.nuget\packaes\system.threading.tasks.parallel\4.3.0\lib\netstandard1.3\System.Threading.Tasks.Parallel.dll, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'

at Mono.Linker.DirectoryAssemblyResolver.Resolve(AssemblyNameReference name, ReaderParameters parameters)
at Mono.Linker.AssemblyResolver.Resolve(AssemblyNameReference name, ReaderParameters parameters)
at Mono.Linker.LinkContext.Resolve(IMetadataScope scope)
at Mono.Linker.LinkContext.Resolve(IMetadataScope scope)
at Mono.Linker.LinkContext.Resolve(String name)
at Mono.Linker.Steps.ResolveFromAssemblyStep.Process()
at Mono.Linker.Steps.BaseStep.Process(LinkContext context)
at Mono.Linker.Pipeline.ProcessStep(LinkContext context, IStep step)
at Mono.Linker.Pipeline.Process(LinkContext context)
at Mono.Linker.Driver.Run(ILogger customLogger)
at Mono.Linker.Driver.Execute(String[] args, ILogger customLogger)
at Mono.Linker.Driver.Main(String[] args)

内容的にはアセンブリの解決ができなかったというものですがパスをよく見たら何かおかしいです。

本来ならばnugetのパッケージのパスはC:\Users\xx\.nuget\packages\...というものですがC:\Users\xx\.nuget\packaes\...となっています。

(packagespackaesになっている)

詳細はわかりませんがおそらく引数が長くなりすぎて限界を超えてしまったため、一部の情報が欠落してしまったのだと思います。

コマンド プロンプト (Cmd.exe) へ 8192 文字以上の引数を渡した場合に発生する現象

根本的にはどうやって解決するべきかわからなかったため参照するアセンブリの数を減らすという対策をとりました。

dnSpyは多言語に対応しておりリソースファイルが多かったのでその数を減らしました。

https://github.com/yaegaki/dnSpy/commit/d2fcbcdf96cfad23f8e093caffe63560490a7535


ファイルのドラッグドロップについて

Kaniは.NETアセンブリをドラッグドロップすることでデコンパイルを行います。

このドラッグドロップを実装するために少し苦労しました。

ブラウザ上にファイルをドラッグドロップしてそのバイナリを得るためにはdragoverdropで発生するイベントについてpreventDefaultを呼び出す必要があります。

Blazorのバインド機能でイベントを設定した場合、どうやってもpreventDefaultを呼ぶことができずページ遷移が発生してしまいました。

これを回避するためにはjsで処理を書く必要があり、C#だけで完結できずに少し残念でした。

また、jsからUint8ArrayをC#に渡す方法がよくわからず最終的にはblobにしてblobURLを発行しC#側でHTTP.GETを行うという回りくどい方法をとりました。

これについてはもう少しまともな方法があるかもしれません。


最後に

BlazordnSpyが凄すぎて感動しました。:sparkles:

特にBlazorは色々可能性を感じました。:zap:

適当なC#アプリをブラウザに移植してみるのも楽しいと思いますのでぜひやってみてください。:thumbsup: