c# - iframe 中元素的 getElementById

标签 c# internet-explorer mshtml

我当前的代码适用于 iframe 之外的元素。我应该如何使用 getElementById 获取 iframe 中的元素?我的最终目标是在 <body id="tinymce"><p>...</p></body> 中编写文本标签。我没有使用 webBrowser 控件 - 这是 iexplore 的外部实例

HTML 示例

enter image description here

代码示例

foreach (InternetExplorer ie in new ShellWindowsClass())
{
    if (ie.LocationURL.ToString().IndexOf("intranet_site_url") != -1)
    {
        IWebBrowserApp wb = (IWebBrowserApp)ie;
        while (wb.Busy) { Thread.Sleep(100); }
        HTMLDocument document = ((HTMLDocument)wb.Document);

        // FETCH BY ID
        IHTMLElement element;
        HTMLInputElementClass hitem;

        element = document.getElementById("tinymce");
        hitem = (HTMLInputElementClass)element;
        hitem.value = first_name;

        // FETCH BY ID in IFRAME
        IHTMLFramesCollection2 hframes = document.frames;
        for (int i = 0; i < hframes.length; i++)
        {
            object ref_index = i;
            IHTMLWindow2 currentFrame = (IHTMLWindow2)hframes.item(ref ref_index);

            if (currentFrame != null)
            {
                MessageBox.Show(currentFrame.name);
                // what to do from here?
            }
            else
                MessageBox.Show("Null");
        }
    }
}

-更新想法 有机会在下面采用我的想法吗?

if (currentFrame != null)
{
    MessageBox.Show(currentFrame.name);

    HTMLDocument document_sub = ((HTMLDocument)currentFrame.document);
    IHTMLElement element_sub;
    HTMLInputElementClass hitem_sub;

    element_sub = (document_sub.getElementById("tinymce"));
    hitem_sub = (HTMLInputElementClass)element_sub;
    try
    {
        hitem_sub.value = first_name;

        // the above will produce...
        // InvalidCastException: Unable to cast COM object of type 'mshtml.HTMLBodyCLass' to class type 'mshtml.HTMLInputElementClass'
    }
    catch { }
}

最佳答案

这个答案的灵感来自 some research我最近使用了 eval将脚本注入(inject) Internet Explorer 的进程外实例。

想法是绕过 MSHTML DOM 互操作接口(interface)并使用动态 JavaScript 获取感兴趣的 DOM 对象。有一些含义:

要解决问题本身,使用下面说明的方法应该很容易获得所需的 body 元素:

var tinymceBody = DispExInvoker.Invoke(ie.Document.parentWindow, "eval", "document.getElementById('decrpt_ifr').contentWindow.document.getElementById('tinymce')");

这是一个在 jsfiddle.net 的子框架上下文中执行 alert(document.URL) 的示例,通过自动化进程外实例InternetExplorer.Application:

private async void Form1_Load(object sender, EventArgs ev)
{
    SHDocVw.InternetExplorer ie = new SHDocVw.InternetExplorer();
    ie.Visible = true;

    var documentCompleteTcs = new TaskCompletionSource<bool>();
    ie.DocumentComplete += delegate
    {
        if (documentCompleteTcs.Task.IsCompleted)
            return;
        documentCompleteTcs.SetResult(true);
    };

    ie.Navigate("http://jsfiddle.net/");
    await documentCompleteTcs.Task;

    // inject __execScript code into the top window
    var execScriptCode = "(window.__execScript = function(exp) { return eval(exp); }, window.self)";
    var window = DispExInvoker.Invoke(ie.Document.parentWindow, "eval", execScriptCode);

    // inject __execScript into a child iframe
    var iframe = DispExInvoker.Invoke(window, "__execScript", 
        String.Format("document.all.tags('iframe')[0].contentWindow.eval('{0}')",  execScriptCode));

    // invoke 'alert(document.URL)' in the context of the child frame
    DispExInvoker.Invoke(iframe, "__execScript", "alert(document.URL)");
}

/// <summary>
/// Managed wrapper for calling IDispatchEx::Invoke
/// https://stackoverflow.com/a/18349546/1768303
/// </summary>
public class DispExInvoker
{
    // check is the object supports IsDispatchEx
    public static bool IsDispatchEx(object target)
    {
        return target is IDispatchEx;
    }

    // invoke a method on the target IDispatchEx object
    public static object Invoke(object target, string method, params object[] args)
    {
        var dispEx = target as IDispatchEx;
        if (dispEx == null)
            throw new InvalidComObjectException();

        var dp = new System.Runtime.InteropServices.ComTypes.DISPPARAMS();
        try
        {
            // repack arguments
            if (args.Length > 0)
            {
                // should be using stackalloc for DISPPARAMS arguments, but don't want enforce "/unsafe"
                int size = SIZE_OF_VARIANT * args.Length;
                dp.rgvarg = Marshal.AllocCoTaskMem(size);
                ZeroMemory(dp.rgvarg, size); // zero'ing is equal to VariantInit
                dp.cArgs = args.Length;
                for (var i = 0; i < dp.cArgs; i++)
                    Marshal.GetNativeVariantForObject(args[i], dp.rgvarg + SIZE_OF_VARIANT * (args.Length - i - 1));
            }

            int dispid;
            dispEx.GetDispID(method, fdexNameCaseSensitive, out dispid);

            var ei = new System.Runtime.InteropServices.ComTypes.EXCEPINFO();
            var result = Type.Missing;
            dispEx.InvokeEx(dispid, 0, DISPATCH_METHOD, ref dp, ref result, ref ei, null);
            return result;
        }
        finally
        {
            if (dp.rgvarg != IntPtr.Zero)
            {
                for (var i = 0; i < dp.cArgs; i++)
                    VariantClear(dp.rgvarg + SIZE_OF_VARIANT * i);
                Marshal.FreeCoTaskMem(dp.rgvarg);
            }
        }
    }

    // interop declarations

    [DllImport("oleaut32.dll", PreserveSig = false)]
    static extern void VariantClear(IntPtr pvarg);
    [DllImport("Kernel32.dll", EntryPoint = "RtlZeroMemory", SetLastError = false)]
    static extern void ZeroMemory(IntPtr dest, int size);

    const uint fdexNameCaseSensitive = 0x00000001;
    const ushort DISPATCH_METHOD = 1;
    const int SIZE_OF_VARIANT = 16;

    // IDispatchEx interface

    [ComImport()]
    [Guid("A6EF9860-C720-11D0-9337-00A0C90DCAA9")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface IDispatchEx
    {
        // IDispatch
        int GetTypeInfoCount();
        [return: MarshalAs(UnmanagedType.Interface)]
        System.Runtime.InteropServices.ComTypes.ITypeInfo GetTypeInfo([In, MarshalAs(UnmanagedType.U4)] int iTInfo, [In, MarshalAs(UnmanagedType.U4)] int lcid);
        void GetIDsOfNames([In] ref Guid riid, [In, MarshalAs(UnmanagedType.LPArray)] string[] rgszNames, [In, MarshalAs(UnmanagedType.U4)] int cNames, [In, MarshalAs(UnmanagedType.U4)] int lcid, [Out, MarshalAs(UnmanagedType.LPArray)] int[] rgDispId);
        void Invoke(int dispIdMember, ref Guid riid, uint lcid, ushort wFlags, ref System.Runtime.InteropServices.ComTypes.DISPPARAMS pDispParams, out object pVarResult, ref System.Runtime.InteropServices.ComTypes.EXCEPINFO pExcepInfo, IntPtr[] pArgErr);

        // IDispatchEx
        void GetDispID([MarshalAs(UnmanagedType.BStr)] string bstrName, uint grfdex, [Out] out int pid);
        void InvokeEx(int id, uint lcid, ushort wFlags,
            [In] ref System.Runtime.InteropServices.ComTypes.DISPPARAMS pdp,
            [In, Out] ref object pvarRes,
            [In, Out] ref System.Runtime.InteropServices.ComTypes.EXCEPINFO pei,
            System.IServiceProvider pspCaller);
        void DeleteMemberByName([MarshalAs(UnmanagedType.BStr)] string bstrName, uint grfdex);
        void DeleteMemberByDispID(int id);
        void GetMemberProperties(int id, uint grfdexFetch, [Out] out uint pgrfdex);
        void GetMemberName(int id, [Out, MarshalAs(UnmanagedType.BStr)] out string pbstrName);
        [PreserveSig]
        [return: MarshalAs(UnmanagedType.I4)]
        int GetNextDispID(uint grfdex, int id, [In, Out] ref int pid);
        void GetNameSpaceParent([Out, MarshalAs(UnmanagedType.IUnknown)] out object ppunk);
    }
}

关于c# - iframe 中元素的 getElementById,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18476186/

相关文章:

c++ - 如何在 MSHTML 中禁用 VML

c# - 在 SQL 中实现 C# 迁移代码

c# - LinqKit System.InvalidCastException 在成员属性上调用方法提供的表达式时

delphi - 是否有另一种不使用 Application.ProcessMessages 加载 MSHTML 文档的方法?

c# - ASP.NET CORE 文件上传问题

css - IE7 中组合框的白色边框

html - vb.net 获取 HTML 元素的样式

c# - 派生DataGridView绘图问题。显示黑色区域

c# - 在 List<Dictionary<string,string>> 中选择最小键值对,其中字典值是日期字符串

html - <a href =""> 在 IE 中不起作用,但在 chrome 中起作用