求助大家
我有通过mshtml 来获得网页的元素集合IHTMLElementCollection(线程A)然后开辟一个线程来分析IHTMLElementCollection 中的元素 (线程B)
在(线程B) 处理的时候,(线程A)会继续获取网页元素
这个时候,我发现原本获取的网页元素值变化了(因为之前获取的是指针)有什么办法可以存储获取的IHTMLElement  ??我想了些法子。。比如遍历属性保存。。但不能存储自定义属性,而且还耗费内存空间谢谢大家

解决方案 »

  1.   

    其实,不管你要完成的是数据的获取,还是挂接各种事件,完成自动表单填写等,.net Webbrowser都可以很轻松的完成,参考如下:下面的代码假设你已经建立了一个Windows Form,上面有一个WebBrowser名为“webBrowser”。Study Case 1:用WinForm的Event Handler响应Web页面的事件现在有这样一个Windows Application,它的界面上只有一个WebBrowser,显示一个本地的HTML文件作为界面。现在的问题是,所有逻辑都可以放在HTML文件里,唯独“关闭”按钮遇到了困难——通常,Web页面是没有办法直接控制浏览器的,更不用说结束这个WinForm程序了。但是,在.Net 2.0当中,“由Windows Form响应Web页面的事件”已经成为了现实。在.Net 2.0中,整个HTML文档以及其包含的各个HTML元素,都和一个个HtmlDocument、HtmlElement之类的.Net对象对应。因此只要找到这个“关闭”按钮对应的HtmlElement对象,为其click事件添加Event Handler即可。
    假设HTML源代码如下:
    <html>
    <body>
    <input type="button" id="btnClose" value="关闭" />
    </body>
    </html>那么找出该按钮并为之添加Event Handler的代码如下:HtmlDocument htmlDoc = webBrowser.Document;
    HtmlElement btnElement = htmlDoc.All["btnClose"];
    if (btnElement != null)
    {
    btnElement.click += new HtmlElementEventHandler(HtmlBtnClose_Click);
    }其中 HtmlBtnClose_Click是按下Web按钮时的Event Handler。很简单吧?那么稍稍高级一点的——我们都知道一个HTML元素可能有很多各种各样的事件,而HtmlElement这个类只给出最常用、共通的几个。那么,如何响应其他事件呢?这也很简单,只需要调用HtmlElement的AttachEventHandler就可以了:btnElement.AttachEventHandler("onclick", new EventHandler(HtmlBtnClose_Click));
    //这一句等价于上面的 btnElement.click += new HtmlElementEventHandler(HtmlBtnClose_Click);对于其他事件,把"onclick"换成该事件的名字就可以了。例如:
    formElement.AttachEventHandler("onsubmit", new EventHandler(HtmlForm_Submit));Study Case 2:表单(form)的自动填写和提交
    要使我们的WebBrowser具有自动填表、甚至自动提交的功能,并不困难。假设有一个最简单的登录页面,输入用户名密码,点“登录”按钮即可登录。已知用户名输入框的id(或Name,下同)是username,密码输入框的id是password,“登录”按钮的id是submitbutton,那么我们只需要在webBrowser的 DocumentCompleted事件中使用下面的代码即可:HtmlElement btnSubmit = webBrowser.Document.All["submitbutton"];
    HtmlElement tbUserid = webBrowser.Document.All["username"];
    HtmlElement tbPasswd = webBrowser.Document.All["password"];
    if (tbUserid == null || tbPasswd == null || btnSubmit == null)
        return;
    tbUserid.SetAttribute("value", "smalldust");
    tbPasswd.SetAttribute("value", "12345678");
    btnSubmit.InvokeMember("click");这里我们用SetAttribute来设置文本框的“value”属性,用InvokeMember来调用了按钮的“click”方法。因为不同的Html元素,其拥有的属性和方法也不尽相同,所以.Net 2.0提供了统一的HtmlElement来概括各种Html元素的同时,提供了这两个方法以调用元素特有的功能。关于各种Html元素的属性和方法一览,可以查阅MSDN的DHTML Reference。※关于表单的提交,的确还有另一种方法就是获取form元素而不是button,并用form元素的 submit方法:HtmlElement formLogin = webBrowser.Document.Forms["loginForm"];
    //……
    formLogin.InvokeMember("submit");本文之所以没有推荐这种方法,是因为现在的网页,很多都在submit按钮上添加onclick事件,以对提交的内容做最基本的验证。如果直接使用form的submit方法,这些验证代码就得不到执行,有可能会引起错误。
      

  2.   

    谢谢,我没有采用Webbrowser 的初衷是
    1、避免产生IE 缓存
    2、加快载入速度(PS: 我是采用xmlhttp + mshtml 的方式, mshtml只是作为DOM 分析引擎在使用)
    --- 我需要一天8调试,针对6个站点循环获取数据。
    如果换用IE 对象,缓存可能会增多,加上加载图片的时间,速度会慢。
      

  3.   

    这次问的问题可能与我工作上无关,主要是为了测试一个最近写的框架。这个项目遵循框架的接口规范,
    框架通过接口,可以来管理符合接口的程序(运行、计划任务运行、停止、错误通知、错误后尝试运行等等)然后这个项目是获取给定网址中所有的超级链接,然后保存超链接下列资料到数据库。
    1、title
    2、body
    3、url再次感谢。
      

  4.   

    这个问题多半是由于GC造成的,垃圾回收之后,造成了空的引用,LZ可是试一下强制GC不回收Document对象,GC.F?????记不太清了,看看有没有效果,如果成功了,也告诉我一声。
      

  5.   

    仔细看了一下LZ的问题,可能和我上面说的还不太一样,LZ的问题应该发生的很普遍,因为mshtml本身是支持脚本的,因此没有加载完就另开线程读取,是肯定有问题的。另外使用mshtml最好不要多线程访问同一个Document,调用这些Com需要让线程跑在STA下面,别用MTA。
    即使在STA下,有时还会出现我上面所说的莫名其妙变为空引用的情况,多半是GC造成的。
      

  6.   


    谢谢mshtml 是非托管代码,理论上mshtml 产生的对象不会被自动回收。而.NET 中存储mshtml 返回值的Array 等信息都没有被回收,只是被存储的IHTMLElement 中的数据被清空了。。
      

  7.   


    我期望的是,我想把mshtml 运行中产生的 Document 提取一个子对象(比如getElementsByTagName 返回的结果集)进行单独处理。 然后mshtml 还是继续执行自己的操作。。但看起来好像 Document 与 产生的结果集 是相互存在的, 结果集不能独立于Document 进行DOM 的访问操作。
      

  8.   

    情况是这样,我以前也曾经用mshtml解析Html,用的是IMarkupService,这样可以避开WebBrower,当解析一些比较大的文档时,会莫名其妙的出现,Document对象变空的情况,从理论上讲,确实不应该出现,后来改用了别的方案。现在我再看这个问题的时候,感觉最有可能造成这个问题的原因就是GC,用 GC.SuppressFinalize,可以指定不回收某个对象,但也没有时间再尝试了,所以我希望LZ能够给我一个结果。
      

  9.   

    呵呵,和我当年的思路是多么的象呀,其实往后还会有更多不可思议的问题,LZ需要有些心理准备。
    .Net下的mshtml其实还是从Com中封装出来的。首先不保留Document对象,只保留具体的Element是肯定不行的,我感觉,Element里面实际保存的就是一个字符串的开始位置和结束位置,这个字符串就是整个Document对象的内容。另外LZ可以看一下,当Elements变为空的时候,其父级的Elements是否也已经是空了?或说整个Document都已经变为空了?
      

  10.   

    谢谢。如果被GC 回收,是否会执行到Dispose ?
    刚才调试结果: 从Elements 有值到无值 不会触发 Dispose 方法我想到个解决方法
    首先,我不是通过mshtml get\post 站点,而是通过xmlhttp (虽然可以直接使用mshtml 访问站点,
    但由于最初我是用VFP 写的COM(xmlhttp + ie 对象 ),然而在.NET 多线程调用的情况下,VFP Runtime 会产生C0000005 的Error 所以使用.NET 重写,为了尽可能利用原来的代码,所以还是保留了原来的设计方法。
    )所以,mshtml 对我来说只是DOM 分析的一个框架。 
    联想到:我存储Elements 的时候,把页面的Document 也存储起来,
    需要取值的时候把Document 恢复到mshtml 中。
    有点感概,虽然用xmlhttp 获取数据有点绕路,但在这个方案感觉反而简单了
    优点:
    1,整个站点都通过一个xmlhttp 访问,不会因为获取Elements(采用Document 恢复到mshtml) 而造成数据的错误.
    缺点:
    1,可能因为存储了整个Document ,内存上可能会被增多。
    不过方法能否行的通,还需要做测试,最近几天会回复结果。
      

  11.   

    并不是说被GC回收掉了,而是GC在做回收时,为了避免过多的内存碎片,会把对象存放的地址移动,怀疑是在这个移动的过程中,造成引用指向了错误的地址,所以造成了空的情况。
      

  12.   

    按照网上查找的资料显示。GC 回收的时候会对栈的资源进行回收(值类型、引用类型的引用)而不会对堆的数据进行修改,前几天事情比较多,都没上网。由于对GC 不熟悉,这几天还得好好查查资料看看。。
      

  13.   

    应该都会回收的,除了LOH部分。
      

  14.   

    厄不会执行到。。
    是不是我的方法有错误?我在下列地方设置断点,但都没跑到1、在类的析构函数
    2、Dispose 中不行就用前面说的笨方法了。
      

  15.   

    Dispose 跟GC没什么直接关系,因此不会被执行到。
    析构函数在回收时会被执行,但如果此时对象还被引用,GC就不会回收。
      

  16.   


    谢谢litaoye 一直回帖。  这个问题就此结束,等有时间了再深究唉,技术爱你不容易啊现实很残酷啊。。