MSHTML : Calling member of javascript object?
FINALLY !! I got it up and running !
The reason for the
System.InvalidCastException
that was thrown, whenever I tried to reference the parentWindow of an mshtml.IHTMLDocument2 and / or assign it to an mshtml.IHTMLWindow2 window object had to do with Threading.
For some, unknown to me, reason it seems that the COM objects of mshtml.IHTMLWindow are operating on another Thread that must be of Single-Threaded Apartment (STA) state.
So the trick was, calling / executing the required piece of code on another Thread with STA state.
Here's a sample code:
SHDocVw.InternetExplorer IE = new SHDocVw.InternetExplorer
bool _isRunning = false;
private void IE_DocumentComplete(object pDisp, ref obj URL)
{
//Prevent multiple Thread creations because the DocumentComplete event fires for each frame in an HTML-Document
if (_isRunning) { return; }
_isRunning = true;
Thread t = new Thread(new ThreadStart(Do))
t.SetApartmentState(ApartmentState.STA);
t.Start();
}
private void Do()
{
mshtml.IHTMLDocument3 doc = this.IE.Document;
mshtml.IHTMLElement player = doc.getElementById("player");
if (player != null)
{
//Now we're able to call the objects properties, function (members)
object value = player.GetType().InvokeMember("getLastSongPlayed", System.Reflection.BindingFlags.InvokeMethod, null, player, null);
//Do something with the returned value in the "value" object above.
}
}
We're now also able to reference the parentWindow of an mshtml.IHTMLDocument2 object and execute a sites script and / or our own (remember it must be on an STA thread):
mshtml.IHTMLWindow2 window = doc.parentWindow;
window.execScript("AScriptFunctionOrOurOwnScriptCode();", "javascript");
This might save someone from headaches in the future. lol
Calling ActionScript (Flash) method via mshtml from C#
Ok, I've found an issue. Before this execScript. I was doing some modification of DOM and moving elements and apparently this has broken DOM so I couldn't call swfObject because during moving it was somehow modified and lost its properties and methods which were exported via ActionScript.
How to inject Javascript in WebBrowser control?
For some reason Richard's solution didn't work on my end (insertAdjacentText failed with an exception). This however seems to work:
HtmlElement head = webBrowser1.Document.GetElementsByTagName("head")[0];
HtmlElement scriptEl = webBrowser1.Document.CreateElement("script");
IHTMLScriptElement element = (IHTMLScriptElement)scriptEl.DomElement;
element.text = "function sayHello() { alert('hello') }";
head.AppendChild(scriptEl);
webBrowser1.Document.InvokeScript("sayHello");
This answer explains how to get the IHTMLScriptElement
interface into your project.
Rundll32.exe javascript
There's a great explanation of this here: http://thisissecurity.net/2014/08/20/poweliks-command-line-confusion/
To summarize using the same example of:
rundll32.exe javascript:"\..\mshtml,RunHTMLApplication ";alert('foo');
- RunDll32
- Parses the command and decides the intended DLL is:
javascript:"\..\mshtml
- Fails at loading that as an absolute path.
- Fails to find a match in the working directory or on the path.
- Fails to find a manifest
javascript:"\..\mshtml.manifest
for the module. - Calls LoadLibrary
- Parses the command and decides the intended DLL is:
- LoadLibrary
- Adds the extension and attempts to load
javascript:"\..\mshtml.dll
- Treats this as relative, so it goes up from the fake
javascript:"\
directory. - Searches for
mshtml.dll
which it finds in the System directory. - Loads the DLL using RunHTMLApplication as the entry point.
- Adds the extension and attempts to load
- RunHTMLApplication
- Attempts to execute the command
";alert('foo');
- As that's invalid Javascript it calls GetCommandLine for the original command which returns
javascript:"\..\mshtml,RunHTMLApplication ";alert('foo');
- Attempts to open this URI so it asks the system how to handle the javascript protocol which is typically set to Microsoft HTML Javascript Pluggable Protocol in the registry.
- Then executes the Javascript:
"..\mshtml,RunHTMLApplication ";alert('foo');
- Attempts to execute the command
- Javascript
- The first statement creates a string and does nothing with it which is valid enough to not cause an error.
- Continues executing the rest of the script.
Do I absolutely need to call ReleaseComObject on every MSHTML object?
The RCW will release the COM object when the RCW is finalized, so you don't need to create a wrapper that does this. You call ReleaseComObject
because you don't want to wait around for the finalization; this is the same rationale for the Dispose pattern. So creating wrappers that can be Dispose
d isn't a bad idea (and there are examples out there
For var doc = myBrowser.Document.DomDocument ...;
, you should also capture .Document
in a separate variable and ReleaseComObject
it as well. Any time you reference a property of a COM object which produces another object, make sure to release it.
In GetAttribute
, you're casting the element to another interface. In COM programming, that adds another reference. You'll need to do something like var htmlElement = (IHTMLElement) element;
so you can release that as well.
Edit - this is the pattern to use when working with COM objects:
IHTMLElement element = null;
try
{
element = <some method or property returning a COM object>;
// do something with element
}
catch (Exception ex) // although the exception type should be as specific as possible
{
// log, whatever
throw; // not "throw ex;" - that makes the call stack think the exception originated right here
}
finally
{
if (element != null)
{
Marshal.ReleaseComObject(element);
element = null;
}
}
This should really be done for every COM object reference you have.
Running a JavaScript function in an instance of Internet Explorer
The problem had to do with threading - I've wasted so much time with STA issues you'd think I'd learn by now :).
Anyhow I found a way to get the second bit of code I posted working and running javascript functions in the IE window! Here is the code:
this.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)(() =>
{
mshtml.HTMLDocument doc = ie.Document;
mshtml.IHTMLWindow2 win = doc.parentWindow as IHTMLWindow2;
win.execScript("init();", "javascript");
}));
Hope it helps someone!
Related Topics
Convert Am/Pm Time to 24 Hours Format
Entity Framework Filter "Expression<Func<T, Bool>>"
Garbage Collection When Using Anonymous Delegates for Event Handling
C# Array.Findallindexof Which Findall Indexof
Autoresize Textbox Control Vertically
Can the Oracle Managed Driver Use Async/Await Properly
Using SQLdataadapter to Insert a Row
Xml Validation Using Xsd Schema
How to Get a Random Number from a Range, Excluding Some Values
Ienumerable Doesn't Have a Count Method
C# Instantiate Generic List from Reflected Type
Is There a C# Generic Constraint for "Real Number" Types
How to Retrieve Disk Information in C#
Converting String Format to Datetime in Mm/Dd/Yyyy