Thank you so much mike_t2e!!!!!!!
-----------------------------------------------------------------------
Is the memory released when you minimize the app ? If so, try this:
-- in class definition
[DllImport("KERNEL32.DLL", EntryPoint = "SetProcessWorkingSetSize", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
internal static extern bool SetProcessWorkingSetSize(IntPtr pProcess, int dwMinimumWorkingSetSize, int dwMaximumWorkingSetSize);
[DllImport("KERNEL32.DLL", EntryPoint = "GetCurrentProcess", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
internal static extern IntPtr GetCurrentProcess();
-- code to call when you want to reduce the memory
IntPtr pHandle = GetCurrentProcess();
SetProcessWorkingSetSize(pHandle, -1, -1);
vb2008 webbrowser 如何禁止 脚本错误提示 及弹出对话框
1.在form 初始化时将webbrowser控件的ScriptErrorsSuppressed = True(false调试模式,允许脚本错误提示),也可以在其他合适的事件中设置。
2.写个子过程
Private Sub Window_Error(ByVal sender As Object, _ ByVal e As HtmlElementErrorEventArg
End Sub
3.在webbrowser控件的browser_DocumentCompleted事件中调用上述子过程,代码如下:
(ByVal sender As Object, _
ByVal e As WebBrowserDocumentComple
End Sub
注:browser为webbrowser控件名,比如webbrowser1之类的。
WinForm中的webBrowser控件禁止弹出新窗口
前两天领导让做一个可以自动填写网页数据并提交的小程序,其中使用到了webBrowser控件,内嵌的网页中的链接会调用本地的IE打开新窗口,影响功能的实现,最后找到了方法解决这一问题:
- //打开新窗体时
- private void webBrowser1_NewWindow(object sender, CancelEventArgs e)
- {
- e.Cancel = true;
- webBrowser1.Navigate(webBrowser1.StatusText);
- }
另外说明一下在webBrowswer使用时操作网页元素的方法:
- /// <summary>
- /// 网页加载完成
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
- {
- HtmlDocument doc = webBrowser1.Document;
- //获取web页面控件
- HtmlElement BT_Username = doc.All["tbAccount"];
- HtmlElement BT_Password = doc.All["tbPassword"];
- HtmlElement BT_Element = doc.All["ImgBtn"];
- //btn控件调用winform中的事件
- if (BT_Element != null)
- {
- //BT_Element.Click += new HtmlElementEventHandler(HtmlBtnClose_Click);
- }
- //web页面控件赋值
- BT_Username.SetAttribute("value", this.username.Text);
- BT_Password.SetAttribute("value", this.password.Text);
- //DropDownList.SetAttribute("value", "2");
- //txt1.SetAttribute("value", "admin");
- //btnElement.InvokeMember("click");
- }
- private void HtmlBtnClose_Click(object sender, HtmlElementEventArgs e)
- {
- //MessageBox.Show("1111");
- }
WebBrowser控件应用:弹出新窗体和关闭窗口
缘起:上次写了一个《WebBrowser控件的简单应用2》,提到了在NewWindow事件中打开新窗口的例子。有网友“队长”提出那个事件得到的参数是本页面的,而不是新页面的,经过测试,果然url参数不是新页面的。
Open新页面要处理的:
1:<<a href=’xxxxx’ target=’_blank’>>;
2: οnclick=’window.open’ ;
3:引用js文件 ;
4:ClientScript.RegisterStartupScript();等。
5:还有要实现在打开的页面里能用window.opener对象
Close要处理的:
1:οnclick=’window.close ;
2:引用js文件 ;
3:ClientScript.RegisterStartupScript()等。
经过在网上搜索,结果并不令人满意。基本上,WebBrowser控件不提供这个(将要打开的新页面的地址)功能,只能通过其他方法。
这里介绍一下网上的一个简单的解决方案、我的一个简单方案、微软的回答、一个终极解决方法。
网上现在有人给出这样的一个解决方案:在_NewWindow事件通过wb_Container.StatusText来获得当前将要打开的页面。代码可以这样写:
private void wb_Container_NewWindow(object sender,CancelEventArgs e)
{
e.Cancel = true;
string newURL = wb_Container.StatusText;
//'open
}
我测试了一下,这样做基本上只能处理<a href=’xxxxx’ target=’_blank’>的这种情况。对于<button>里面的onlcick事件window.open()毫无作用。
原因很简单,他只是利用了WebBrower现实Status的特点来做,不全面,不安全。至于其他情况也是完全不能处理,比如js中使用window.Open,这个方案根本不能检查。
我的处理方法:
刚开始,我使用了这样的思路:针对所有可能出现的情况,找个各个不同方法来处理。
比如说,对于<a herf=’’的这种情况,就用wb_Container.StatusText方法,但是要加入一个判断。If(wb_Container.StatusText!=””)。对于window.open和RegisterStartupScript的,可以通过替换js来实现。
本来的window.open函数,在程序完成加载之后,我把这个函数修改成window.external.open,
然后,我把我的AppBrowser类里面加入几个public的Open函数来对应原来的js的open函数就可以了。
这是一个代码实现。
private void wb_Container_DocumentCompleted(object sender,WebBrowserDocumentCompletedEventArgs e)
{
//for windows.open
if (wb_Container.DocumentText.IndexOf("window.open(") > -1 || wb_Container.DocumentText.IndexOf("window.close()") > -1)
{
wb_Container.DocumentText = wb_Container.DocumentText.Replace("window.open(","window.external.open(").Replace("window.close()","window.external.close()");
}
}
这样处理之后,保证了我的浏览器也能理解这些js。
这个方案唯一不能处理的就是js文件里面有处理的情况。
还有一个小问题,替换之后的document对象的action变化了。这个可以通过方法来实现,就是替换上边的函数,改用递归document.all来实现,检查每个element控件的内容来实现。
但是,毕竟美中不足。
微软的解释是这样的:这个问题,下一个版本在解决(http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=115195),开发者可以通过扩展WebBrower及其Event来实现。并且提供了一个简单的代码(但是显示不完全)。是的,这是个好的解决方案,但不是我想要的。既然封装了这个控件,为什么不提供这么重要的一个参数呢?
有高手已经做出了一个模型:(http://www.codeproject.com/csharp/ExtendedWebBrowser.asp)
(作者实现了一个类似IE7效果的浏览器)
这个模型里面,作者自己封装并且扩展了这个控件,是个最终极的解决方法。
主要思路:提供了一个将要打开新窗口的事件,并且提供更多的参数。这些参数来自IWebBrowser2等接口,还提供了一些WndProc重载来实现window.close。
下载之后,我保留了这些类,做了一个简单的测试
ExtendedWebBrowser : System.Windows.Forms.WebBrowser
.WebBrowserExtendedEvents : UnsafeNativeMethods.DWebBrowserEvents2
.WindowsMessages(enum)
UnsafeNativeMethods
.DWebBrowserEvents2([ComImport, TypeLibType((short)0x1010), InterfaceType((short)2), Guid("34A715A0-6587-11D0-924A-0020AFC7AC4D")])
.IWebBrowser2([ComImport, SuppressUnmanagedCodeSecurity, TypeLibType(TypeLibTypeFlags.FOleAutomation | (TypeLibTypeFlags.FDual | TypeLibTypeFlags.FHidden)), Guid("D30C1661-CDAF-11d0-8A3E-00C04FC9E26E")])
BrowserExtendedNavigatingEventArgs : CancelEventArgs
UrlContext
ScriptError
NativeMethods
结果非常不错。
主要处理的几个地方:
NewWindow2和NewWindow3:处理新打开窗口
BeforeNavigate2:获得将要打开的窗口的地址。
主要提供的新事件:_StartNewWindow(object sender, BrowserExtendedNavigatingEventArgs e)
下边这一个是我的第二个AppBrowser类,使用上边封装扩展之后的WebBrower控件。
1
[System.Runtime.InteropServices.ComVisibleAttribute(true)]2
public partial class AppBrowser2 : Form3

{4

property#region property5
private string _url;6
public string URL7

{8

get
{ return _url; }9

set
{ this._url = value; }10
}11

12
private ExtendedWebBrowser _browser;13
// Allows other code to obtain a reference to the extended web browser component14
private ExtendedWebBrowser WebBrowser15

{16

get
{ return _browser; }17
}18
#endregion19

cons.#region cons.20
public AppBrowser2()21

{22
InitializeComponent();23
}24
public AppBrowser2(string url)25

{26
InitializeComponent();27
_browser = new ExtendedWebBrowser();28
_browser.Dock = DockStyle.Fill;29
_browser.DownloadComplete += new EventHandler(_browser_DownloadComplete);30
_browser.Navigated += new WebBrowserNavigatedEventHandler(_browser_Navigated);31
_browser.StartNewWindow += new EventHandler<BrowserExtendedNavigatingEventArgs>(_browser_StartNewWindow);//get js:window.open or <a href event32
_browser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(_browser_DocumentCompleted);33
_browser.StatusTextChanged += new EventHandler(_browser_StatusTextChanged);34
_browser.Quit += new EventHandler(_browser_Quit);//get js:window.close event35
this.pnl_container.Controls.Add(_browser);36
this.URL = url;37
_browser.Navigate(URL);38
_browser.ObjectForScripting = this;//set this to be the COM handler39
} 40
#endregion41
42

window : close and open#region window : close and open43
void _browser_Quit(object sender, EventArgs e)44

{45
this.Close();46
}47

48
void _browser_StartNewWindow(object sender, BrowserExtendedNavigatingEventArgs e)49

{50
AppBrowser2 ab2 = new AppBrowser2(e.Url.ToString());51
e.AutomationObject = ab2.WebBrowser.Application;//for 'window.opener' 52
ab2.Show();53
} 54
#endregion55

other browser event#region other browser event56
void Window_Error(object sender, HtmlElementErrorEventArgs e)57

{58
// We got a script error, record it59
//ScriptErrorManager.Instance.RegisterScriptError(e.Url, e.Description, e.LineNumber);60
// Let the browser know we handled this error.61
e.Handled = true;62
}63
void _browser_DownloadComplete(object sender, EventArgs e)64

{65
// Check wheter the document is available (it should be)66
if (this.WebBrowser.Document != null)67

{68
// Subscribe to the Error event69
this.WebBrowser.Document.Window.Error += new HtmlElementErrorEventHandler(Window_Error);70
UpdateAddressBox();71
}72
}73
void _browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)74

{75
UpdateAddressBox();76
}77
void _browser_Navigated(object sender, WebBrowserNavigatedEventArgs e)78

{79
UpdateAddressBox();80
}81

82
void _browser_StatusTextChanged(object sender, EventArgs e)83

{84
this.toolStripStatusState.Text = WebBrowser.StatusText;85
}86
// Updates the info on the window87
private void UpdateAddressBox()88

{89
this.toolStripStatusTitle.Text = this.WebBrowser.Document.Title;90
}91
#endregion92

93

external function for js#region external function for js94

/**//// <summary>95
/// this function can be invoked by js in html96
/// like this 'javascript:window.external.ShowMessage('this is invoke from web');'97
/// </summary>98
/// <param name="msg"></param>99
public void ShowMessage(string msg)100

{101
MessageBox.Show(msg, "alert");102
}103

104

/**//// <summary>105
/// open new, for JS: window.open106
/// </summary>107
/// <param name="url"></param>108
public void Open(string url)109

{110
AppBrowser2 newAB2 = new AppBrowser2(url);111
newAB2.Show();112
} 113
#endregion114
}
在程序里是用WebBrower控件定时访问不同的网页,
程序运行时间越长,占用的内存资源越多,最后所有的内存都用完了,
程序会报错,提示大概意思是"资源在使用中"
WebBrower1.Dispose();使用这句想释放内存,但定时器下次触发定时处理事件时,会报不能使用已释放的资源.
请各位帮一忙,看有没有好的解决办法?
因为一旦将 WebBrowser Dispose,那么它就放到待回收队列去了,此时如果再次访问它,CLR 会报一个无法访问已经 Dispose 资源的 Exception。
当然,Dispose 后有些方法是可以再次访问该对象的,这个就是神奇的对象复活。。。我就不多说了。
WebBrowser 实际上是 Internet Explorer ActiveX 的一个 Wrapper,用的是 shdocvw.dll。shdocvw.dll 是非托管的。在只有一个 WebBrowser 实例的 Windows Forms 上,Form 的 Dispose 会自动处理 WebBrowser 的 Dispose。不过,如果在一个 Form 上放了多个 WebBrowser (包括动态创建的),这就可能造成内存泄漏。因为非托管资源的回收需要显式调用 Dispose(false)。而这个工作是在 Form 的 Finalizer 完成的。
一个经典的,也是最好的 Dispose 实现如下:
public class Form : IDisposable
{
public void Dispose()
{
this.Dispose(true);
System.GC.SupressFinalizer(this);
}
public virtual void Dispose(bool disposing)
{
if (disposing) // 托管资源
{
} else {
// 非托管资源
}
}
~Form()
{
this.Dispose(false);
}
}
可见,非托管资源在窗体的析构函数 (Finalizer) 中被回收。而 Form 的析构函数在窗体/应用程序被关闭时执行。
我只能解释原理,剩下的如何寻找解决方案,就要看您怎么修改实现了。
mazhou:您好
"可见,非托管资源在窗体的析构函数 (Finalizer) 中被回收。而 Form 的析构函数在窗体/应用程序被关闭时执行。"
我的这个应用程序运行后不关闭,让它一直运行,有两个WebBrower实例,其中一个实例只打开一次网页就不管它了,另一个实例我会定时(现在是5秒)打开一个不同的网页,但随着时间的推移,这个程序占用的内存越来越大,不知道是不是内存泄漏,如果是,应该如果处理?如果要用dispose进行回收,看了你上面的经典例子后,好像是在窗体关闭或程序退出时进行的,但这个程序就这一个窗体,不关闭它,也不退出程序,像这种情况应该如何减少内存占用?
其实这里一定存在内存泄漏的,WebBrowser 的行为跟 IE 是保持一致的。利用 GC.Collect() 或者 Dispose 只能回收部分托管资源。对于 IE 泄露的内存则可能无能为力。
如果窗体一直打开运行,且只有一个 WebBrowser 控件的话,也没什么好优化的了。这里内存泄露的原因主要在于访问的 Web 页。
您可以做一个简单的试验。打开一 IE,依次输入 10 个 URL 访问 10 个不同的 Web 页,用 Performance Counter 或者 Task Manager 记下每一个 Page 打开后的内存情况。您或许会发现,您的内存使用有增无减。还有,有一些 Web 页使用 AJAX/JavaScript/Timer 获取数据,如果程序代码写得不当,则可能造成更多的内存泄露。
IE 的内存泄露主要表现在:
- JavaScript 闭包导致泄露
- 不恰当的 JavaScript 编码方式导致泄露
- InnerHTML, AppendChild 的内存泄露
- 浏览器后退/前进按钮导致的内存泄露
其实这个问题已经与 Dispose 无关了。要找到 Workaround 的话,您可以先按照我说的做个实验,确保不是由于不同的 Web 页访问导致内存泄露的。
Mark Zhou
本文详细介绍WebBrowser控件的应用技巧,包括自动滚动、禁用脚本错误提示、禁止弹出新窗口、内存管理和事件处理等内容。适用于需要利用WebBrowser控件进行网页交互和爬虫开发的场景。
4064

被折叠的 条评论
为什么被折叠?



