现在在做Memory Leak的测试,需要覆盖到Native的C++ Code和Managed C# Code,下边是学习笔记和一些自己的想法…
Native的C++代码来说,内存泄露就是New的对象没有delete,概念相对Managed来说要简单很多,而且很好理解,可以用Memory Validator来做,这个工具会很明显的给出什么地方可能会有内存泄露,并给出Call Stack,我们可以看到在哪个对象New了以后没有delete。我们通过在整个solution中搜索这个变量,去看对象有没有被Return、赋值或者delete,如果都没有的话,那说明这个地方是一个内存泄露。
对Managed的C#代码来说,内存泄露的概念就跟Native的C++概念不同。因为所有的New所分配的内存是在托管堆上分配的,所以不存在一般意义上的内存泄露。对Managed Code来说,内存泄露就是指一些没有用的对象被reference,导致GC不能回收,从而造成这部分内存相对泄露(这个是我自己的概念^_^)。我们可以通过.Net Memory Validator和.NET Memory Profiler 3.1来跟踪内存的使用情况,从而分析内存泄露。
两个例子:
1. 比如一个全局的ArrayList里边New了很多的对象,这个ArrayList只在一个函数里边使用,那么在这个函数调用结束的时候,就必须给ArrayList赋值为null。否则这个全集的ArrayList只能等到程序结束的时候才能被释放,在这个期间因为ArrayList的引用,导致ArrayList里边的对象不能被释放回收。
2. 比如一个SolidBrush对对象,没有包括在using语句中,那个这个对象就不会自动被调用despose接口,导致内存泄露。
我们有时候需要做一些很耗时间的操作,比如到网络上拿一些数据或者对很多数据进行运算处理,如果是在单线程的程序中,这些运算就会阻塞UI线程,现象就是UI不能响应用户的任何操作,不会刷新。这个时候用户很可能以为程序已经死掉,从而造成很差的用户体验。
解决方法:将很耗时间的运算和处理放在单独的一个线程中进行,UI操作不会受到影响,用户还可以进行其它操作(如果UI中有些操作依赖于当前的的处理,我们可以先将它灰掉,在处理线程结束后再使其可用。更好的是在UI的线程中放一个进度条,来告诉用户当前的处理进度)
在C#中做多线程的处理非常简单:
只需要用代理(Delegate)和跟代理相关的两个函数:
1. BeginInvoke 这个方法将代理指向的方法在一个单独的线程中调用
使用方法:
a. 声明一个代理的对象
b. 用这个代理对象调用BeginInvoke方法2. Control.Invoke 这个方法在当前UI的线程空间内调用代理所指向的方法
使用方法:
a. 声明一个代理对象
b. 调用控件的Invoke方法(一般在WinForm程序中,可以调用this.Invoke方法),将代理对象作为参数传递过去看代码:
1: private delegate void LoadPhotosDelegate(SearchParam param);2: private void LoadPhotosFromFlickr(SearchParam param)3: {4: UpdateUIDelegate updateUI = new UpdateUIDelegate(this.UpdataUI);5: this.Invoke(updateUI);6: }7:8: private delegate void UpdateUIDelegate();9: private void UpdataUI()10: {11: //进行UI的更新操作,比如更新进度条,显示图片等12: }13:14: private void SearchPhoto(SearchParam param)15: {16: LoadPhotosDelegate loadPhotos = new LoadPhotosDelegate(LoadPhotosFromFlickr);17: loadPhotos.BeginInvoke(param, null, null);18: }
看代码:
需要Reference下面的DLL:
System.Drawing
System.Windows.Forms
WindowBase
1: using System;
2: using System.Collections.Generic;
3: using System.Text;
4: using System.Windows;
5: using System.Windows.Forms;
6: using System.Drawing;
7: using System.Runtime.InteropServices;
8:
9: namespace ConsoleApplication1
10: {
11: class Program
12: {
13: [System.Runtime.InteropServices.DllImport("gdi32.dll")]
14: public static extern IntPtr CreateDC(string driver, string device, IntPtr res1, IntPtr res2);
15:
16: public enum TernaryRasterOperations
17: {
18: SRCCOPY = 0×00CC0020, /* dest = source*/
19: SRCPAINT = 0×00EE0086, /* dest = source OR dest*/
20: SRCAND = 0×008800C6, /* dest = source AND dest*/
21: SRCINVERT = 0×00660046, /* dest = source XOR dest*/
22: SRCERASE = 0×00440328, /* dest = source AND (NOT dest )*/
23: NOTSRCCOPY = 0×00330008, /* dest = (NOT source)*/
24: NOTSRCERASE = 0×001100A6, /* dest = (NOT src) AND (NOT dest) */
25: MERGECOPY = 0×00C000CA, /* dest = (source AND pattern)*/
26: MERGEPAINT = 0×00BB0226, /* dest = (NOT source) OR dest*/
27: PATCOPY = 0×00F00021, /* dest = pattern*/
28: PATPAINT = 0×00FB0A09, /* dest = DPSnoo*/
29: PATINVERT = 0×005A0049, /* dest = pattern XOR dest*/
30: DSTINVERT = 0×00550009, /* dest = (NOT dest)*/
31: BLACKNESS = 0×00000042, /* dest = BLACK*/
32: WHITENESS = 0×00FF0062, /* dest = WHITE*/
33: };
34:
35: [DllImport("gdi32.dll")]
36: public static extern bool BitBlt(IntPtr hObject, int nXDest, int nYDest, int nWidth,
37: int nHeight, IntPtr hObjSource, int nXSrc, int nYSrc, TernaryRasterOperations dwRop);
38:
39: public static void CaptureDesktop(string sPath)
40: {
41: Rect rect = new Rect();
42: rect.Width = Screen.PrimaryScreen.Bounds.Width;
43: rect.Height = Screen.PrimaryScreen.Bounds.Height;
44:
45: IntPtr dcTmp = CreateDC("DISPLAY", "DISPLAY", (IntPtr)null, (IntPtr)null);
46: Graphics gScreen = Graphics.FromHdc(dcTmp);
47: Bitmap image = new Bitmap((int)(rect.Width), (int)(rect.Height), System.Drawing.Imaging.PixelFormat.Format24bppRgb);
48: Graphics gImage = Graphics.FromImage(image);
49: IntPtr dcImage = gImage.GetHdc();
50: IntPtr dcScreen = gScreen.GetHdc();
51: BitBlt(dcImage, 0, 0, (int)(rect.Width), (int)(rect.Height), dcScreen, (int)(rect.Left), (int)(rect.Top), TernaryRasterOperations.SRCCOPY);
52: gScreen.ReleaseHdc(dcScreen);
53: gImage.ReleaseHdc(dcImage);
54:
55: image.Save(sPath);
56: }
57:
58: static void Main(string[] args)
59: {
60: CaptureDesktop("c:\\1.bmp");
61: }
62: }
63: }
上一篇讲了在自动化测试中如果控件不能识别,我们最后的办法是模拟鼠标键盘,这一篇就讲如何来做。
首先我先讲在C#中怎么做,至于在C++或者脚本中怎么做,留在后边来讲
对C#来说,键盘的模拟比较简单,在.Net Framework中System.Windows.Forms.SendKeys这个类
鼠标呢,看下边代码:
1: [DllImport(“user32″)]
2: public static extern void mouse_event(int dwFlags, int dx, int dy, int dwData, int dwExtraInfo);
3:
4: [Flags]
5: public enum MouseEventFlags
6: {
7: Move = 0×0001,
8: LeftDown = 0×0002,
9: LeftUp = 0×0004,
10: RightDown = 0×0008,
11: RightUp = 0×0010,
12: MiddleDown = 0×0020,
13: MiddleUp = 0×0040,
14: Wheel = 0×0800,
15: Absolute = 0×8000
16: }
17:
18: void PixelsToAbsCoors(double x, double y, ref double xOut, ref double yOut)
19: {
20: //points are based on current screen size setting
21: xOut = x * 65536 / Screen.PrimaryScreen.Bounds.Width + 0.5;
22: yOut = y * 65536 / Screen.PrimaryScreen.Bounds.Height + 0.5;
23: }
24: public void Move(double x, double y)
25: {
26: PixelsToAbsCoors(x, y, ref x, ref y);
27: mouse_event((int)(MouseEventFlags.Move | MouseEventFlags.Absolute), (int)x, (int)y, 0, 0);
28: }
29: public void Click(double x, double y)
30: {
31: Move(x, y);
32: PixelsToAbsCoors(x, y, ref x, ref y);
33: mouse_event((int)(MouseEventFlags.LeftDown | MouseEventFlags.Absolute), (int)x, (int)y, 0, 0);
34: mouse_event((int)(MouseEventFlags.LeftUp | MouseEventFlags.Absolute), (int)x, (int)y, 0, 0);
35: }