| File: System\Windows\Forms\Controls\WebBrowser\HtmlElement.HtmlElementShim.cs | Web Access |
| Project: src\winforms\src\System.Windows.Forms\System.Windows.Forms.csproj (System.Windows.Forms) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using Windows.Win32.System.Com; using Windows.Win32.Web.MsHtml; namespace System.Windows.Forms; public sealed partial class HtmlElement { /// <summary> /// HtmlElementShim - this is the glue between the DOM eventing mechanisms /// and our CLR callbacks. /// /// HTMLElementEvents2: we create an IConnectionPoint (via ConnectionPointCookie) between us and MSHTML and it calls back /// on our an instance of HTMLElementEvents2. The HTMLElementEvents2 class then fires the event. /// /// </summary> internal unsafe class HtmlElementShim : HtmlShim { private static readonly Type[] s_dispInterfaceTypes = [ typeof(Interop.Mshtml.DHTMLElementEvents2), typeof(Interop.Mshtml.DHTMLAnchorEvents2), typeof(Interop.Mshtml.DHTMLAreaEvents2), typeof(Interop.Mshtml.DHTMLButtonElementEvents2), typeof(Interop.Mshtml.DHTMLControlElementEvents2), typeof(Interop.Mshtml.DHTMLFormElementEvents2), typeof(Interop.Mshtml.DHTMLFrameSiteEvents2), typeof(Interop.Mshtml.DHTMLImgEvents2), typeof(Interop.Mshtml.DHTMLInputFileElementEvents2), typeof(Interop.Mshtml.DHTMLInputImageEvents2), typeof(Interop.Mshtml.DHTMLInputTextElementEvents2), typeof(Interop.Mshtml.DHTMLLabelEvents2), typeof(Interop.Mshtml.DHTMLLinkElementEvents2), typeof(Interop.Mshtml.DHTMLMapEvents2), typeof(Interop.Mshtml.DHTMLMarqueeElementEvents2), typeof(Interop.Mshtml.DHTMLOptionButtonElementEvents2), typeof(Interop.Mshtml.DHTMLSelectElementEvents2), typeof(Interop.Mshtml.DHTMLStyleElementEvents2), typeof(Interop.Mshtml.DHTMLTableEvents2), typeof(Interop.Mshtml.DHTMLTextContainerEvents2), typeof(Interop.Mshtml.DHTMLScriptEvents2) ]; private readonly AgileComPointer<IHTMLWindow2>? _associatedWindow; private AxHost.ConnectionPointCookie? _cookie; // To hook up events from the native HtmlElement private HtmlElement _htmlElement; public HtmlElementShim(HtmlElement element) { _htmlElement = element; // Snap our associated window so we know when to disconnect. HtmlDocument? doc = _htmlElement.Document; if (doc is not null) { HtmlWindow? window = doc.Window; if (window is not null) { _associatedWindow = window.NativeHtmlWindow; } } } public override IHTMLWindow2.Interface? AssociatedWindow => (IHTMLWindow2.Interface?)_associatedWindow?.GetManagedObject(); public IHTMLElement.Interface NativeHtmlElement => (IHTMLElement.Interface)_htmlElement.NativeHtmlElement.GetManagedObject(); internal HtmlElement Element => _htmlElement; /// Support IHTMLElement2.AttachEventHandler public override void AttachEventHandler(string eventName, EventHandler eventHandler) { // IE likes to call back on an IDispatch of DISPID=0 when it has an event, // the HtmlToClrEventProxy helps us fake out the CLR so that we can call back on // our EventHandler properly. HtmlToClrEventProxy proxy = AddEventProxy(eventName, eventHandler); using var htmlElement2 = _htmlElement.GetHtmlElement<IHTMLElement2>(); using BSTR name = new(eventName); using var dispatch = ComHelpers.GetComScope<IDispatch>(proxy); VARIANT_BOOL result; htmlElement2.Value->attachEvent(name, dispatch, &result).ThrowOnFailure(); } public override void ConnectToEvents() { if (_cookie is null || !_cookie.Connected) { for (int i = 0; i < s_dispInterfaceTypes.Length && _cookie is null; i++) { _cookie = new AxHost.ConnectionPointCookie( NativeHtmlElement, new HTMLElementEvents2(_htmlElement), s_dispInterfaceTypes[i], throwException: false); if (!_cookie.Connected) { _cookie = null; } } } } /// Support IHTMLElement2.DetachHandler public override void DetachEventHandler(string eventName, EventHandler eventHandler) { HtmlToClrEventProxy? proxy = RemoveEventProxy(eventHandler); if (proxy is not null) { using var htmlElement2 = _htmlElement.GetHtmlElement<IHTMLElement2>(); using BSTR name = new(eventName); using var dispatch = ComHelpers.GetComScope<IDispatch>(proxy); htmlElement2.Value->detachEvent(name, dispatch).ThrowOnFailure(); } } public override void DisconnectFromEvents() { _cookie?.Disconnect(); _cookie = null; } protected override void Dispose(bool disposing) { base.Dispose(disposing); if (disposing) { _htmlElement?.NativeHtmlElement?.Dispose(); _htmlElement = null!; } } protected override object GetEventSender() => _htmlElement; } }