2010-10-05

WebBrowserコントロールのコンテキストメニューを無効化する

QRリーダーを使っていただいた方はお気づきだと思いますが、画面下段の結果表示部分はWebBrowerコントロールを使っています。

WebBrowserコントロールなので タップ&ホールドすると コンテキストメニューが表示されてしまうのですが、表示される意味が無いので何とかしたいところです。(”すべてのテキストを選択” と ”ズームレベル” はそこそこ有用かもしれませんが・・・)

ContextMenuプロパティに NULLをセットしてみたりもしたのですが、効果ありませんでした。

リモートスパイで覗いてみると WebBrowserコントロールの下に IExploreというウィンドウがあって更にその下に MSPIE Statusと PIEHTMLというウィンドウがあるみたいです。さすがに複雑な構造になっているんですね。
ちなみにタップ&ホールドした時のメッセージは PIEHTMLが受け取っていました。

対象がわかったのでこのコントロールにメッセージを渡さないようにします。

まずは P/Invokeの準備。
せっかく .NETで作ってるのに結局はこうなるのか と思わないでもないですが...
#region P/Invoke関係
[DllImport("coredll.dll")]
private extern static IntPtr SetWindowLong(IntPtr hwnd, int nIndex, IntPtr dwNewLong);
private const int GWL_WNDPROC = -4;

[DllImport("coredll.dll")]
private extern static int CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hwnd, uint msg, uint wParam, int lParam);
private const uint WM_NOTITY = 0x004E;
private const uint WM_INITMENUPOPUP = 0x0117;
private const uint WM_ENTERMENULOOP = 0x0211;

[DllImport("coredll.dll")]
private extern static IntPtr GetWindow(IntPtr hwnd, uint uCmd);
private const uint GW_HWNDNEXT = 2;
private const uint GW_CHILD = 5;

[DllImport("coredll.dll", EntryPoint = "GetClassNameW", CharSet = CharSet.Unicode)]
public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
private const string PIEHTML = "PIEHTML";
private const int MAX_CLASSNAME = 64;

private delegate int WndProcDelegate(IntPtr hwnd, uint msg, uint wParam, int lParam);

private IntPtr _oldWndProcPtr;
private IntPtr _newWndProcPtr;
private WndProcDelegate _wndProc;
private IntPtr _hWndPIEHTML;
#endregion

次に差し替えるウィンドウプロシージャを準備。
特定のメッセージを無視しているだけです。
protected virtual int WndProc(IntPtr hwnd, uint msg, uint wParam, int lParam)
{
    if (WM_NOTITY == msg ||
        WM_INITMENUPOPUP == msg ||
        WM_ENTERMENULOOP == msg)

    {
        return 0;
    }

    return CallWindowProc(this._oldWndProcPtr, hwnd, msg, wParam, lParam);
}

フォームのコンストラクタで WebBrowser(の孫ウィンドウ)をサブクラス化します。
Windows Mobileには EnumChildWindows()が無いのでちょっと面倒ですね。
ヘッダ眺めてたら普通にありましたw
もう少し楽に書けそうですが、作った後だったのでまぁいいか...。
// P/InvokeでWebブラウザコントロールをサブクラス化
// WebBrowserの下の IExploreの下の MSPIE Statusの次の PIEHTMLが目的
this._hWndPIEHTML = IntPtr.Zero;
IntPtr hWndIExplore = GetWindow(this.webBrowser.Handle, GW_CHILD);
if (IntPtr.Zero != hWndIExplore)
{
    IntPtr hWnd = GetWindow(hWndIExplore, GW_CHILD);
    StringBuilder sbClassName;
    while (IntPtr.Zero != hWnd)
    {
        sbClassName = new StringBuilder(MAX_CLASSNAME);
        GetClassName(hWnd, sbClassName, sbClassName.Capacity);
        if (PIEHTML == sbClassName.ToString())
        {
            this._hWndPIEHTML = hWnd;
            break;
        }
        hWnd = GetWindow(hWnd, GW_HWNDNEXT);
    }
}

if (IntPtr.Zero != this._hWndPIEHTML)
{
    this._wndProc = new WndProcDelegate(WndProc);
    this._newWndProcPtr = Marshal.GetFunctionPointerForDelegate(this._wndProc);
    this._oldWndProcPtr = SetWindowLong(this._hWndPIEHTML, GWL_WNDPROC, this._newWndProcPtr);
}

一応、終了時に後始末をしておきます。
protected override void Dispose(bool disposing)
{
    if (disposing && (components != null))
    {
        if (IntPtr.Zero != this._hWndPIEHTML)
        {
            SetWindowLong(this._hWndPIEHTML, GWL_WNDPROC, this._oldWndProcPtr);
        }
        components.Dispose();
    }
    base.Dispose(disposing);
}
という訳でようやくコンテキストメニューを無効化できました。
他にもアドレス帳の登録で項目を間違えていた部分があったので修正しています。

今回から ひっそりと Giraffe に登録しています。
もう少し完成度が上がったら Marketplaceにも登録したいのですが先は長そうですね。

0 件のコメント: