WebBrowserSite: how to call a private COM interface method in a derived class?(WebBrowserSite:如何在派生类中调用私有 COM 接口方法?)
问题描述
这是挑战.我来自框架的 WebBrowserSite
类.我的派生类的实例 ImprovedWebBrowserSite
通过 WebBrowser.CreateWebBrowserSiteBase
,我在派生版本的 WebBrowser
类- 专门提供一个自定义站点对象.框架的 WebBrowser
实现进一步将其传递给底层的非托管 WebBrowser ActiveX 控件.
Here is the challenge. I'm deriving from the Framework's WebBrowserSite
class. An instance of my derived class, ImprovedWebBrowserSite
, is returned via WebBrowser.CreateWebBrowserSiteBase
, which I override in my derived version of the WebBrowser
class - specifically to provide a custom site object. The Framework's WebBrowser
implementation further passes it to down to the underlying unmanaged WebBrowser ActiveX control.
到目前为止,我已经设法在 ImprovedWebBrowserSite
实现中覆盖了 IDocHostUIHandler
(例如 这个).我现在正在寻找更多的核心 COM 接口,例如 IOleClientSite
,我想将其传递给 WebBrowserSite
.它们都通过 ComImport
向 COM 公开,但由框架的 WebBrowserSite
实现声明为 private
或 internal
/UnsafeNativeMethods
.因此,我不能明确地重新实现 他们在派生类中.我必须定义自己的版本,就像我对 IDocHostUIHandler
所做的那样.
So far, I've managed to override IDocHostUIHandler
in my ImprovedWebBrowserSite
implementation (like this). I'm now looking for more core COM interfaces, like IOleClientSite
, which I want to pass-through to WebBrowserSite
. All of them are exposed to COM with ComImport
, but declared as private
or internal
by the Framework's implementation of WebBrowserSite
/UnsafeNativeMethods
. Thus, I cannot explicitly re-implement them in the derived class. I have to define my own versions, like I did with IDocHostUIHandler
.
那么,问题是,如何从我的派生类中调用 WebBrowserSite
中定义的私有或内部 COM 接口的方法? 例如,我想调用 IOleClientSite.GetContainer
.我可以使用反射(如 this),但这是最后的手段,仅次于重新实现 WebBrowser
从头开始.
So, the question is, how do I call a method of a private or internal COM interface defined in WebBrowserSite
, from my derived class? For example, I want to call IOleClientSite.GetContainer
. I can use reflection (like this), but that would be the last resort, second to re-implementing WebBrowser
from scratch.
我的想法是,因为框架的私有 UnsafeNativeMethods.IOleClientSite
和我自己的 ImprovedWebBrowserSite.IOleClientSite
都是 COM 接口,用 ComImport
属性,相同的 GUID 和相同的方法签名..NET 4.0+ 中有 COM Type Equivalence,所以必须成为一种无需反思的方式.
My thinking is, because the Framework's private UnsafeNativeMethods.IOleClientSite
and my own ImprovedWebBrowserSite.IOleClientSite
are both COM interfaces, declared with the ComImport
attribute, the same GUID and identical method signatures. There's COM Type Equivalence in .NET 4.0+, so there has to be a way to do it without reflection.
[UPDATE] 现在我有了解决方案,我相信它会打开自定义 WinForms 版本WebBrowser
控件的一个>.
[UPDATE] Now that I've got a solution, I believe it opens some new and interesting possibilities in customizing the WinForms version of WebBrowser
control.
这个版本的问题是在我最初尝试以更抽象的形式表述问题之后创建的,被称为误导由评论员.该评论后来被删除,但我决定保留这两个版本.
This version of the question was created after my initial attempt to formulate the problem in a more abstract form was dubbed misleading by a commentator. The comment has been removed later, but I decided to keep both versions.
为什么我不想使用反射来解决这个问题?有几个原因:
依赖于内部或私有方法的实际符号名称,由
WebBrowserSite
的实现者给出,与 COM 接口不同,它是关于二进制 v-table 合约的.
Dependency on the actual symbolic names of the internal or private methods, as given by the implementers of
WebBrowserSite
, unlike with a COM interface, which is about the binary v-table contract.
庞大的反射代码.例如,考虑调用基础的私有 TranslateAccelerator
通过 Type.InvokeMember
,我有大约 20 个这样的方法可以调用.
Bulky reflection code. E.g., consider calling the base's private TranslateAccelerator
via Type.InvokeMember
, and I have ~20 methods like that to call.
虽然不太重要,但效率:通过反射的后期绑定调用总是比通过 v-table 直接调用 COM 接口方法的效率低.
Although less important, efficiency: a late-bound call via reflection is always less efficient than a direct call to a COM interface method via v-table.
推荐答案
最后,我相信我已经解决了问题 使用 Marshal.CreateAggregatedObject
,在@EricBrown 的帮助下.
Finally, I believe I've solved the problem using Marshal.CreateAggregatedObject
, with some help from @EricBrown.
以下代码可以自定义 WebBrowserSite
OLE 接口,以 IOleClientSite
为例,调用 WebBrowserSite
的私有 COM 可见实现.它可以扩展到其他接口,例如IDocHostUIHandler
.
Here's the code that makes possible customizing WebBrowserSite
OLE interfaces, using IOleClientSite
as an example, calling the private COM-visible implementation of WebBrowserSite
. It can be extended to other interfaces, e.g. 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 接口方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:WebBrowserSite:如何在派生类中调用私有 COM 接口方法?
- 是否可以在 .Net 3.5 中进行通用控件? 2022-01-01
- 使用 rss + c# 2022-01-01
- 在 LINQ to SQL 中使用 contains() 2022-01-01
- 为什么 C# 中的堆栈大小正好是 1 MB? 2022-01-01
- Windows 喜欢在 LINUX 中使用 MONO 进行服务开发? 2022-01-01
- C# 通过连接字符串检索正确的 DbConnection 对象 2022-01-01
- 带问号的 nvarchar 列结果 2022-01-01
- 在 C# 中异步处理项目队列 2022-01-01
- CanBeNull和ReSharper-将其用于异步任务? 2022-01-01
- Azure Active Directory 与 MVC,客户端和资源标识同一 2022-01-01