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

C# WebBrowserSite:如何在派生类中调用私有COM接口方法?,c#,.net,com,webbrowser-control,com-interop,C#,.net,Com,Webbrowser Control,Com Interop,这就是挑战。我来自于框架的类。我的派生类的一个实例,ImprovedWebBrowserSite,通过返回,我在该类的派生版本中重写了该实例,特别是为了提供一个自定义站点对象。框架的WebBrowser实现进一步将其传递给底层非托管WebBrowser ActiveX控件 到目前为止,我已经在我的ImprovedWebBrowserSite实现中成功地覆盖了IDocHostUIHandler。我现在正在寻找更多的核心COM接口,如IOleClientSite,我希望将其传递到WebBrowser

这就是挑战。我来自于框架的类。我的派生类的一个实例,
ImprovedWebBrowserSite
,通过返回,我在该类的派生版本中重写了该实例,特别是为了提供一个自定义站点对象。框架的
WebBrowser
实现进一步将其传递给底层非托管WebBrowser ActiveX控件

到目前为止,我已经在我的
ImprovedWebBrowserSite
实现中成功地覆盖了
IDocHostUIHandler
。我现在正在寻找更多的核心COM接口,如
IOleClientSite
,我希望将其传递到
WebBrowserSite
。它们都是通过
ComImport
向COM公开的,但框架实现的
WebBrowserSite
/
非安全性方法将它们声明为
private
internal
。因此,我不能在派生类中使用它们。我必须定义自己的版本,就像我对
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
        }
    }
}
因此,问题是,如何从派生类调用
WebBrowserSite
中定义的私有或内部COM接口的方法?
例如,我想调用
IOleClientSite.GetContainer
。我可以使用反射(比如),但那将是最后的手段,其次是从头开始重新实现
WebBrowser

我的想法是,因为框架的私有
unsafentiveMethods.IOleClientSite
和我自己的
ImprovedWebBrowserSite.IOleClientSite
都是COM接口,使用
ComImport
属性声明,具有相同的GUID和相同的方法签名。在.NET4.0+中存在一些缺陷,因此必须有一种不需要反射的方法

[UPDATE]现在我有了一个新的解决方案,我相信它为自定义
WebBrowser
控件打开了一些新的、有趣的可能性

该问题的这个版本是在一位评论员将问题描述成更抽象的形式后产生的,被称为误导。该评论后来被删除,但我决定保留这两个版本。

为什么我不想使用反射来解决此问题?原因如下:

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

  • 庞大的反射代码。例如,考虑通过<代码>类型调用基础私有。
  • 效率虽然不那么重要:但通过反射进行的后期绑定调用的效率总是低于通过v-table直接调用COM接口方法的效率


    • 只是一个想法,但也许您可以使用来自的一些源代码。你可能会重新实现,但它可能会给你你想要的。

      最后,我相信在@EricBrown的帮助下,我已经解决了这个问题

      下面的代码可以定制
      WebBrowserSite
      OLE接口,例如使用
      IOleClientSite
      调用
      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
              }
          }
      }
      
      使用系统;
      使用系统诊断;
      使用System.Linq;
      使用System.Runtime.InteropServices;
      使用System.Windows.Forms;
      命名空间自定义浏览器
      {
      公共部分类主窗体:窗体
      {
      公共表格(
      {
      初始化组件();
      }
      私有void主窗体加载(对象发送方、事件参数e)
      {
      var wb=新改进的WebBrowser();
      wb.Dock=DockStyle.Fill;
      this.Controls.Add(wb);
      wb.Visible=true;
      wb.DocumentText=“来自ImprovedWebBrowser的您好!”;
      }
      }
      //改进的WebBrowser,具有自定义的通过IOleClientSite的功能
      公共类改进型WebBrowser:WebBrowser
      {
      //提供自定义WebBrowserSite,
      //在这里,我们覆盖IOleClientSite并调用基本实现
      受保护的重写WebBrowserSiteBase CreateWebBrowserSiteBase()
      {
      返回新的改进WebBrowserSite(本);
      }
      //IOleClientSite
      [ComImport(),Guid(“00000118-0000-0000-C000-0000000000 46”)]
      [接口类型(ComInterfaceType.InterfaceSiunknown)]
      公共接口IOleClientSite
      {
      void SaveObject();
      [返回:MarshalAs(UnmanagedType.Interface)]
      对象GetMoniker(
      [In,marshallas(UnmanagedType.U4)]int-dwAssign,
      [In,marshallas(UnmanagedType.U4)]int-dwWhichMoniker);
      [信号]
      int GetContainer([Out]Out IntPtr ppContainer);
      void ShowObject();
      void OnShowWindow([In,marshallas(UnmanagedType.I4)]int fShow);
      void RequestNewObjectLayout();
      }
      //改进的网状硅钙石
      受保护级别改进的WebBrowserSite:
      韦伯氏体,
      IOleClientSite,
      ICustomQueryInterface,
      可识别
      {
      IOleClientSite_baseIOleClientSite;
      IntPtr_unkOuter;
      IntPtr _;
      内(内);;
      #区域内部
      //内部作为聚合对象
      类内部:
      ICustomQueryInterface,
      可识别
      {
      物体外;
      []_型接口;
      公共内部对象(外部对象)
      {
      _外部=外部;
      //基地的专用COM接口在这里
      _接口=_outer.GetType().BaseType.GetInterfaces();
      }
      公共客户查询接口