var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-333696-1']); _gaq.push(['_trackPageview']); _gaq.push(['_trackPageLoadTime']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })();
  • 2008年10月17日

    Web自动化(3)

    分类:

    4.     导入ExDispID.hExDisp.h,直接调用IWebBrowser2接口。下面提供此方法实现的Web自动化:

    勾选”ActiveX controls”新建MFC对话框项目IEAutoDemo。首先创建MSIE实例并使其可见:

    HRESULT hr;

    IWebBrowser2* pWebBrowser = NULL;

    hr = CoCreateInstance(CLSID_InternetExplorer, NULL, CLSCTX_SERVER, IID_IWebBrowser2, (LPVOID*)&pWebBrowser);//启动MSIE作为它的子进程

    m_pWebBrowser->put_Visible(VARIANT_TRUE);//默认为False

    m_pWebBrowser->put_StatusBar(VARIANT_TRUE);//该属性及以后2个默认为True;若不想显示,则必须显示声明

    m_pWebBrowser->put_ToolBar(VARIANT_TRUE);

    m_pWebBrowser->put_MenuBar(VARIANT_TRUE);

    如果Adding ATL Support to MFC,因为CComPtrBase::CoCreateInstance已经封装了CoCreateInstance(定义在头文件atlcomcli.h中,而这个头文件被包含在头文件atlbase.h),所以应该这样调用:

    CComQTPtr<IWebBrowser2> m_pWebBrowser2;

    m_pWebBrowser2.CoCreateInstance(CLSID_InternetExplorer);

    注:CLSID_InternetExplorer定义在头文件ExDisp.h中,其对应的注册表ProgIDInternetExplorer.Application(.1)IID_IWebBrowser2也定义在头文件ExDisp.h中。MFC中,IDispatch在基类CCmdTarget中实现,所以不需要INTERFACE_MAP,需要DISPATCH_MAP宏。

    另外一种启动MSIE的方法是:先启动MSIE,然后Attach到它上面。本方法首先用CreateProcess或者ShellExecute来启动iexplore.exe,然后从ShellWindows对象中找出MSIE对象,并同先前启动的进程id做比较,相同即可用。

    CComPtr<IShellWindows> psw;

    psw.CoCreateInstance(CLSID_ShellWindows);

    long lShellWindowCount=0;

    psw->get_Count(&lShellWindowCount);

    for(long i=0;i<lShellWindowCount;i++)

    {

               CComPtr<IDispatch> pdispShellWindow;

               psw->Item(COleVariant(i),&pdispShellWindow);

               CComQIPtr<IWebBrowser2> pIE(pdispShellWindow);

                          //is it the right type?

         CString strWindowClass=GetWindowClassName(PIE);

    if(pIE?strWindowClass==_T("IEFrame"):strWindowClass==_T("CabinetWClass"))

          {

                     HWND hWndID=NULL;

                     pIE->get_HWND((long*)&hWndID);

          }

    }

    由于微软从Windows Vista起加入了UAC特性,该特性使得用户运行MSIEProtected mode下,致使MSIE跟程序IEAutoDemo无法正常通讯。如果对MSIE做简单操作,可以使用函数IELaunchURL,见问题;如果仍用原有方法,需要设置IEAutoDemoManifest File(项目属性->Linker->Manifest File)

     

    接着浏览到需要的网页:

    CString m_strUrl;

    m_strUrl=L"http://www.google.cn";

    COleVariant vaURL(m_strUrl);

    hr=m_pWebBrowser2->Navigate2(

          &vaURL,

          COleVariant((long)0,VT_I4),

          COleVariant((LPCTSTR)NULL,VT_BSTR),

          COleSafeArray(),

          COleVariant((LPCTSTR)NULL,VT_BSTR));

     

    当整个页面加载完成时,顶级frame会引发DocumentComplete事件,于是可以在该事件中进行判断整个页面加载是否完成:

    Void DocumentComplete(IDispatch* pDisp, VARIANT* URL)

    {

     CComQIPtr<IUnknown,&IID_IUnknown> pUK(m_pWebBrowser2);

     CComQIPtr<IUnknown,&IID_IUnknown> pSendUK(pDisp);

     HRESULT hr;

     if (pUK == pSendUK)

     {.TODO.}

    }

    但这样判断不是很稳定,于是可以该用IWebBrowser2ReadyState

    READYSTATE ready;

    hr=m_pWebBrowser2->get_ReadyState(&ready);

    if (SUCCEEDED(hr) && ready==READYSTATE_COMPLETE)

    {.TODO.}

     

    这时整个MSIE页面加载完毕,可以进行DOM操作了:修改MSIE标题,给谷歌首页输入框赋值,并进行”Google搜索。利用Firefoxfirebug中看到其对应的前端代码为:

    <td nowrap="" align="center">

    <input type="hidden" value="zh-CN" name="hl"/>

    <input value="" title="Google 搜索" size="55" name="q" maxlength="2048" autocomplete="off"/>

    <br/>

    <input type="submit" value="Google 搜索" name="btnG"/>

    <input type="submit" value=" 手气不错 " name="btnI"/>

    </td>

    注意标红的html标签。

     

    根据前端标签,开始获取然后点击搜索按钮:

    CComQIPtr<IDispatch> pHTMLDocDispatch;

    hr=m_pWebBrowser2->get_Document(&pHTMLDocDispatch);

    if (SUCCEEDED(hr) && pHTMLDocDispatch!=NULL)

        {

              CComQIPtr<IHTMLDocument2> pHTMLDoc(pHTMLDocDispatch);

              if (pHTMLDoc!=NULL)

              {

                 CComBSTR bstrTitle(_T("Customize Google Search now"));

                 pHTMLDoc->put_title(bstrTitle);

                 CComQIPtr<IHTMLElementCollection> eC;

                 hr=pHTMLDoc->get_all(&eC);

                 if (SUCCEEDED(hr) && eC)

                 {

                    CComQIPtr<IDispatch> pInputDispatch;

                    CComQIPtr<IDispatch> pSubmitDispatch;        

                    hr=eC->item(COleVariant(L"q"),COleVariant((long)0),&pInputDispatch); hr=eC->item(COleVariant(L"btnG"),COleVariant((long)0),&pSubmitDispatch);

        if (SUCCEEDED(hr) && pInputDispatch)

        {

          CComQIPtr<IHTMLElement> pInput(pInputDispatch);

          pInput->setAttribute(CComBSTR(L"VALUE"),COleVariant(L"You can search now!"),0);

          CComQIPtr<IHTMLElement> pSubmit(pSubmitDispatch);

          pSubmit->click();                            

        }}}

     

    以上是C++WebBrowser控件实现的MSIE自动化,对C#可参考书.NET Test Automation Recipes: A Problem-Solution Approach。对比这两种语言的实现,不难发现由于C#的封装使得操控MSIE起步比C++容易了很多。

     

    演示下载:IEAutoDemo

    注:若只用IEAutoDemo.exe,需要先安装Microsoft Visual C++ 2008 SP1 Redistributable Package (x86/x64),然后才能正常运行。

     

    【资源】

    IEHelper - Internet Explorer Helper Class

    Automate the Active Windows Explorer or Internet Explorer Window

    如何对webbrowserIE编程(1)(2)(3)(4)(5)(6)(7)(8)(9)

    How To Retrieve the Top-Level IWebBrowser2 Interface from an ActiveX Control

    WebBrowser Control Overviews and Tutorials

    MFCReference

    封装加载COM

     

    全文阅读:Web自动化

    分享到: