0

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);
    }
Juan
  • 15,274
  • 23
  • 105
  • 187
  • Have you checked "Allow unsafe code" in your project settings? Not sure if it is needed by this code since there isn't actually any unsafe code in your assembly even though it's being called. Also, why did you extract this code to your own assembly at all? – Karl-Johan Sjögren Aug 12 '12 at 17:07
  • @Karl-JohanSjögren: I already have allow unsafe code checked. The reason why I need this is because [this method](http://stackoverflow.com/a/11704061/1219414) will give you an `IHtmlDocument2` but not an `HtmlDocument`. – Juan Aug 12 '12 at 17:48
  • Keep in mind, the assembly you got this from (System.Windows.Forms.dll) has specific privileges in a system. An assembly you create, might not. If you run this code in a medium-trust environment, you won't have permission to run unsafe code. – Peter Ritchie Aug 12 '12 at 17:50
  • @PeterRitchie I see. Seems like that's the problem. Is there any way around this? (I just tried running it as an administrator but got the same problem, would this mean this is another problem?) – Juan Aug 12 '12 at 17:52
  • It's new in .NET 4.0. See http://msdn.microsoft.com/en-us/magazine/ee677170.aspx and http://msdn.microsoft.com/en-us/library/dd233103.aspx – Peter Ritchie Aug 12 '12 at 18:02

1 Answers1

1

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.

Sheng Jiang 蒋晟
  • 15,125
  • 2
  • 28
  • 46