Found out a workaround:
I had to implement the additional DWebBrowserEvents2
interface as described in this Code Project article to get additional events.
Then, I get notified in the BeforeNavigate2
event with more information.
I discovered that pDisp
is an IWebBrowser2
interface and that the Parent
property of that interface is NULL
for top windows and non-NULL
for (I)FRAMES.
This was sufficient to me so I could then write:
public class BrowserExtendedNavigatingEventArgs : CancelEventArgs
{
// ...
public bool IsInsideFrame
{
get
{
var wb = AutomationObject as UnsafeNativeMethods.IWebBrowser2;
return wb != null && wb.Parent != null;
}
}
// ...
}
Please note that you have to ensure that you call the IsInsideFrame
property from the thread that created the web browser control, otherwise you might get access violation exceptions, just as described here:
"...All IE COM objects, including the webbrowser control, are Single
Threaded Apartment objects. Which means, among other things, you can
only access them from the thread on which they were created. If you
need to use them from other threads, you need to marshall them using
the GIT or CoMarshallInterThreadInterfaceInStream() and related
functions..."
I hope this might help others, too.
Alternative solution
Since I got AccessViolationException
s when accessing the wb.Parent
, although I think I did everything correctly, I was looking for an alternative solution.
I found it in this Code Project article.
Basically the article uses an alternative way to determine the frame. It does it by comparing the references to the web browser control.
Pseudo code:
public void BeforeNavigate2(
object pDisp,
ref object url,
ref object flags,
ref object targetFrameName,
ref object postData,
ref object headers,
ref bool cancel)
{
// ...
var isTopFrame = _browser._axIWebBrowser2.Equals(pDisp);
// ...
}
My tests run successfully so far, I really do hope that I get no more of these frightening access violation exceptions.