Blocking dialogs in .NET WebBrowser control - c#

I have a .NET 2.0 WebBrowser control used to navigate some pages with no user interaction (don't ask...long story). Because of the user-less nature of this application, I have set the WebBrowser control's ScriptErrorsSuppressed property to true, which the documentation included with VS 2005 states will [...]"hide all its dialog boxes that originate from the underlying ActiveX control, not just script errors." The MSDN article doesn't mention this, however.
I have managed to cancel the NewWindow event, which prevents popups, so that's taken care of.
Anyone have any experience using one of these and successfully blocking all dialogs, script errors, etc?
EDIT
This isn't a standalone instance of IE, but an instance of a WebBrowser control living on a Windows Form application. Anyone have any experience with this control, or the underlying one, AxSHDocVW?
EDIT again
Sorry I forgot to mention this... I'm trying to block a JavaScript alert(), with just an OK button. Maybe I can cast into an IHTMLDocument2 object and access the scripts that way, I've used MSHTML a little bit, anyone know?

And for an easy way to inject that magic line of javascript, read how to inject javascript into webbrowser control.
Or just use this complete code:
private void InjectAlertBlocker() {
HtmlElement head = webBrowser1.Document.GetElementsByTagName("head")[0];
HtmlElement scriptEl = webBrowser1.Document.CreateElement("script");
string alertBlocker = "window.alert = function () { }";
scriptEl.SetAttribute("text", alertBlocker);
head.AppendChild(scriptEl);
}

This is most definitely hacky, but if you do any work with the WebBrowser control, you'll find yourself doing a lot of hacky stuff.
This is the easiest way that I know of to do this. You need to inject JavaScript to override the alert function... something along the lines of injecting this JavaScript function:
window.alert = function () { }
There are many ways to do this, but it is very possible to do. One possibility is to hook an implementation of the DWebBrowserEvents2 interface. Once this is done, you can then plug into the NavigateComplete, the DownloadComplete, or the DocumentComplete (or, as we do, some variation thereof) and then call an InjectJavaScript method that you've implemented that performs this overriding of the window.alert method.
Like I said, hacky, but it works :)
I can go into more details if I need to.

Bulletproof alert blocker:
Browser.Navigated +=
new WebBrowserNavigatedEventHandler(
(object sender, WebBrowserNavigatedEventArgs args) => {
Action<HtmlDocument> blockAlerts = (HtmlDocument d) => {
HtmlElement h = d.GetElementsByTagName("head")[0];
HtmlElement s = d.CreateElement("script");
IHTMLScriptElement e = (IHTMLScriptElement)s.DomElement;
e.text = "window.alert=function(){};";
h.AppendChild(s);
};
WebBrowser b = sender as WebBrowser;
blockAlerts(b.Document);
for (int i = 0; i < b.Document.Window.Frames.Count; i++)
try { blockAlerts(b.Document.Window.Frames[i].Document); }
catch (Exception) { };
}
);
This sample assumes you have Microsoft.mshtml reference added, "using mshtml;" in your namespaces and Browser is your WebBrowser instance.
Why is it bulletproof? First, it handles scripts inside frames. Then, it doesn't crash when a special "killer frame" exists in document. A "killer frame" is a frame which raises an exception on attempt to use it as HtmlWindow object. Any "foreach" used on Document.Window.Frames would cause an exception, so safer "for" loop must be used with try / catch block.
Maybe it's not the most readable piece of code, but it works with real life, ill-formed pages.

You may have to customize some things, take a look at IDocHostUIHandler, and then check out some of the other related interfaces. You can have a fair amount of control, even to the point of customizing dialog display/ui (I can't recall which interface does this). I'm pretty sure you can do what you want, but it does require mucking around in the internals of MSHTML and being able to implement the various COM interfaces.
Some other ideas:
http://msdn.microsoft.com/en-us/library/aa770041.aspx
IHostDialogHelper
IDocHostShowUI
These may be the things you're looking at implementing.

webBrowser1.ScriptErrorsSuppressed = true;
Just add that to your entry level function. After alot of research is when I came across this method, and touch wood till now its worked. Cheers!!

window.showModelessDialog and window.showModalDialog can be blocked by implementing INewWindowManager interface, additionally code below show how to block alert dialogs by implementing IDocHostShowUI
public class MyBrowser : WebBrowser
{
[PermissionSetAttribute(SecurityAction.LinkDemand, Name = "FullTrust")]
public MyBrowser()
{
}
protected override WebBrowserSiteBase CreateWebBrowserSiteBase()
{
var manager = new NewWindowManagerWebBrowserSite(this);
return manager;
}
protected class NewWindowManagerWebBrowserSite : WebBrowserSite, IServiceProvider, IDocHostShowUI
{
private readonly NewWindowManager _manager;
public NewWindowManagerWebBrowserSite(WebBrowser host)
: base(host)
{
_manager = new NewWindowManager();
}
public int ShowMessage(IntPtr hwnd, string lpstrText, string lpstrCaption, int dwType, string lpstrHelpFile, int dwHelpContext, out int lpResult)
{
lpResult = 0;
return Constants.S_OK; // S_OK Host displayed its UI. MSHTML does not display its message box.
}
// Only files of types .chm and .htm are supported as help files.
public int ShowHelp(IntPtr hwnd, string pszHelpFile, uint uCommand, uint dwData, POINT ptMouse, object pDispatchObjectHit)
{
return Constants.S_OK; // S_OK Host displayed its UI. MSHTML does not display its message box.
}
#region Implementation of IServiceProvider
public int QueryService(ref Guid guidService, ref Guid riid, out IntPtr ppvObject)
{
if ((guidService == Constants.IID_INewWindowManager && riid == Constants.IID_INewWindowManager))
{
ppvObject = Marshal.GetComInterfaceForObject(_manager, typeof(INewWindowManager));
if (ppvObject != IntPtr.Zero)
{
return Constants.S_OK;
}
}
ppvObject = IntPtr.Zero;
return Constants.E_NOINTERFACE;
}
#endregion
}
}
[ComVisible(true)]
[Guid("01AFBFE2-CA97-4F72-A0BF-E157038E4118")]
public class NewWindowManager : INewWindowManager
{
public int EvaluateNewWindow(string pszUrl, string pszName,
string pszUrlContext, string pszFeatures, bool fReplace, uint dwFlags, uint dwUserActionTime)
{
// use E_FAIL to be the same as CoInternetSetFeatureEnabled with FEATURE_WEBOC_POPUPMANAGEMENT
//int hr = MyBrowser.Constants.E_FAIL;
int hr = MyBrowser.Constants.S_FALSE; //Block
//int hr = MyBrowser.Constants.S_OK; //Allow all
return hr;
}
}

The InjectAlertBlocker is absolutely correct
code is
private void InjectAlertBlocker() {
HtmlElement head = webBrowser1.Document.GetElementsByTagName("head")[0];
HtmlElement scriptEl = webBrowser1.Document.CreateElement("script");
IHTMLScriptElement element = (IHTMLScriptElement)scriptEl.DomElement;
string alertBlocker = "window.alert = function () { }";
element.text = alertBlocker;
head.AppendChild(scriptEl);
}
References needed to be added is
Add a reference to MSHTML, which will probalby be called "Microsoft HTML Object Library" under COM references.
Add using mshtml; to your namespaces.
Get a reference to your script element's IHTMLElement:
Then you can use the Navigated event of webbrowser as:
private void InjectAlertBlocker()
{
HtmlElement head = webBrowser1.Document.GetElementsByTagName("head")[0];
HtmlElement scriptEl = webBrowser1.Document.CreateElement("script");
IHTMLScriptElement element = (IHTMLScriptElement)scriptEl.DomElement;
string alertBlocker = "window.alert = function () { }";
element.text = alertBlocker;
head.AppendChild(scriptEl);
}
private void webDest_Navigated(object sender, WebBrowserNavigatedEventArgs e)
{
InjectAlertBlocker();
}

Are you trying to implement a web robot? I have little experience in using the hosted IE control but I did completed a few Win32 projects tried to use the IE control. Disabling the popups should be done via the event handlers of the control as you already did, but I found that you also need to change the 'Disable script debugging xxxx' in the IE options (or you could modify the registry in your codes) as cjheath already pointed out. However I also found that extra steps needed to be done on checking the navigating url for any downloadable contents to prevent those open/save dialogs. But I do not know how to deal with streaming files since I cannot skip them by looking at the urls alone and in the end I turned to the Indy library saving me all the troubles in dealing with IE. Finally, I remember Microsoft did mention something online that IE is not designed to be used as an OLE control. According to my own experience, every time the control navigates to a new page did introduce memory leaks for the programs!

I had bigger problems with this: loading a webpage that is meant for printing and it displays annoying Print dialog. The InjectBlocker was the only way that worked, but fairly unreliable. Under certain conditions (I am considering it's due that WebBrowser control uses IE engine and this depends on installed IE version) the print dialog still appears. This is a major problem, the solution works on Win7 with IE9 installed, but WinXP with IE8 displays the dialog, no matter what.
I believe the solution is in modifying source code and removing the print javascript, before control renders the page. However I tried that with: DocumentText property of the webbrowser control and it is not working. The property is not read only, but it has no effect, when I modify the source.
The solution I found for my problem is the Exec script:
string alertBlocker = "window.print = function emptyMethod() { }; window.alert = function emptyMethod() { }; window.open = function emptyMethod() { };";
this.Document.InvokeScript("execScript", new Object[] { alertBlocker, "JavaScript" });

I managed to inject the code above by creating an extended WebBroswer class and overriding the OnNavigated method.
This seemed to work quite well:
class WebBrowserEx : WebBrowser
{
public WebBrowserEx ()
{
}
protected override void OnNavigated( WebBrowserNavigatedEventArgs e )
{
HtmlElement he = this.Document.GetElementsByTagName( "head" )[0];
HtmlElement se = this.Document.CreateElement( "script" );
mshtml.IHTMLScriptElement element = (mshtml.IHTMLScriptElement)se.DomElement;
string alertBlocker = "window.alert = function () { }";
element.text = alertBlocker;
he.AppendChild( se );
base.OnNavigated( e );
}
}

Simply from the browser control properties: scriptErrorSupressed=true

The easiest way to do this is :
In the : Webbrowser Control you have the procedure ( standard ) BeforeScriptExecute
( The parameter for BeforeScriptExecute is pdispwindow )
Add this :
pdispwindow.execscript("window.alert = function () { }")
In this way before any script execution on the page window alert will be suppressed by injected code.

Related

WPF WebBrowser scroll on reload

I have a System.Windows.Controls.WebBrowser. It has some html that is coming from another document that the user is editing. When the html changes, what I want to do is update the WebBrowser's html, then scroll the WebBrowser back to wherever it was. I am successfully cacheing the scroll offset (see How to retrieve the scrollbar position of the webbrowser control in .NET). But I can't get a callback when the load is complete. Here is what I have tried:
// constructor
public HTMLReferenceEditor()
{
InitializeComponent();
WebBrowser browser = this.EditorBrowser;
browser.LoadCompleted += Browser_LoadCompleted;
//browser.Loaded += Browser_Loaded; // commented out as it doesn't fire when the html changes . . .
}
private void Browser_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
CommonDebug.LogLine("LoadCompleted");
this.ScrollWebBrowser();
}
private void ScrollWebBrowser()
{
WebBrowser browser = this.EditorBrowser;
ReferenceHierarchicalViewModel rhvm = this.GetReferenceHierarchichalViewModel();
int? y = rhvm.LastKnownScrollTop; // this is the cached offset.
browser?.ScrollToY(y);
}
The LoadCompleted callbacks are firing all right. But the scrolling is not happening. I suspect the callbacks are coming too soon. But it is also possible that my scroll method is wrong:
public static void ScrollToY(this WebBrowser browser, int? yQ)
{
if (yQ.HasValue)
{
object doc = browser?.Document;
HTMLDocument castDoc = doc as HTMLDocument;
IHTMLWindow2 window = castDoc?.parentWindow;
int y = yQ.Value;
window?.scrollTo(0, y);
CommonDebug.LogLine("scrolling", window, y);
// above is custom log method; prints out something like "scrolling HTMLWindow2Class3 54", which
// at least proves that nothing is null.
}
}
How can I get the browser to scroll? Incidentally, I don't see some of the callback methods others have mentioned, e.g. DocumentCompleted mentioned here does not exist for me. Detect WebBrowser complete page loading. In other words, for some reason I don't understand, my WebBrowser is different from theirs. For me, the methods don't exist.

Getting actions from WebBrowser control in C#

Is it possible to do something like this - for example, I have a form with a WebBrowser control and local HTML page that has some elements in it. When user clicks on a button in a web page, form does something (for example, close application). Is there any way to connect that DOM actions with form events and how?
Yes, you can use the WebBrowser.ObjectForScripting property to set an object that will be exposed to JavaScript as window.external. Then from within your JavaScript you can call methods on that object. If you need to first inject some JavaScript into the page to hook that stuff up in an HTML page you didn't write, look into the WebBrowser.DocumentCompleted event, where you can inject JavaScript into the WebBrowser.Document like so:
private void webBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
try
{
mainDoc = webBrowser.Document;
if (mainDoc != null)
{
HtmlElementCollection heads = mainDoc.GetElementsByTagName("head");
HtmlElement scriptEl = mainDoc.CreateElement("script");
IHTMLScriptElement el = (IHTMLScriptElement)scriptEl.DomElement;
el.text = "alert('hello world')";
heads[0].AppendChild(scriptEl);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
}
}
Edit: I neglected to mention that IHTMLScriptElement comes from COM and you'll need this code somewhere:
[ComImport, ComVisible(true), Guid(#"3050f28b-98b5-11cf-bb82-00aa00bdce0b")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
[TypeLibType(TypeLibTypeFlags.FDispatchable)]
public interface IHTMLScriptElement
{
[DispId(1006)]
string text { set; [return: MarshalAs(UnmanagedType.BStr)] get; }
}

Implement browser in windows form using awesomium

I"m using Awesomium to try and implement webpages inside my windows form application.
I've used the the awesomium .NET samples but I just don't get the my tab with my homeurl.
When I run my project the status bar is floating inside my form and nothing else happens.
Anyone know where I can get a tut on how to do this or know what could be the problem?
public Laboratory() {
WebCoreConfig webConfig = new WebCoreConfig() {
SaveCacheAndCookies = true,
HomeURL = "http://www.google.com",
LogLevel = LogLevel.Verbose
};
// Using our executable as a child rendering process, is not
// available when debugging in VS.
if (!Process.GetCurrentProcess().ProcessName.EndsWith("vshost")) {
// We demonstrate using our own executable as child rendering process.
// Also see the entry point (Main function) in Program.cs.
webConfig.ChildProcessPath = WebCoreConfig.CHILD_PROCESS_SELF;
}
WebCore.Initialize(webConfig);
InitializeComponent();
}
#region methodes
#region OpenTab
internal WebDocument OpenTab(string url = null, string title = null) {
WebDocument doc = String.IsNullOrEmpty(url) ? new WebDocument() :
String.IsNullOrEmpty(title) ? new WebDocument(url) : new WebDocument(url, title);
doc.Show(dockPanel);
return doc;
}
#endregion
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
this.OpenTab();
}
I've redone the left panel completely and used the other example that was with the download, that works like a charm. It's very basic but that'll do for now.
I too had the same problem but i devised a work around for the problem . I implemented the main browser engine that needs to be rendered on every tab (WebDocument Page as per the example) as a user control . Then i used a DockPannel in the mainForm .
So i create an instance of the user control and then add this to an instance of the DockPannel and thus it creates a tab with the required structure .
In case you have still not found a solution or have problems , please leave down a comment and i'll put in some code to help you .

How to use Internet Explorer OnQuit Event in a BHO using C#?

I'm trying to use the OnQuit Event of IE, but it's just not firing when I close Internet Explorer. Is there any other way to detect closing of tabs, or the browser in IE?
I'm using it in a BHO written in C#.
Using IE9 and native c++ BHO (ATL), I've no problems getting the onQuit event
My BHO derives from IDispEventImpl, and in the SINK_MAP I specify the DISPID_ONQUIT event:
class ATL_NO_VTABLE CMyBho
:public CComObjectRootEx<CComSingleThreadModel>
...
,IObjectWithSiteImpl<CMyBho>
,IDispEventImpl<1, CMyBho, &DIID_DWebBrowserEvents2, &LIBID_SHDocVw, 1, 1>
{
...
BEGIN_SINK_MAP(CMyBho)
SINK_ENTRY_EX( 1, DIID_DWebBrowserEvents2, DISPID_ONQUIT, onQuit )
END_SINK_MAP()
...
STDMETHODCALLTYPE onQuit( );
...
STDMETHOD(SetSite)(IUnknown* unkSite) {
CComQIPtr<IServiceProvider> ifServiceProvider(unkSite);
CComPtr<IWebBrowser2> ifBrz2;
ifServiceProvider->QueryService( SID_SWebBrowserAPP, IID_IWebBrowser2,
(void**)&ifBrz2 );
this->DispEventAdvise( ifBrz2 );
}
}
Saying all that, I know this is native code (vs. C#) and this is IE9 - but maybe it will give you a hint what needs to be done on your C# implementation. Sent me a note or comment if you want the full source code or need more help.
If you are using WebBrowser Object then as per this OnQuit() MSDN:
The WebBrowser object ignores this event.. One solution is to use native code as mentioned in Uri's answer.
OnQuit works pretty well for me with IE11.
Handler:
public void OnQuit()
{
logger.Debug("Entered OnQuit");
}
Wiring:
int IObjectWithSite.SetSite(object site)
{
if (site != null)
{
mainWindowBrowser = (WebBrowser)site;
mainWindowBrowser.DocumentComplete += new DWebBrowserEvents2_DocumentCompleteEventHandler(this.OnDocumentComplete);
mainWindowBrowser.OnQuit += new DWebBrowserEvents2_OnQuitEventHandler(this.OnQuit);
}
else
{
mainWindowBrowser.DocumentComplete -= new DWebBrowserEvents2_DocumentCompleteEventHandler(this.OnDocumentComplete);
mainWindowBrowser.OnQuit -= new DWebBrowserEvents2_OnQuitEventHandler(this.OnQuit);
mainWindowBrowser = null;
}
return 0;
}

Managed BHOs not instantiated using Protected Mode

I am writing a BHO for IE using C#. The code I'm concerned with is this:
public class BHO : IObjectWithSite, IOleCommandTarget
{
...
public BHO()
{
MessageBox.Show("Constructor called");
}
public int SetSite(object site)
{
MessageBox.Show("SetSite called!");
if( site != null )
{
_webBrowser = (WebBrowser) site;
_webBrowser.NavigateComplete2 += OnNavigateComplete2;
}
else
{
_webBrowser.NavigateComplete2 -= OnNavigateComplete2;
_webBrowser = null;
}
return 0;
}
private void OnNavigateComplete2(object pDisp, ref object URL)
{
MessageBox.Show("OnNavigateComplete2 called");
}
When IE is run with Protected Mode off, everything works fine. However, if Protected Mode is turned on, NavigateCompleted2() is called, but SetSite() and the constructor are never called (!?!). However, if I create a menu item which calls a method in the BHO class, or open a new tab, everything is correctly called.
Does anyone know why it doesn't work when I open a new IE window?
The full source listing can be found here.
Someone on MSDN answered my question: the constructor and method were still being called, but for some reason the MessageBoxes don't show when I open a new window in Protected Mode until the page is loaded. Variables weren't being set due to a different problem - the constructor was instantiating an object which was silently failing.
I now need help with a different (very much related) problem.

Categories