Question

Here is the code I got using ILSpy:

public static object InvokeScript(this IHTMLDocument2 document, string scriptName, object[] args = null)
{
    object result = null;
    UnsafeNativeMethods.tagDISPPARAMS tagDISPPARAMS = new UnsafeNativeMethods.tagDISPPARAMS();
    tagDISPPARAMS.rgvarg = IntPtr.Zero;
    try
    {
        UnsafeNativeMethods.IDispatch dispatch = ((IHTMLDocument)document).Script as UnsafeNativeMethods.IDispatch;
        if (dispatch != null)
        {
            Guid empty = Guid.Empty;
            string[] rgszNames = new string[]
            {
                scriptName
            };
            int[] array = new int[]
            {
                -1
            };
            int iDsOfNames = dispatch.GetIDsOfNames(ref empty, rgszNames, 1, UnsafeNativeMethods.GetThreadLCID(), array);
            if (UnsafeNativeMethods.Succeeded(iDsOfNames) && array[0] != -1)
            {
                if (args != null)
                {
                    Array.Reverse(args);
                }
                tagDISPPARAMS.rgvarg = ((args == null) ? IntPtr.Zero : ArrayToVARIANTVector(args));
                tagDISPPARAMS.cArgs = ((args == null) ? 0 : args.Length);
                tagDISPPARAMS.rgdispidNamedArgs = IntPtr.Zero;
                tagDISPPARAMS.cNamedArgs = 0;
                object[] array2 = new object[1];
                if (dispatch.Invoke(array[0], ref empty, UnsafeNativeMethods.GetThreadLCID(), 1, tagDISPPARAMS, array2, new UnsafeNativeMethods.tagEXCEPINFO(), null) == 0)
                {
                    result = array2[0];
                }
            }
        }
    }
    catch (Exception ex)
    {
        if (IsSecurityOrCriticalException(ex))
        {
            throw;
        }
    }
    finally
    {
        if (tagDISPPARAMS.rgvarg != IntPtr.Zero)
        {
            FreeVARIANTVector(tagDISPPARAMS.rgvarg, args.Length);
        }
    }
    return result;
}

I also got some of their other methods that are being called from this method. Here is how I call it (note is an extension method):

var doc = Browser.Document.DomDocument as IHTMLDocument2;
doc.InvokeScript("alert", new object[] { "hi" });

But the line int iDsOfNames = dispatch.GetIDsOfNames(ref empty, rgszNames, 1, UnsafeNativeMethods.GetThreadLCID(), array); throws an AccessViolationException. One thing I'm not sure about is the UnsafeNativeMethods.IDispatch dispatch = ((IHTMLDocument)document).Script as UnsafeNativeMethods.IDispatch; line. The actual ILSpy line is UnsafeNativeMethods.IDispatch dispatch = this.NativeHtmlDocument2.GetScript() as UnsafeNativeMethods.IDispatch; but for some reason I don't have the GetScript method.

Any idea what am I doing wrong?

Edit

Here is my IDispatch:

    [Guid("00020400-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), SuppressUnmanagedCodeSecurity]
    [ComImport]
    internal interface IDispatch
    {
        [SecurityCritical]
        void GetTypeInfoCount(out uint pctinfo);
        [SecurityCritical]
        void GetTypeInfo(uint iTInfo, int lcid, out IntPtr info);
        [SecurityCritical]
        void GetIDsOfNames(ref Guid iid, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 2)] string[] names, uint cNames, int lcid, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.I4, SizeParamIndex = 2)] [Out] int[] rgDispId);
        [PreserveSig]
        int GetIDsOfNames([In] ref Guid riid, [MarshalAs(UnmanagedType.LPArray)] [In] string[] rgszNames, [MarshalAs(UnmanagedType.U4)] [In] int cNames, [MarshalAs(UnmanagedType.U4)] [In] int lcid, [MarshalAs(UnmanagedType.LPArray)] [Out] int[] rgDispId);
        [SecurityCritical]
        void Invoke(int dispIdMember, ref Guid riid, int lcid, System.Runtime.InteropServices.ComTypes.INVOKEKIND wFlags, ref System.Runtime.InteropServices.ComTypes.DISPPARAMS pDispParams, IntPtr pvarResult, IntPtr pExcepInfo, IntPtr puArgErr);
        [PreserveSig]
        int Invoke(int dispIdMember, [In] ref Guid riid, [MarshalAs(UnmanagedType.U4)] [In] int lcid, [MarshalAs(UnmanagedType.U4)] [In] int dwFlags, [In] [Out] tagDISPPARAMS pDispParams, [MarshalAs(UnmanagedType.LPArray)] [Out] object[] pVarResult, [In] [Out] tagEXCEPINFO pExcepInfo, [MarshalAs(UnmanagedType.LPArray)] [Out] IntPtr[] pArgErr);
    }
Was it helpful?

Solution

Edit: the dispid parameter in GetIDsOfNames is a caller provided array, thus you cannot use [out] (it means the callee allocate the array on the COM heap).

Hard to tell without seeing your declaration of UnsafeNativeMethods.IDispatch, but calling GetIDsOfNames without pinning the dispid array or adding a ref to the parameter is wrong. If you marshaled the array by passing the address by value the garbage collector could move the array around while the GetIDsOfNames call is still running and the native code would be writing to a wild pointer upon return. If you pass the array as a reference then your code won't compile - you need to add ref to the parameter.

You can use System.Type.InvokeMember method to access global variables or functions via the script object. This method calls IDispatch::GetIDsOfNames and IDispatch::Invoke for you.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top