IEのクローズ処理をハンドリングする
普通はそんなの難しいからあきらめてもらうのだが、どうしてもやりたいというので考えることにした。
まずは、Googleで調べてみるとそれなりにHITする。みんな苦労しているんだな。
onbeforeunload
最初にたどり着いたのは、onbeforeunload イベントを利用する方法。onbeforeunload は ページがアンロードされる前に発生するものなので、リンクを押したりしたりするだけでも発生する。なので、クローズ以外でも発生することが問題になり、これを対策する必要がある。がんばってみよう。
よく利用されている対策は、onbeforeunloadイベントが発生した際の event.clientYやevent.clientXを調べて0より小さい場合はCloseだと判断する方法である。しかし、この方法だと更新ボタンを押してもCloseだと判断してしまう問題がある。
これを対策する方法としては、先ほどのevent.clientXをもっと厳密に調べるアイデアがあるようで、それが以下に紹介されていた。IE6で確認したが、更新ボタンをうまく無視できた。すごい力技!!
End User Sessions When the Browser Closes With Remote Scripting
ただ、それでも「File->close」や「Alt-F4」には対応できないようだ。また、こんなことはしないだろうが、xボタンの付近でF5押しても誤った認識をしてしまう。
この方法はあきらめたほうがよいかな。
監視用Window
次に思いつくアイデアとしては、別のWindowから監視する方法だが、監視用のWindowをはじめからは上げられない場合は駄目かな。と思っていたが、先ほどのonbeforeunload のタイミングで監視用のWindowを上げる方法を利用した方法があった。
Detect browser closing through clicks on the X button
簡略化したスクリプトで試してみた。
my.html <script language=javascript> function launch_spyWin() { spyWin = open('spy.html','spyWin', 'width=100,height=100,left=2000,top=0,status=0'); spyWin.blur(); } onunload = launch_spyWin; </script>
spy.html <script language='javascript'> function check_opener() { if (opener && opener.closed){ alert("IEが閉じられます"); parent.opener=''; parent.close(); } else{ parent.opener=''; parent.close(); } } onload = function() { self.blur(); setTimeout('check_opener()',0); } </script>
上手くいくようだ。GOOD、GOOD!!
codeprojectでの評価は3ちょっとと高くないけど素晴らしい。
ActiveX
とりあえず目標達成したのだが、もっと直接的な方法で本当にIEのCloseを取ることができないかな。
JavaScriptでは無理でもActiveXを利用したらできるかな。
調べてみると、どうやらブラウザ(IE)コンポーネントのOnQuitをハンドルすればいいようだ。
ということで、ActiveXからブラウザ(IE)コンポーネントを取得すれば方法を調査。
ActiveX コントロールから トップレベル IWebBrowser2 インターフェイスを取得する方法
#自動翻訳の割にはまともなタイトルだ
VC++か、.NETでのサンプルはないかなと調べてみるとこんなのがあった。
Re: WinForms control in Internet Explorer の#10のMSFTのリプライ
サンプルをもとに実際に作ってみる。確認だけなので、よく理解しないしていない部分はあるがとりあえず作ってみたら、動いた。ヤッタ!!
using System; using System.Runtime.InteropServices; using System.Reflection; using System.Security; using System.Windows.Forms; using SHDocVw; //c:\WINDOWS\system32\shdocvw.dll [Guid("76DC5F81-459D-4fb7-A9B5-8D183CD0EB0F")] public class WatchQuit : System.Windows.Forms.Control { private Guid SID_STopLevelBrowser = new Guid(0x4C96BE40, 0x915C, 0x11CF, 0x99, 0xD3, 0x00, 0xAA, 0x00, 0x4A, 0xE8, 0x37); private Guid SID_SWebBrowserApp = typeof(SHDocVw.IWebBrowserApp).GUID; public WatchQuit() { this.HandleCreated += new EventHandler(WatchQuit_Created); } void WatchQuit_Created(object sender, EventArgs e) { try { Guid guidIServiceProvider = typeof(IServiceProvider).GUID; Guid guidIWebBrowser2 = typeof(SHDocVw.IWebBrowser2).GUID; object objIServiceProvider2; object objIWebBrowser2; Type typeIOleObject = this.GetType().GetInterface("IOleObject", true); //call the method on that interface object oleClientSite = typeIOleObject.InvokeMember("GetClientSite", BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.Public, null, this, null); IServiceProvider serviceProvider = oleClientSite as IServiceProvider; serviceProvider.QueryService(ref SID_STopLevelBrowser, ref guidIServiceProvider, out objIServiceProvider2); serviceProvider = objIServiceProvider2 as IServiceProvider; serviceProvider.QueryService(ref SID_SWebBrowserApp, ref guidIWebBrowser2, out objIWebBrowser2); IWebBrowser2 webBrowser = objIWebBrowser2 as IWebBrowser2; InternetExplorer ie = (InternetExplorer)webBrowser; ie.OnQuit += new DWebBrowserEvents2_OnQuitEventHandler(ie_OnQuit); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex.ToString()); } } void ie_OnQuit() { MessageBox.Show("IEを終了します"); } } [ComImport, Guid("6d5140c1-7436-11ce-8034-00aa006009fa"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public interface IServiceProvider { void QueryService(ref Guid guidService, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out object ppvObject); }
あとは以下のようなObjectタグでActiveXをHTMLに張り付ければOK。
<object id="WatchQuit" classid="CLSID:76DC5F81-459D-4fb7-A9B5-8D183CD0EB0F"></object>
結論
IEのクローズ処理は取れないことはない。でもやっぱりトリッキーなので、まずはあきらめてもらうほうがいいのだろうな。
追記
残念ながらIE7では試していない。