[英]WebBrowserSite: how to call a private COM interface method in a derived class?
这是挑战。 我是从Framework的WebBrowserSite
类派生出来的。 我的派生类ImprovedWebBrowserSite
一个实例是通过WebBrowser.CreateWebBrowserSiteBase
返回的,我在WebBrowser
类的派生版本中覆盖了它 - 专门用于提供自定义站点对象。 Framework的WebBrowser
实现进一步将其传递给底层的非托管WebBrowser ActiveX控件。
到目前为止,我已经设法在ImprovedWebBrowserSite
实现中覆盖IDocHostUIHandler
(像这样 )。 我现在正在寻找更多的核心COM接口,比如IOleClientSite
,我想将它传递给WebBrowserSite
。 所有这些都通过ComImport
暴露给COM,但是通过Framework的WebBrowserSite
/ UnsafeNativeMethods
实现声明为private
或internal
。 因此,我无法在派生类中明确地重新实现它们。 我必须定义自己的版本,就像我使用IDocHostUIHandler
。
所以,问题是,如何从我的派生类中调用WebBrowserSite
定义的私有或内部COM接口的方法? 例如,我想调用IOleClientSite.GetContainer
。 我可以使用反射(像这样 ),但这将是最后的手段,其次是从头开始重新实现WebBrowser
。
我的想法是,因为Framework的私有UnsafeNativeMethods.IOleClientSite
和我自己的ImprovedWebBrowserSite.IOleClientSite
都是COM接口,使用ComImport
属性声明,相同的GUID和相同的方法签名。 .NET 4.0+中有COM类型等价 ,因此必须有一种方法可以在没有反射的情况下完成。
[更新]现在我已经有了解决方案 ,我相信它为自定义WebBrowser
控件的WinForms版本打开了一些新的和有趣的可能性。
这个版本的问题是在我最初尝试以更抽象的形式表达问题之后创建的,被评论员称为误导。 评论已被删除,但我决定保留这两个版本。
为什么我不想用反射来解决这个问题? 原因如下:
依赖于内部或私有方法的实际符号名称,由WebBrowserSite
的实现者给出,与COM接口不同,后者是关于二进制v表合同。
庞大的反射代码。 例如,考虑通过Type.InvokeMember
调用base的私有TranslateAccelerator
,我有大约20个这样的方法来调用。
虽然不太重要,但效率:通过反射进行的后期绑定调用总是比通过v-table直接调用COM接口方法效率低。
最后,我相信我已经使用Marshal.CreateAggregatedObject
在@EricBrown的帮助下解决了这个问题。
下面是使用IOleClientSite
作为示例自定义WebBrowserSite
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
}
}
}
只是一个想法,但也许你可以使用这里的一些源代码。 你会重新实现,但它可能会给你你想要的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.