Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

getElementById on element within an iframe

My current code works on elements outside of an iframe. How should I approach fetching elements within an iframe using getElementById? My end goal is to write text within the the <body id="tinymce"><p>...</p></body> tags. I am not using a webBrowser control - this is for an external instance of iexplore

HTML Sample

enter image description here

Code Sample

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");
        }
    }
}

- update idea Chance of adapting my idea below?

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 { }
}
like image 229
Patrick Alexson Avatar asked Aug 27 '13 21:08

Patrick Alexson


1 Answers

This answer was inspired by some research I recently did on using eval to inject script into an out-of-proc instance of Internet Explorer.

The idea is to bypass MSHTML DOM interop interfaces and use dynamic JavaScript to obtain a DOM object of interest. There are some implications:

  • execScript method is deprecated in upcoming IE11.
  • Unmanaged IDispatchEx::InvokeEx has to be used to access dynamic JavaScript objects inside HTML window object out-of-proc (in case of in-proc WebBrowser control, standard Type.Invoke, HtmlDocument.InvokeScript or dynamic invocation work just fine with this technique).

To address the question itself, it should be quite easy to get the desired body element, using the approach illustrated below:

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

Here's a sample which executes alert(document.URL) in the context of a child frame of jsfiddle.net, by automating an out-of-proc instance of 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);
    }
}
like image 110
noseratio Avatar answered Sep 28 '22 08:09

noseratio