9

これが課題です。フレームワークのWebBrowserSiteクラスから派生しています。派生クラス のインスタンスがImprovedWebBrowserSiteを介して返されます。このインスタンスは、クラスWebBrowser.CreateWebBrowserSiteBaseの派生バージョンでオーバーライドしWebBrowserます。具体的には、カスタム サイト オブジェクトを提供します。フレームワークのWebBrowser実装は、それを基になるアンマネージ WebBrowser ActiveX コントロールにさらに渡します。

これまでのところ、実装でオーバーライドすることができましたIDocHostUIHandlerImprovedWebBrowserSitethis のよう)。IOleClientSiteにパススルーしたいのような、より多くのコア COM インターフェイスを探していますWebBrowserSite。それらはすべて を使用して COM に公開されますが、 /のフレームワークの実装によってまたはComImportとして宣言されます。したがって、派生クラスでそれらを明示的に再実装することはできません。で行ったように、独自のバージョンを定義する必要があります。privateinternalWebBrowserSiteUnsafeNativeMethodsIDocHostUIHandler

問題は、WebBrowserSite派生クラスから で定義されたプライベートまたは内部 COM インターフェイスのメソッドを呼び出すにはどうすればよいかということです。たとえば、 を呼び出したいとしますIOleClientSite.GetContainer。リフレクション (このWebBrowserような) を使用できますが、それはゼロからの再実装に次ぐ最後の手段です。

私の考えでは、フレームワークのプライベートUnsafeNativeMethods.IOleClientSiteと私自身ImprovedWebBrowserSite.IOleClientSiteはどちらもCOMインターフェイスであり、ComImport属性、同じ GUID、および同じメソッド シグネチャで宣言されているためです。.NET 4.0+にはCOM Type Equivalenceがあるため、リフレクションなしでそれを行う方法が必要です。

[更新]解決策が得られたので、 WinForms バージョンWebBrowserコントロールをカスタマイズする際に、いくつかの新しい興味深い可能性が開かれると思います。

このバージョンの質問は、問題をより抽象的な形式で定式化しようとした私の最初の試みが、コメンテーターによって誤解を招くと呼ばれた後に作成されました。コメントは後で削除されましたが、両方のバージョンを保持することにしました。

この問題を解決するためにリフレクションを使用したくないのはなぜですか? いくつかの理由から:

  • WebBrowserSiteバイナリ v テーブル コントラクトに関する COM インターフェイスとは異なり、 の実装者によって指定された、内部メソッドまたはプライベート メソッドの実際のシンボリック名への依存。

  • かさばるリフレクション コード。TranslateAcceleratorたとえば、を介してベースのプライベートを呼び出すことを検討Type.InvokeMemberしてください。呼び出すメソッドは 20 まであります。

  • あまり重要ではありませんが、効率: リフレクションを介したレイト バインド呼び出しは、v-table を介した COM インターフェイス メソッドへの直接呼び出しよりも常に効率が低くなります。

4

2 に答える 2

9

最後に、@EricBrown の助けを借りて、 を使用して問題を解決したと思います。Marshal.CreateAggregatedObject

WebBrowserSite以下に、 OLE インターフェイスのカスタマイズを可能にするコードを示しますIOleClientSite。例として、COM から見える のプライベートな実装を呼び出しWebBrowserSiteます。他のインターフェイスに拡張できますIDocHostUIHandler

using System;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace CustomWebBrowser
{
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            var wb = new ImprovedWebBrowser();
            wb.Dock = DockStyle.Fill;
            this.Controls.Add(wb);
            wb.Visible = true;
            wb.DocumentText = "<b>Hello from ImprovedWebBrowser!</b>";
        }
    }

    // ImprovedWebBrowser with custom pass-through IOleClientSite 
    public class ImprovedWebBrowser: WebBrowser
    {
        // provide custom WebBrowserSite,
        // where we override IOleClientSite and call the base implementation
        protected override WebBrowserSiteBase CreateWebBrowserSiteBase()
        {
            return new ImprovedWebBrowserSite(this);
        }

        // IOleClientSite
        [ComImport(), Guid("00000118-0000-0000-C000-000000000046")]
        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        public interface IOleClientSite
        {
            void SaveObject();

            [return: MarshalAs(UnmanagedType.Interface)]
            object GetMoniker(
                [In, MarshalAs(UnmanagedType.U4)] int dwAssign,
                [In, MarshalAs(UnmanagedType.U4)] int dwWhichMoniker);

            [PreserveSig]
            int GetContainer([Out] out IntPtr ppContainer);

            void ShowObject();

            void OnShowWindow([In, MarshalAs(UnmanagedType.I4)] int fShow);

            void RequestNewObjectLayout();
        }

        // ImprovedWebBrowserSite
        protected class ImprovedWebBrowserSite :
            WebBrowserSite,
            IOleClientSite,
            ICustomQueryInterface,
            IDisposable
        {
            IOleClientSite _baseIOleClientSite;
            IntPtr _unkOuter;
            IntPtr _unkInnerAggregated;
            Inner _inner;

            #region Inner
            // Inner as aggregated object
            class Inner :
                ICustomQueryInterface,
                IDisposable
            {
                object _outer;
                Type[] _interfaces;

                public Inner(object outer)
                {
                    _outer = outer;
                    // the base's private COM interfaces are here
                    _interfaces = _outer.GetType().BaseType.GetInterfaces(); 
                }

                public CustomQueryInterfaceResult GetInterface(ref Guid iid, out IntPtr ppv)
                {
                    if (_outer != null)
                    {
                        var ifaceGuid = iid;
                        var iface = _interfaces.FirstOrDefault((t) => t.GUID == ifaceGuid);
                        if (iface != null)
                        {
                            var unk = Marshal.GetComInterfaceForObject(_outer, iface, CustomQueryInterfaceMode.Ignore);
                            if (unk != IntPtr.Zero)
                            {
                                ppv = unk;
                                return CustomQueryInterfaceResult.Handled;
                            }
                        }
                    }
                    ppv = IntPtr.Zero;
                    return CustomQueryInterfaceResult.Failed;
                }

                ~Inner()
                {
                    // need to work out the reference counting for GC to work correctly
                    Debug.Print("Inner object finalized.");
                }

                public void Dispose()
                {
                    _outer = null;
                    _interfaces = null;
                }
            }
            #endregion

            // constructor
            public ImprovedWebBrowserSite(WebBrowser host):
                base(host)
            {
                // get the CCW object for this
                _unkOuter = Marshal.GetIUnknownForObject(this);
                Marshal.AddRef(_unkOuter);
                try
                {
                    // aggregate the CCW object with the helper Inner object
                    _inner = new Inner(this);
                    _unkInnerAggregated = Marshal.CreateAggregatedObject(_unkOuter, _inner);

                    // turn private WebBrowserSiteBase.IOleClientSite into our own IOleClientSite
                    _baseIOleClientSite = (IOleClientSite)Marshal.GetTypedObjectForIUnknown(_unkInnerAggregated, typeof(IOleClientSite));
                }
                finally
                {
                    Marshal.Release(_unkOuter);
                }
            }

            ~ImprovedWebBrowserSite()
            {
                // need to work out the reference counting for GC to work correctly
                Debug.Print("ImprovedClass finalized.");
            }

            public CustomQueryInterfaceResult GetInterface(ref Guid iid, out IntPtr ppv)
            {
                if (iid == typeof(IOleClientSite).GUID)
                {
                    // CustomQueryInterfaceMode.Ignore is to avoid infinite loop during QI.
                    ppv = Marshal.GetComInterfaceForObject(this, typeof(IOleClientSite), CustomQueryInterfaceMode.Ignore);
                    return CustomQueryInterfaceResult.Handled;
                }
                ppv = IntPtr.Zero;
                return CustomQueryInterfaceResult.NotHandled;
            }

            void IDisposable.Dispose()
            {
                base.Dispose();

                // we may have recicular references to itself
                _baseIOleClientSite = null;

                if (_inner != null)
                {
                    _inner.Dispose();
                    _inner = null;
                }

                if (_unkInnerAggregated != IntPtr.Zero)
                {
                    Marshal.Release(_unkInnerAggregated);
                    _unkInnerAggregated = IntPtr.Zero;
                }

                if (_unkOuter != IntPtr.Zero)
                {
                    Marshal.Release(_unkOuter);
                    _unkOuter = IntPtr.Zero;
                }
            }

            #region IOleClientSite
            // IOleClientSite
            public void SaveObject()
            {
                Debug.Print("IOleClientSite.SaveObject");
                _baseIOleClientSite.SaveObject();
            }

            public object GetMoniker(int dwAssign, int dwWhichMoniker)
            {
                Debug.Print("IOleClientSite.GetMoniker");
                return _baseIOleClientSite.GetMoniker(dwAssign, dwWhichMoniker);
            }

            public int GetContainer(out IntPtr ppContainer)
            {
                Debug.Print("IOleClientSite.GetContainer");
                return _baseIOleClientSite.GetContainer(out ppContainer);
            }

            public void ShowObject()
            {
                Debug.Print("IOleClientSite.ShowObject");
                _baseIOleClientSite.ShowObject();
            }

            public void OnShowWindow(int fShow)
            {
                Debug.Print("IOleClientSite.OnShowWindow");
                _baseIOleClientSite.OnShowWindow(fShow);
            }

            public void RequestNewObjectLayout()
            {
                Debug.Print("IOleClientSite.RequestNewObjectLayout");
                _baseIOleClientSite.RequestNewObjectLayout();
            }
            #endregion
        }
    }
}
于 2013-11-02T07:00:00.267 に答える
4

ちょっと考えただけですが、ここからソースコードの一部を使用できるかもしれません。あなたは再実装するでしょうが、それはあなたが望むものを与えるかもしれません。

于 2013-11-01T12:47:59.523 に答える