【C#】Internet Explorerを祭る文

# Internet Explorerを祭る文

歳は2022年に在り、6月15日、梅雨愈々来て雨多く日長きの時、青星忽焉として堕ちて声あり、嗚呼 Internet Explorer 死す。而して其黒歴史は全く抹殺されぬ。(略)

## はじめに

去る2022年6月15日、ついに単体のアプリケーションとしてのInternet Explorerが終了を迎えました。
これからもwindows界のコンポーネントとしては生き続けるようですが、一つの時代の終焉を実感せずにはいられません。
今回の記事は、そんな Internet Explorer の思い出話です。
~~ちなみにタイトルに深い意味はありません。かっこいいので使ってみました。~~

## C#er と Internet Explorer

さて、C#erにとってのInternet Explorerの思い出といえば、`WebBrowser` でしょう。
というのも、WindowsフォームやWPFに標準で搭載されている `WebBrowser` は、中身がInternet Explorerなのです。
特に、Windowsフォームの頃はまだまだIEが当たり前でしたので、非常にポピュラーな選択肢でした。
今回はそんな、Windowsフォームの `WebBrowser` コントロール ( `System.Windows.Forms`名前空間 ) の話です。

## デフォルトバージョンがいつまでもIE7

`WebBrowser` コントロール、何故か内部のIEのバージョンの既定値ががいつまでもバージョン7です。
そして、このバージョンの変更が C# コードから直接はできず、Windowsのレジストリに書き込むという謎仕様でした。
なので、こんな感じのおまじないを毎回書く羽目になりました。当然覚えられないので毎回調べてました。

```cs
const string FEATURE_BROWSER_EMULATION = @"Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION";
Microsoft.Win32.RegistryKey registryKey = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(FEATURE_BROWSER_EMULATION);
registryKey.SetValue(Process.GetCurrentProcess().ProcessName + ".exe", 11001, Microsoft.Win32.RegistryValueKind.DWord);
```

これでなんとか IE11 が内部で使われるようになります。懐かしい。

## 大量のスクリプトエラー

Webページのjavascript の実行で何かエラーが起きると、いちいちポップアップが出るのがデフォルト設定でした。
意味ないのに、閉じても閉じても何度も出ることがあって、本当にやかましかったです。

![スクリーンショット 2022-06-28 195850.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/510423/5a93df95-17f2-c608-7fdf-82f741950eb3.png)

なので、やっぱりこのポップアップを抑止するおまじないを毎回書いていました。
プロパティ1つで無効化できるだけ優しい。

```cs
webBrowser = new System.Windows.Forms.WebBrowser();
webBrowser.ScriptErrorsSuppressed = true;
```

## 自分のアプリケーションのUIに統合できる

WindowsフォームのUI部品として提供されているので、自分のアプリケーションのUIに完全に統合できました。
余計な部分は一切表示されないので、結構使い道が多様で面白かったです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/510423/064c5d18-58ad-5a75-6589-294f2cbaf40a.png)

## DOM操作のラッパーが提供されていた

世間で Google Chromeが流行り、Internet Explorer がオワコンと言われても、
WindowsフォームをやめてWPFに移行してもなおこの `WebBrowser` コントロールを使い続けたわけが、そこにありました。
当時 javascript なんて一ミリも書けなかった自分にとって、この `WebBrowser` コントロールが提供するDOMラッパーは
Webの世界を操作する唯一の手段だったのです。
例えば、以下のような **C# コード** で、フォームにテキストを入れて送信できました。

```cs
private void SendAnswer(string answer, WebBrowser webBrowser)
{
    webBrowser.Document.GetElementById("chatText").InnerText = answer;
    webBrowser.Document.GetElementById("chatSubmitButton").InvokeMember("click");
}
```

プロパティやメソッド名は大文字ではじまるところが C# らしいです。
これを活用して、いろいろなWeb自動化をして遊びました。

## メモリリークする

そうして javascript の勉強へ踏み出せず、いつまでも `WebBrowser`コントロールと Internet Explorer に頼り続けていた自分がついにやめたのは
この `WebBrowser`コントロールと Internet Explorer にはメモリリークのバグがあることがわかったからです。
あるスクレイピングのプロジェクトを進めていたとき、8時間くらい経ったところで何故か落ちる、というバグに見舞われることになります。
困難を極めたものの、頑張って色々デバッグした結果、Internet Explorer がメモリリークしているのが原因とわかりました。
32bitプロセスなのでメモリを使い果たしてバグっていたようです。
そうして、ついに Internet Explorer と決別しました。でもこれ、2020年の話です。

## 過去作を振り返る

この、`WebBrowser`コントロールと Internet Explorer の組み合わせで作ったプログラムとして忘れられないプロジェクトがあります。
Internet Explorer が完全終了し、このプロジェクトが過去の闇に埋もれてしまう前に思い出話をしておきます。

ピ⚪トセ⚪スという、友達が書いている絵が何なのかを当てるというブラウザゲームがありました。
このゲームには、ランダムな平仮名6文字が表示され続けるUIが存在し、このUIを押すことで必ず正解が入力できるようになっていました。
つまり、お題がN文字なら、少なめに見積もっても `(6/50) ^ N` に絞れるということです。
お題は高々数百～数千通りだったので、 これでほぼ答えにたどりつけるのではないかと予想されました。

ということで、実際に `WebBrowser`コントロールと Internet Explorer で実装したのでした。
予め、お題の候補を保存しておき、それをもとに、平仮名をノードとする木構造のデータを生成しておき、効率的に探索する工夫をしました。
クラスで本当に木を実現しているのが数年前の自分らしいです。
この時期はまだ競プロとか触れてないので、自己発明だと思われます。そう考えると結構すごい。

```cs
class StringTreeElement
{
    public char Element { get; private set; }
    public StringTreeElement Parent { get; private set; }
    public List<StringTreeElement> Children { get; private set; }
    public bool IsRoot { get; private set; }
    public bool HasFinish { get; set; }
}
```

たとえば、平仮名表示UIの解析は以下のように書くことができました。
Linq とか使ってないのがやっぱり数年前の自分らしいです。

```cs
/// <summary>
/// Webページを解析し、候補となっている文字を解析して返します
/// </summary>
/// <param name="webBrowser"></param>
/// <returns></returns>
private string[] ReadKohoChars(WebBrowser webBrowser)
{
    string[] kohoChar = new string[6];
    HtmlElement htmlKohoElement = webBrowser.Document.GetElementById("charList");
    var buttons = htmlKohoElement.GetElementsByTagName("button");
    int i = 0;
    foreach (HtmlElement button in buttons)
    {
        kohoChar[i] = button.InnerText;
        i++;
    }
    return kohoChar;
}
```

これで得た候補で、先程の木の枝を切っていきます。
そして、残っている枝の次のノードに対して各々同じ処理を繰り返していきます。これで答えが導出できるはずです。
ちょっと前に掲載したものと同じですが、導出できればこんな感じでフォームに導き出した答えを入れて送信できました。

```cs
private void SendAnswer(string answer, WebBrowser webBrowser)
{
    webBrowser.Document.GetElementById("chatText").InnerText = answer;
    webBrowser.Document.GetElementById("chatSubmitButton").InvokeMember("click");
}
```

めっちゃ javascript ぽいですが全部 C# のコードです。

そして、このプログラムは本当にミリ秒オーダーの世界で正解することができました。
解析から回答送信まですべて自動化されているので、人間が適う余地はありませんでした。
最初は回答までのディレイを挿入して、次第にディレイを減らして無双するという方法で友達に自慢しました。
今思うとめっちゃ下らないです。でも楽しかったですし、友人も技術の部分に興味を持ってもらって楽しんでたみたいなので Win-Win です。

ずっと笑いながら開発していたのを覚えています。多分人生で5本の指には入る楽しい開発でした。

## さいごに

多分もう `WebBrowser`コントロールと Internet Explorer を使うことはないでしょうし、最後に紹介したような過去作も、紹介できなかったものも、いずれ動かなくなってしまうのだと思われます。それでも、こんなふうにバカみたいなアプリケーションを作りながら習得した、C#プログラミングや Web の技術はこれからも生き続けるのでしょう。

ありがとう、`WebBrowser`コントロールと Internet Explorer。