iframe中的元素上的getElementById

时间:2013-08-27 21:59:37

标签: 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 { }
}

4 个答案:

答案 0 :(得分:4)

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

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

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

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

这是一个在alert(document.URL)子框架的上下文中执行jsfiddle.net的示例,它通过自动执行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);
    }
}

答案 1 :(得分:2)

试试这个:

Windows.Forms.HtmlWindow frame = WebBrowser1.Document.GetElementById("decrpt_ifr").Document.Window.Frames["decrpt_ifr"];
HtmlElement body = frame.Document.GetElementById("tinymce");
body.InnerHtml = "Hello, World!";

获取框架并将其视为不同的文档(因为它是)然后它尝试从其id获取元素。祝你好运。

编辑:这应该可以利用dynamic数据类型和InternetExplorer接口:

private void Form1_Load(object sender, EventArgs e)
{
    foreach (InternetExplorer ie in new ShellWindows())
    {
        if (ie.LocationURL.ToString().IndexOf("tinymce") != -1)
        {
            IWebBrowserApp wb = (IWebBrowserApp)ie;
            wb.Document.Frames.Item[0].document.body.InnerHtml = "<p>Hello, World at </p> " + DateTime.Now.ToString();
        }
    }
}

答案 2 :(得分:1)

答案 3 :(得分:0)

另一种方法是获取该iframe的网址并将其加载到您的浏览器中。

对我来说,已接受的解决方案是"Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))"错误。