这是挑战。我来自Framework的WebBrowserSite
课程。我的派生类ImprovedWebBrowserSite
的一个实例是通过WebBrowser.CreateWebBrowserSiteBase
返回的,我在WebBrowser
类的派生版本中覆盖了它 - 专门用于提供自定义站点对象。 Framework的WebBrowser
实现进一步将其传递给底层的非托管WebBrowser ActiveX控件。
到目前为止,我已成功覆盖IDocHostUIHandler
实施中的ImprovedWebBrowserSite
(例如this)。我现在正在寻找更多核心COM接口,例如IOleClientSite
,我希望将其传递给WebBrowserSite
。所有这些都通过ComImport
向COM公开,但由框架的private
/ internal
实现声明为WebBrowserSite
或UnsafeNativeMethods
。因此,我不能在派生类中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接口方法效率低。
答案 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中的一些源代码。你会重新实现,但它可能会给你你想要的东西。