问题
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);
}
回答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.
来源:https://stackoverflow.com/questions/11924016/decompiled-htmldocuments-invokescript-not-working