C#开发中WebBrowser控件的跨域问题使用实例

作者:小菜 更新时间:2025-02-26 点击数:
简介:我们在做web测试时,经常会使用WebBrowser来进行一些自动化的任务。

而有些网页上面会用IFrame去嵌套别的页面,这些页面可能不是在相同域名下的,这时就

【菜科解读】

我们在做web测试时,经常会使用WebBrowser来进行一些自动化的任务。

而有些网页上面会用IFrame去嵌套别的页面,这些页面可能不是在相同域名下的,这时就会出现跨域问题,无法直接在WebBrowser中获取到IFrame中的元素。

下面来做个试验,自己写个页面嵌套一个百度的首页,然后在我们自己的页面上输入要查询的词,最后在百度上自动完成搜索。

下面再建一个简单的WinForm工程测试一下,界面如下:

下面就是WebBrowser的测试代码:

using System;using System.Windows.Forms;namespace WebBrowserTest{ public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { this.webBrowser1.Navigate(this.textBox1.Text); } private void button2_Click(object sender, EventArgs e) { var doc = this.webBrowser1.Document; var frames = doc.Window.Frames; String testValue = doc.GetElementById("search").GetAttribute("value"); frames[0].Document.GetElementById("kw").SetAttribute("value", testValue); frames[0].Document.GetElementById("su").InvokeMember("click"); } }}

我们运行我们的测试程序后,加载之前我们自己写的页面后,在自己的页面上输入我们要查询的词,点击测试按钮,就会看到程序报未处理UnauthorizedAccessException错误:

下面来编写一个Helper类来解决这个问题,主要原理大致就是利用IWebBrowser2这个接口来获取Ifream中的Dom,IWebBrowser2中的document可以转换为IHtmlDocument1,IHtmlDocument2,IHtmlDocument3。

using System;using System.Runtime.InteropServices;using System.Windows.Forms;using mshtml;namespace WebBrowserTest{ // This is the COM IServiceProvider interface, not System.IServiceProvider .Net interface! [ComImport(), ComVisible(true), Guid("6D5140C1-7436-11CE-8034-00AA006009FA"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] public interface IServiceProvider { [return: MarshalAs(UnmanagedType.I4)] [PreserveSig] int QueryService(ref Guid guidService, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out object ppvObject); } public enum OLECMDF { OLECMDF_DEFHIDEONCTXTMENU = 0x20, OLECMDF_ENABLED = 2, OLECMDF_INVISIBLE = 0x10, OLECMDF_LATCHED = 4, OLECMDF_NINCHED = 8, OLECMDF_SUPPORTED = 1 } public enum OLECMDID { OLECMDID_PAGESETUP = 8, OLECMDID_PRINT = 6, OLECMDID_PRINTPREVIEW = 7, OLECMDID_PROPERTIES = 10, OLECMDID_SAVEAS = 4 } public enum OLECMDEXECOPT { OLECMDEXECOPT_DODEFAULT, OLECMDEXECOPT_PROMPTUSER, OLECMDEXECOPT_DONTPROMPTUSER, OLECMDEXECOPT_SHOWHELP } [ComImport, Guid("D30C1661-CDAF-11d0-8A3E-00C04FC9E26E"), TypeLibType(TypeLibTypeFlags.FOleAutomation | TypeLibTypeFlags.FDual | TypeLibTypeFlags.FHidden)] public interface IWebBrowser2 { [DispId(100)] void GoBack(); [DispId(0x65)] void GoForward(); [DispId(0x66)] void GoHome(); [DispId(0x67)] void GoSearch(); [DispId(0x68)] void Navigate([In] string Url, [In] ref object flags, [In] ref object targetFrameName, [In] ref object postData, [In] ref object headers); [DispId(-550)] void Refresh(); [DispId(0x69)] void Refresh2([In] ref object level); [DispId(0x6a)] void Stop(); [DispId(200)] object Application { [return: MarshalAs(UnmanagedType.IDispatch)] get; } [DispId(0xc9)] object Parent { [return: MarshalAs(UnmanagedType.IDispatch)] get; } [DispId(0xca)] object Container { [return: MarshalAs(UnmanagedType.IDispatch)] get; } [DispId(0xcb)] object Document { [return: MarshalAs(UnmanagedType.IDispatch)] get; } [DispId(0xcc)] bool TopLevelContainer { get; } [DispId(0xcd)] string Type { get; } [DispId(0xce)] int Left { get; set; } [DispId(0xcf)] int Top { get; set; } [DispId(0xd0)] int Width { get; set; } [DispId(0xd1)] int Height { get; set; } [DispId(210)] string LocationName { get; } [DispId(0xd3)] string LocationURL { get; } [DispId(0xd4)] bool Busy { get; } [DispId(300)] void Quit(); [DispId(0x12d)] void ClientToWindow(out int pcx, out int pcy); [DispId(0x12e)] void PutProperty([In] string property, [In] object vtValue); [DispId(0x12f)] object GetProperty([In] string property); [DispId(0)] string Name { get; } [DispId(-515)] int HWND { get; } [DispId(400)] string FullName { get; } [DispId(0x191)] string Path { get; } [DispId(0x192)] bool Visible { get; set; } [DispId(0x193)] bool StatusBar { get; set; } [DispId(0x194)] string StatusText { get; set; } [DispId(0x195)] int ToolBar { get; set; } [DispId(0x196)] bool MenuBar { get; set; } [DispId(0x197)] bool FullScreen { get; set; } [DispId(500)] void Navigate2([In] ref object URL, [In] ref object flags, [In] ref object targetFrameName, [In] ref object postData, [In] ref object headers); [DispId(0x1f5)] OLECMDF QueryStatusWB([In] OLECMDID cmdID); [DispId(0x1f6)] void ExecWB([In] OLECMDID cmdID, [In] OLECMDEXECOPT cmdexecopt, ref object pvaIn, IntPtr pvaOut); [DispId(0x1f7)] void ShowBrowserBar([In] ref object pvaClsid, [In] ref object pvarShow, [In] ref object pvarSize); [DispId(-525)] WebBrowserReadyState ReadyState { get; } [DispId(550)] bool Offline { get; set; } [DispId(0x227)] bool Silent { get; set; } [DispId(0x228)] bool RegisterAsBrowser { get; set; } [DispId(0x229)] bool RegisterAsDropTarget { get; set; } [DispId(0x22a)] bool TheaterMode { get; set; } [DispId(0x22b)] bool AddressBar { get; set; } [DispId(0x22c)] bool Resizable { get; set; } } class CorssDomainHelper { private static Guid IID_IWebBrowserApp = new Guid("0002DF05-0000-0000-C000-000000000046"); private static Guid IID_IWebBrowser2 = new Guid("D30C1661-CDAF-11D0-8A3E-00C04FC9E26E"); // Utility for IE cross domain access // Returns null in case of failure. public static IHTMLDocument3 GetDocumentFromWindow(IHTMLWindow2 htmlWindow) { if (htmlWindow == null) { return null; } // First try the usual way to get the document. try { IHTMLDocument2 doc = htmlWindow.document; return (IHTMLDocument3)doc; } catch (COMException comEx) { // I think COMException won't be ever fired but just to be sure ... } catch (UnauthorizedAccessException) { } catch (Exception ex) { return null; } // At this point the error was E_ACCESSDENIED because the frame contains a document from another domain. // IE tries to prevent a cross frame scripting security issue. try { // Convert IHTMLWindow2 to IWebBrowser2 using IServiceProvider. IServiceProvider sp = (IServiceProvider)htmlWindow; // Use IServiceProvider.QueryService to get IWebBrowser2 object. Object brws = null; sp.QueryService(ref IID_IWebBrowserApp, ref IID_IWebBrowser2, out brws); // Get the document from IWebBrowser2. IWebBrowser2 browser = (IWebBrowser2)(brws); return (IHTMLDocument3)browser.Document; } catch (Exception ex) { Console.WriteLine(ex); } return null; } }}

最后将我们的运行代码改为如下形式,调用Helper类中的GetDocumentFromWindow方法:

using System;using System.Windows.Forms;using mshtml;namespace WebBrowserTest{ public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { this.webBrowser1.Navigate(this.textBox1.Text); } private void button2_Click(object sender, EventArgs e) { var doc = this.webBrowser1.Document; var frames = doc.Window.Frames; String testValue = doc.GetElementById("search").GetAttribute("value"); IHTMLDocument3 baiduDoc = CorssDomainHelper.GetDocumentFromWindow(frames[0].DomWindow as IHTMLWindow2); baiduDoc.getElementById("kw").setAttribute("value", testValue); baiduDoc.getElementById("su").click(); } }}

最后运行一下程序可以看到我们可以正常获取到百度上的元素了。

开发,中,WebBrowser,控件,的,跨域,问题,使用,

Android开发使用Dom从网络端解析xml文件

本程序实现了从网络端解析xml文件,展示在列表,并实现点击进入相关页面。

首先我们创建一个类,用来实现http请求和xml文件节点的获取public class XMLParser { // constructor public XMLParser() { * Getting XML from URL making HTTP request * @param url string * */ public String getXmlFromUrl(String url) { String xml = null; try { // defaultHttpClient DefaultHttpClient httpClient = new DefaultHttpClient(); HttpPost httpPost = new HttpPost(url); HttpResponse httpResponse = httpClient.execute(httpPost); HttpEntity httpEntity = httpResponse.getEntity(); xml = EntityUtils.toString(httpEntity,"utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); // return XML return xml; * Getting XML DOM element * @param XML string * */ public Document getDomElement(String xml){ Document doc = null; DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); try { DocumentBuilder db = dbf.newDocumentBuilder(); InputSource is = new InputSource(); is.setCharacterStream(new StringReader(xml)); doc = db.parse(is); } catch (ParserConfigurationException e) { Log.e("Error: ", e.getMessage()); return null; } catch (SAXException e) { Log.e("Error: ", e.getMessage()); return null; } catch (IOException e) { Log.e("Error: ", e.getMessage()); return null; return doc; /** Getting node value * @param elem element public final String getElementValue( Node elem ) { Node child; if( elem != null){ if (elem.hasChildNodes()){ for( child = elem.getFirstChild(); child != null; child = child.getNextSibling() ){ if( child.getNodeType() == Node.TEXT_NODE ){ return child.getNodeValue(); return ""; * Getting node value * @param Element node * @param key string * */ public String getValue(Element item, String str) { NodeList n = item.getElementsByTagName(str); return this.getElementValue(n.item(0));}然后我们创建一个Activity继承与ListActivity,在这个Activity中定义一些节点。

public class AndroidXMLParsingActivity extends ListActivity { // All static variables static final String URL = "http://10.0.2.2/biyeshejidata/menu.xml"; // XML node keys static final String KEY_ITEM = "item"; // parent node static final String KEY_ID = "id"; static final String KEY_NAME = "name"; static final String KEY_COST = "cost"; static final String KEY_DESC = "description"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); ArrayList> menuItems = new ArrayList>(); XMLParser parser = new XMLParser(); String xml = parser.getXmlFromUrl(URL); // getting XML Document doc = parser.getDomElement(xml); // getting DOM element NodeList nl = doc.getElementsByTagName(KEY_ITEM); // looping through all item nodes for (int i = 0; i map = new HashMap(); Element e = (Element) nl.item(i); // adding each child node to HashMap key => value map.put(KEY_ID, parser.getValue(e, KEY_ID)); map.put(KEY_NAME, parser.getValue(e, KEY_NAME)); map.put(KEY_COST, "Rs." + parser.getValue(e, KEY_COST)); map.put(KEY_DESC, parser.getValue(e, KEY_DESC)); // adding HashList to ArrayList menuItems.add(map); // Adding menuItems to ListView ListAdapter adapter = new SimpleAdapter(this, menuItems, R.layout.list_item, new String[] { KEY_NAME, KEY_DESC, KEY_COST }, new int[] { R.id.name, R.id.desciption, R.id.cost }); setListAdapter(adapter); // selecting single ListView item ListView lv = getListView(); lv.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView ?> parent, View view, int position, long id) { // getting values from selected ListItem String name = ((TextView) view.findViewById(R.id.name)).getText().toString(); String cost = ((TextView) view.findViewById(R.id.cost)).getText().toString(); String description = ((TextView) view.findViewById(R.id.desciption)).getText().toString(); // Starting new intent Intent in = new Intent(getApplicationContext(), SingleMenuItemActivity.class); in.putExtra(KEY_NAME, name); in.putExtra(KEY_COST, cost); in.putExtra(KEY_DESC, description); startActivity(in);}最后实现点击进入一个新的页面的Activity。

public class SingleMenuItemActivity extends Activity { // XML node keys static final String KEY_NAME = "name"; static final String KEY_COST = "cost"; static final String KEY_DESC = "description"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.single_list_item); // getting intent data Intent in = getIntent(); // Get XML values from previous intent String name = in.getStringExtra(KEY_NAME); String cost = in.getStringExtra(KEY_COST); String description = in.getStringExtra(KEY_DESC); // Displaying all values on the screen TextView lblName = (TextView) findViewById(R.id.name_label); TextView lblCost = (TextView) findViewById(R.id.cost_label); TextView lblDesc = (TextView) findViewById(R.id.description_label); lblName.setText(name); lblCost.setText(cost); lblDesc.setText(description);} Android,开发,使用,Dom,从,网络,端,解析,xm

压力测试工具JMeter中文使用技巧

如何使用英文界面的JMeterJmeter启动时会自动判断操作系统的locale并选择合适的语言启动,所以,我们启动jmeter后,其会出现一个倍感亲切的中文界面。

但由于jmeter本身的汉化工作做得不好,你会看到有未被汉化的选项及元件的参数。

而且部分翻译并不准确,因此对于英文比较好的牛人来说更喜欢纯正的英文界面。

强制以英文方式启动jmeter的方法如下:在windows环境下,打开jmeter解压目录,bin目录下的jmeter.bat文件,也就是jmeter程序的启动文件,选择记事本方式打开。

做以下修改:.................set HEAP=-Xms512m -Xmx512mset NEW=-XX:NewSize=128m -XX:MaxNewSize=128mset SURVIVOR=-XX:SurvivorRatio=8 -XX:TargetSurvivorRatio=50%set TENURING=-XX:MaxTenuringThreshold=2set RMIGC=-Dsun.rmi.dgc.client.gcInterval=600000 -Dsun.rmi.dgc.server.gcInterval=600000set PERM=-XX:PermSize=64m -XX:MaxPermSize=64mset LOCALE=-Duser.language=en -Duser.region=rem set DEBUG=-verbose:gc -XX:+PrintTenuringDistribution.........rem Server moderem Collect the settings defined aboveset ARGS=%DUMP% %HEAP% %NEW% %SURVIVOR% %TENURING% %RMIGC% %PERM% %DDRAW% %LOCALE%.............晕死,当我上面介绍了那么多后,在最新的2.8版本,我无意中发现了这个功能。

如何使用镜像服务器在调试和修改测试计划的过程中,通常会为采样器增加一些额外的设置,例何设置额外的HTTP头、cookie管理器或认证管理器等,但当设置了这些内容后,sampler发出的请求是否就与预期的完全一支呢?当然用户可以通过添加监听器来看查采样器发出的HTTP请求,但如果调试过程中并不想真正地把请求发送给被测应用,如何解决这个问题呢?Jmeter提供了一个名叫HTTPMirrorServer的组件,HTTPMirrorServer可以启动一个镜像的服务器,该服务器把所有接收到的请求原封不动地返回,这样就可以看到发出请求的具体内容了。

添加HTTPMirrorServer的方式:右键点击“工作台”--->非测试元件--->HTTPMirrorServer如果有必要的话需要修改端口号,点击“启动”按钮来启动Server。

接下来修改采样器,使其将HTTP请求发送到localhost:8081(也就是MirrorServer启动的位置)然后,运行测试计划,则可以从监听器“查看结果树”中看到响应数据。

JMeter分布式测试Jmeter是java应用,对于CPU和内存的消耗比较大,因此,当需要模拟数以千计的并发用户时,使用单台机器模拟所有的并发用户就有些力不从心,甚至会引起JAVA内存溢出错误。

为了让jmeter工具提供更大的负载能力,jmeter短小精悍一有了使用多台机器同时产生负载的机制。

那么,是如何实现多台负载机同时运行的呢?当然不会多个人坐在多台负载机面前,一喊开始,大家同时启动jmeter。

这种方式很笨,也很难达到真正的同步。

其实,我们通过单个jmeter客户端就可以控制多个远程的jmeter服务器,使它们同步的对服务器进行压力测试。

通过远程运行jmeter,测试人员可以跨越多台低端计算机复制测试,这样就可以模拟一个比较大的服务器压力,一个jmeter客户端实例,理论上可以控制任意多的远程jmeter实例,并通过他们收集测试数据。

这样一样,就有了如下特性:* 保存测试采样数据到本地机器* 通过单台机器管理多个jmeter执行引擎。

* 没有必要将测试计划复制到每一台机器,jmeterGUI客户端会将它发往每一台jmeter服务器。

*每一台jmeter远程服务器都执行相同的测试计划,jmeter不会在执行期间做负载均衡,每一台服务器都会完整地运行测试计划。

在1.4GHz~3GHz的CPU、1GB内存的JMeter客户端上,可以处理线程100~300。

但是WebService例外。

XML处理是CPU运算密集的,会迅速消耗掉所有的CPU。

一般来说,以XML技术为核心的应用系统,其性能将是普通Web应用的10%~25%。

另外,如果所有负载由一台机器产生,网卡和交换机端口都可能产生瓶颈,所以一个JMeter客户端线程数不应超过100。

采用JMeter远程模式并不会比独立运行相同数目的非GUI测试更耗费资源。

但是,如果使用大量的JMeter远程服务器,可能会导致客户端过载,或者网络连接发生拥塞。

使用多台机器产生负载的操作步骤如下:(1)在所有期望运行jmeter作为负载生成器的机器上安装jmeter,并确定其中一台机器作为controller,其他的的机器作为agent。

然后运行所有agent机器上的jmeter-server文件(假定使用两台机器192.168.0.100和192.168.0.101作为agent)(2)在controller机器的jmeter的bin目录下,找到jmeter.properties文件,编辑该文件:查找:remote_hosts=127.0.0.1remote_hosts=192.168.0.100:1099,192.168.0.101:1099 这里要特别注意端口后,有些资料说明端口1644为jmeter的controller和agent之间进行通信的默认RMI端口号,但是在测试时发现,设置为1644运行不成功,改成1099后运行通过。

另外还要留意agent的机子是否开启了防火墙等。

(3)启动controller机子上的jmeter应用,选择菜单“运行”--->“远程启动”,来分别启动agent,也可以直接选择“远程全部启动”来将所有的agent启动。

启动Debug日志记录大多数测试元件都支持Debug日志记录。

如果通过GUI运行测试计划,那么在选中测试元件后,可以通过“帮助”菜单enabledebug或者disabledebug。

在“帮助”菜单中有一个选项“What’sthisnode?”,通过它可以查看GUI和测试元件的类名,如图11-7所示。

通过它们,测试人员可以决定修改哪一项JMeter属性,以便修改日志级别。

例如:我们可以点击一个HTTP请求,选择菜单栏“帮助”--->what‘sthisnode? 在jmeter的bin\目录下,找到jmeter.properties文件,关于日志级别的属性如下:#Logging levels for the logging categories in JMeter. Correct values are FATAL_ERROR, ERROR, WARN, INFO, and DEBUG# To set the log level for a package or individual class, use:# log_level.[package_name].[classname]=[PRIORITY_LEVEL]# But omit "org.apache" from the package name. The classname is optional. Further examples below.log_level.jmeter=INFOlog_level.jmeter.junit=DEBUG#log_level.jmeter.control=DEBUG#log_level.jmeter.testbeans=DEBUG#log_level.jmeter.engine=DEBUG#log_level.jmeter.threads=DEBUG#log_level.jmeter.gui=WARN#log_level.jmeter.testelement=DEBUG#log_level.jmeter.util=WARN#log_level.jmeter.util.classfinder=WARN#log_level.jmeter.test=DEBUG#log_level.jmeter.protocol.http=DEBUG# For CookieManager, AuthManager etc:#log_level.jmeter.protocol.http.control=DEBUG#log_level.jmeter.protocol.ftp=WARN#log_level.jmeter.protocol.jdbc=DEBUG#log_level.jmeter.protocol.java=WARN#log_level.jmeter.testelements.property=DEBUGlog_level.jorphan=INFO搜索功能这是在使用一个变量或含有一定的URL或参数测试计划树和元素有时很难找到。

现在一个新的特点是从2.6开始,你可以访问它的菜单搜索。

它提供了搜索与下列选项:* 使搜索区分大小写区分大小写:* 正则表达式是一个正则表达式搜索文本,如果有的话将被搜索的正则表达式树的组件,例如“\BTEST\b”将匹配任何组件,包含测试组件的搜索元素线程之间传递变量JMeter变量作用域局限于所属线程。

这样设计是经过深思熟虑的,目的是让测试线程能够独立运转。

有时候用户可能需要在不同线程间(可能属于同一个线程组,也可能不属于同一个线程组)传递变量。

其中一种方法就是使用属性。

属性为所有JMeter线程所共享,因此当某个线程设置一个属性后,其他线程就可以读取更新后的值。

如果存在大量数据需要在线程间传递,那么可以考虑使用文件。

例如,测试人员可以在一个线程中使用监听器,保存响应到文件(SaveResponsestoafile)或者BeanShellPostProcessor。

而在另外一个线程中使用HTTP采样器的“file:”协议来读取文件,接着使用一个后置处理器或者BeanShell测试元件提取信息。

如果在测试启动前测试人员就能获得测试数据,那么最好将数据保存到文件中,使用CSVDataset读取。

----------------------------------------这最后一个技巧,操作较为麻烦,暂时不给详细的例子。

算是提供个思路吧!知道有这当子事儿就行了。

日后有机会再实践^_^ 压力,测试工具,JMeter,中文,使用技巧,如何,

加入收藏
               

C#开发中WebBrowser控件的跨域问题使用实例

点击下载文档

格式为doc格式

  • 账号登录
社交账号登录