C#工作总结(二):Webbrower使用注意事项总结

C#工作总结(二):Webbrower使用注意事项总结_第1张图片


一. ActiveX单线程(ActiveX STAThread)

        随由于ActiveX控件只能在单线程空间中运行,而Webbrower是ActiveX控件的一种,所以有会有如下几点要求:

        (1)在异步的时候,Webbrower必须转移到单线程空间中进行创建和运行。在一些情况下,使用异步多线程会改变线程的状态,使Webbrower创建或者运行代码空间不是单线程空间,导致如下的错误,如图1:

C#工作总结(二):Webbrower使用注意事项总结_第2张图片

图1

        解决的方法:将创建或者执行的线程归入窗体的单线程空间中。使用“this.Invoke(()=>{...});”来进行解决。

        注:这种问题还有一种表现形式,在代码执行的时候卡死或者创建的时候,窗体Show()不出来或者Show()出来一片空白。均有可能是由于这个问题造成的。

        (2)还有一中情况是,当前执行的代码段就是不是单线程空间。在winform中创建,在Main()方法上本来就有声明“[STAThread]”即为单线程空间。所以在一些控制台程序中使用,要在创建或者执行有Webbrower的方法上标注标签“[STAThread]”,如下图2:

C#工作总结(二):Webbrower使用注意事项总结_第3张图片

图2

        还有另外一种做法就是使用System.Threading.Thread的时候,显示声明单线程单元,如下代码:

Thread thread = new Thread(() => Console.WriteLine("Hello Webbrower!"));
                    thread.SetApartmentState(ApartmentState.STA);
                    thread.Start();

        (3)还有切记不要将业务逻辑归入有Webbrower的窗体中做,而是应该直接在调用方的领域空间中完成。并且不要让归入的主窗体有时间非常长的主线程业务处理逻辑(主线程需要同步处理),不然会有业务逻辑执行完成之后,调用Invoke将线程归入主窗体进行处理,来刷新Webbrower,而主窗体中又有事务在处理,导致Invoke的处理事项一直在等待主窗体的任务执行完毕,造成Webbrower的窗体无法直接更新,造成假死。

        解决的方法:Webbrower窗体不处理复杂的业务逻辑。业务逻辑让调用方异步进行,刷新的话让调用方回调处理Webbrower窗体刷新等操作。

这是我的测试工程

二. COM互操作 (COM Operation)

        在Webbrower的使用过程中会涉及到COM互操作,就是一些底层的C++库的调用。只要在Webbrower承载的窗体类上增加特性“[ComVisibleAttribute(true)]”,否则在运行的时候会出现异常提示。如下图3:

C#工作总结(二):Webbrower使用注意事项总结_第4张图片

图3


三. Webbrower常用属性设置 (Setting Webbrower)

        (1)禁用用户鼠标右键菜单,将IsWebBrowserContextMenuEnabled设置为false;

        (2)禁用快捷键,将WebBrowerShortcutsEnabled设置为false;

        (3)尽量不要使用忽略脚本错误提示设置,而是通过如下的方式来在前端进行捕获,并在后台进行日志等的异常处理,如下代码:

function TestMethod()
{
    try
    {
        //可能会出现问题的代码
    }
    catch(e)
    { 
        //前端的异常处理逻辑
        window.external.ResolveException(e);//后台的异常处理方法
    }
};

        (4)禁用超链接,超链接有两种,一种是当前页面直接跳转,一种是在新窗口的打开:

            <1>对于在窗口中直接跳转的,直接禁用AllowNavigation为false

            <2>对于在新的窗口中打开的,我们要拦截webbrower的打开新窗体的事件:

private void webBrowser_NewWindow(object sender, CancelEventArgs e)
{
     e.Cancel = true;
} 

        (5)禁用下拉列表,将AllowWebBrowserBrop设置为false;  

        (6)在一些做窗体的时候,可以禁用滚动条ScrollBarsEnabled为false。于此同时将Dock的填充属性设置为Fill;

        (7)禁用鼠标刷新和右击,如果不禁用会导致右击或者按F5刷新导致页面一片空白:

//禁用鼠标刷新和右击
document.onkeydown = function (e) {
	e = window.event || e;var keycode = e.keyCode || e.which;
	if (keycode = 116) {
		return false;
	}
}
document.oncontextmenu = function (e) {
	return false;
}


四. Webbrower内部网页使用注意与技巧 (Inner Html Page)

        (1)使用网页最大兼容性,在head标签中加入,即最佳兼容性方案。因为webbrower默认用IE7的内核进行加载网页,通过这个声明强行把使用的IE设置为最高。有时候会显示canves的某某某属性不存在,或者莫名其妙的提示一个js的“缺少标识符、字符串或数字”的错误,一般是由于IE使用的内核太低导致的。用这句话来提高IE的运行版本,使其能兼容这些html或js的新增的元素。

注:这句话要紧跟在head后面,并且前面不要有任何其他的标签,这是为了防止失效。这个声明同时也会解决一定显示布局的兼容性问题,如:可以解决界面布局可以被选中的问题,如下图4:

C#工作总结(二):Webbrower使用注意事项总结_第5张图片

图4

        (2)通过使用CSS来清除所有元素的边距:

*{
    magin:0px;
    padding:0px;
}

        (3)禁止用户对内容的选择,比如刷黑文字来选择复制:

选择器{
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}

        注:这种方式会让文字在内的等等的元素,在webbrower中不能被选择复制。        

        *(4)禁止用户将图片拖放到其他的Webbrower中:

$(document).ready(function () {
        document.ondragstart = function (event) {
            return false;
        };
});

        注:这种方式是用来防止图片元素在不同的webbrower中被拖来拖去,使得页面被图片沾满之后出现意想不到的BUG。这边我找了一个例子,如下图5:

C#工作总结(二):Webbrower使用注意事项总结_第6张图片

图5

        *(5)不要轻易使用标签,如果一定要使用来简单的制造“下划线”的效果,那么要将它的href点击特性进行禁用。如:内容部分进行禁用(当然有其他的禁用方式,只要可以做到禁用就行)。这是因为,如果在不禁用的情况下或者简单写成href="#"或者href=""的情况下,都会出现使webbrower重载网页,即在C#后台调用一次webbrower的DocumentCompleted事件,导致出现很难发现的隐藏Bug。如下图6,进行禁用连接跳转:


图6

        (6)对于webbrower载入网页的速度问题,尽量做到在不用动态加载的部分,直接写成静态的html标签和样式,在网页Navigate的时候直接初始化掉。对于动态部分,比如文字内容等,再在DocumentCompleted的时候进行动态的注入。这样会减少加载网页的时候出现“白色”(没有内容)的时间。


四. Webbrower与IE内核兼容性(Compatibility With IE Core)

        这一部分的内容我先综述一下。Webbrower和IE内核的兼容性问题,这边建议使用IE10 的内核,当然IE11目前看来也是可以胜任,但是我真的主推IE10。这是因为IE11和IE9,以及IE9一下的版本,都与Webbrower有一定的兼容性Bug。我下面分点来描述一下:

        *(1)IE11在webbrower加载gif图片多次打开窗体,或者从这个加载窗体打开新的窗体之后再退回到这个窗体的时候。会有gif不动的情况。但在IE9和IE10中就不会有这个问题,可以尝试使用WPF中的Webbrower控件解决gif不动的问题,就是在winform中加入一个WPF的用户控件,里面承载一个Webbroser

        *(2)在使用WPF的webbrower时,有时候在定了两个div元素后,在webbrower中加载完毕后,用鼠标同时跨div,选中两个div中的部分内容,会报出一个神奇的C++底层运行库的错误问题,这个问题可能是由以下两个原因造成的:

                   <1>C++运行库没有安装,或者安装未安装完整;

                   <2>IE9内核的兼容问题(由于亲测IE10及以上版本都不会有这个问题);

        (3)最后说下IE9以下的内核。IE8开始就不能很正常的在webbrower中运行网页了。会弹出各种错误。对于更加以下的版本,就完全没得用了。

        所以综上所述,最好的使用的IE内核就是IE10了。在程序中,只要用户电脑中有IE10的版本,即使默认是IE11,也可以通过在软件运行的时候动态去强制使用IE10来处理webbrower网页的使用,也可以通过修改注册表来实现。


五. 关于Webbrower的内存泄漏问题(Memory Leak)

        之前的写的这篇文章没有写这个,这个第五点是后面补充的。在此期间查看了网上的很多资料,有一篇文章说,在IE8以下的版本中,微软有一个Bug会造成webbrower内存泄漏,但是在IE及以上版本中不会有这个问题。

WebBrower控件 内存溢出(泄露)解决方案汇总     

         于是我在项目测试的时候使用了IE9以上的内核,但是居然还是有内存泄漏的问题。于是又看了好多网上的资料,说使用GC回收配合SetProcessWorkingSetSize来处理这个问题,但是尝试一下,发现还是有内存泄漏的问题。

用SetProcessWorkingSetSize来解决

        由于SetProcessWorkingSetSize是把物理内存转换为虚拟内存来减少程序集的内存大小,但是还是会由于周而复始的Webbrower创建加载导致程序弹出内存不足的异常。重点是使用了GC.Colloct()没有任何的作用。

        后面多次尝试后发现,在Winform的Webbrower中要进行先显示的Dispose()之后,即Webbrower.Dispose()之后,再进行GC.Collect(),才能将Webbrower占用的内存释放。与此同时,经过尝试后发现,对于WPF的Webbrower就算不显式的使用Dispose(),它的内存可以被GC释放:P。

        所以只要在窗体的Closing事件中,加入Webbrower.Dispose()即可解决问题。CLR书上说了,要相信GC的垃圾回收算法和能力。


六. WPF与Winform的不同(Difference Between WPF And Winform)

        要说不同,其实WPF和Winform的东西真的是大同小异。这边我就讲一个注意吧,第一次我写WPF的时候,遇到的一个问题。

        在WPF中绑定ObjectForScripting的对象与Winform中有所不同。在Winform中可以直接使用窗体来作为绑定的对象,即“this.webBrowser.ObjectForScripting = this;”,但是在WPF中必须创建一个代理对象(随便建一个类对象就可以)来进行绑定。


七. 最后(At Last)

        我把上面“神一般”的Bug用“*”标注出来了,这两个问题我阅读了很多的国外文章才总结出来的。在国内用百度根本搜到的都是一知半解的东西,希望可以给自己和大家在工作中有解决方法的思路。如果有什么说的不对的,可以下面留言,我会及时纠正,如果真的帮到了,希望可以关注我一下,或者转发一下。谢谢大家。2018年6月8日,晚。



你可能感兴趣的:(工作总结,C#,前端,后台)