WebBrowserSite:如何在派生类中调用私有COM接口方法?

时间:2013-10-31 22:37:46

标签: c# .net com webbrowser-control com-interop

这是挑战。我来自Framework的WebBrowserSite课程。我的派生类ImprovedWebBrowserSite的一个实例是通过WebBrowser.CreateWebBrowserSiteBase返回的,我在WebBrowser类的派生版本中覆盖了它 - 专门用于提供自定义站点对象。 Framework的WebBrowser实现进一步将其传递给底层的非托管WebBrowser ActiveX控件。

到目前为止,我已成功覆盖IDocHostUIHandler实施中的ImprovedWebBrowserSite(例如this)。我现在正在寻找更多核心COM接口,例如IOleClientSite,我希望将其传递给WebBrowserSite。所有这些都通过ComImport向COM公开,但由框架的private / internal实现声明为WebBrowserSiteUnsafeNativeMethods。因此,我不能在派生类中explicitly re-implement它们。我必须定义自己的版本,就像我对IDocHostUIHandler所做的那样。

所以问题是,如何从我的派生类中调用WebBrowserSite中定义的私有或内部COM接口的方法?例如,我想调用{ {1}}。我可以使用反射(如this),但这是最后的手段,第二是从头开始重新实现IOleClientSite.GetContainer

我的想法是,因为框架的私有WebBrowser和我自己的UnsafeNativeMethods.IOleClientSite都是 COM 接口,使用ImprovedWebBrowserSite.IOleClientSite属性声明,相同的GUID和相同的方法签名。在.NET 4.0+中有COM Type Equivalence,因此必须有一种方法可以在没有反射的情况下完成它。

[更新] 现在我已经获得了solution,我相信它会为自定义ComImport WebBrowser开启一些新的有趣的可能性控制。

此问题的版本是在WinForms version之后创建的,以更抽象的形式表达问题被评论员误称为误导。该评论已被删除,但我决定保留这两个版本。

为什么我不想使用反射来解决这个问题?有几个原因:

  • 依赖于WebBrowserSite的实现者给出的内部或私有方法的实际符号名称,与COM接口不同,后者是关于二进制v表合约。

  • 庞大的反射代码。例如,考虑通过Type.InvokeMember呼叫基地的私人my initial attempt,我有大约20种方法可以打电话。

  • 虽然不太重要,但效率:通过反射进行的后期绑定调用总是比通过v-table直接调用COM接口方法效率低。

2 个答案:

答案 0 :(得分:9)

最后,我相信我已经在@EricBrown的帮助下解决了问题using Marshal.CreateAggregatedObject

以下是使用WebBrowserSite作为示例自定义IOleClientSite OLE接口的代码,调用WebBrowserSite的私有COM可见实现。它可以扩展到其他接口,例如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
        }
    }
}

答案 1 :(得分:4)

只是想一想,但也许您可以使用here中的一些源代码。你会重新实现,但它可能会给你你想要的东西。