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();
}
公共客户查询接口