C#藉助API實現黑盒自動化測試載入器的編寫)

來源:互聯網
上載者:User

http://space.itpub.net/12639172/viewspace-677324

本文代碼下載(VS2010開發):http://download.csdn.net/source/2796362

本文摘要:

1:一個簡單的例子    

   1.1:EnumChildWindows介紹

   1.2:主要源碼

2:痛點:如何擷取指定的控制項控制代碼

   2.1:使用SPY++

   2.2:擷取控制項位置

   2.3:擷取控制項ID

1:一個簡單的例子  

     在日常編碼過程中,我們常常會進行自動化測試。這裡的自動化測試不是指單元測試,而是類比人工輸入來進行快速的、高並發的測試。可以使用的自動化工具有LOADRUNNER,以及目前在VS2010中的功能很強大的測試工作平台(錄製操作步驟,自動產生代碼)。但是,這些工具的熟練掌握也有一定的時間成本,並且,最主要的,對於一個程式員來說,那不夠靈活。所以,比較高效的一個做法是,調用WINDOWS API,自己動手寫編碼來實現。

     下面做一個簡單的示範。為了簡便起見,假設存在這樣一個應用程式:

1:提供一個WINFORM表單,上面存在一個TextBox,以及一個Button;

2:點擊Button,會彈出提示框,提示框內容為TextBox的值;

     現在,測試要求如下:

1:在300台機器上運行上面的程式;

2:到這300台機器上去點擊這個Button,看看上文中的功能2有沒有實現;

     很顯然,實際情況中沒有這麼簡單的程式,實際的情況有可能是點擊Button,統一下載一個檔案,而測試的要求可能就變為考核伺服器的負載。現在,測試部顯然也沒有300個人坐在客戶機上驗證測試的結果,這個時候,就需要我們提供一個自動化的測試載入器,來完成必要的測試工作。

     測試載入器,首先也是一個C#的程式,它的主要目的是:

1:擷取上文應用程式的視窗控制代碼,繼而擷取TextBox控制代碼及Button控制代碼;

2:為TextBox隨機填入一些字元;

3:類比點擊Button;

1.1:EnumChildWindows介紹

   在這裡需要介紹下EnumChildWindows,

EnumChildWindows可是個好東西,可以枚舉一個父視窗的所有子視窗:

BOOL EnumChildWindows(
  HWNDhWndParent,         // handle to parent window // 父視窗控制代碼
  WNDENUMPROClpEnumFunc,  // callback function // 回呼函數的地址
  LPARAMlParam            // application-defined value // 你自已定義的參數
);

    就這麼簡單,讓我們再定義一個回呼函數,像下面這樣:

BOOL CALLBACK EnumChildProc(
  HWNDhwnd,      // handle to child window
  LPARAMlParam   // application-defined value
);

    在調用EnumChildWindows 這個函數時,直到調用到最個一個子視窗被枚舉或回呼函數返回一個false,否則將一直枚舉下去。

1.2:簡單例子的主要源碼

    測試載入器的主要代碼如下:

privatevoidbutton1_Click(objectsender, EventArgs e)        {//擷取測試程式的表單控制代碼IntPtr mainWnd = FindWindow(null, "FormLogin");            List<IntPtr> listWnd =newList<IntPtr>();//擷取表單上OK按鈕的控制代碼IntPtr hwnd_button = FindWindowEx(mainWnd,newIntPtr(0),null, "OK");//擷取表單上所有控制項的控制代碼EnumChildWindows(mainWnd,newCallBack(delegate(IntPtr hwnd,intlParam)            {                listWnd.Add(hwnd);returntrue;            }), 0);foreach(IntPtr iteminlistWnd)            {if(item != hwnd_button)                {char[] UserChar = "luminji".ToCharArray();foreach(charchinUserChar)                    {                        SendChar(item, ch, 100);                    }                }            }            SendMessage(hwnd_button, WM_CLICK, mainWnd, "0");        }publicvoidSendChar(IntPtr hand,charch,intSleepTime)        {            PostMessage(hand, WM_CHAR, ch, 0);            System.Threading.Thread.Sleep(SleepTime);        }publicstaticintWM_CHAR = 0x102;publicstaticintWM_CLICK = 0x00F5;        [DllImport("User32.dll", EntryPoint = "SendMessage")]publicstaticexternintSendMessage(IntPtr hWnd,intMsg, IntPtr wParam,stringlParam);          [DllImport("user32.dll")]publicstaticexternIntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter,stringlpszClass,stringlpszWindow);        [DllImport("user32.dll", SetLastError =true)]publicstaticexternIntPtr FindWindow(stringlpClassName,stringlpWindowName);        [DllImport("user32.dll")]publicstaticexternintAnyPopup();        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError =true)]publicstaticexternintGetWindowText(IntPtr hWnd, StringBuilder lpString,intnMaxCount);        [DllImport("user32.dll")]publicstaticexternintEnumThreadWindows(IntPtr dwThreadId, CallBack lpfn,intlParam);        [DllImport("user32.dll")]publicstaticexternintEnumChildWindows(IntPtr hWndParent, CallBack lpfn,intlParam);        [DllImport("user32.dll", CharSet = CharSet.Ansi)]publicstaticexternIntPtr PostMessage(IntPtr hwnd,intwMsg,intwParam,intlParam);        [DllImport("user32.dll", CharSet = CharSet.Ansi)]publicstaticexternIntPtr SendMessage(IntPtr hwnd,intwMsg, IntPtr wParam, IntPtr lParam);               [DllImport("user32.dll", CharSet = CharSet.Unicode)]publicstaticexternIntPtr SendMessageA(IntPtr hwnd,intwMsg,intwParam,intlParam);               [DllImport("user32.dll", CharSet = CharSet.Auto)]staticexternintGetClassName(IntPtr hWnd, StringBuilder lpClassName,intnMaxCount);               [DllImport("user32.dll", SetLastError =true, CharSet = CharSet.Auto)]publicstaticexternintGetWindowTextLength(IntPtr hWnd);        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError =false)]publicstaticexternIntPtr GetParent(IntPtr hWnd);publicdelegateboolCallBack(IntPtr hwnd,intlParam);

運行效果:

2:痛點:如何擷取指定的控制項控制代碼

   細心的人可能已經發現,上文中,給文字框賦值的地方,使用了如下代碼:

foreach(IntPtr iteminlistWnd)            {if(item != hwnd_button)                {char[] UserChar = "luminji".ToCharArray();foreach(charchinUserChar)                    {                        SendChar(item, ch, 100);                    }                }            }

   假設我們的表單上有多個文字框,那麼事實上,這段代碼會給所有的文字框輸入"luminji”字樣。這在多數應用程式中都是不允許的,我們需要精確定位需要控制的控制項。

   我們在得到OK按鈕的控制代碼的時候,使用了函數:

IntPtr hwnd_button = FindWindowEx(mainWnd,newIntPtr(0),null, "OK");

   而想要擷取文字框控制代碼的時候,這個函數卻不能使用,因為,所有文字框都是沒有標題的,也就是類似"OK"這個值。有人說,那就使用控制項ID吧。且看:

2.1:擷取控制項ID

   非.NET程式,一旦程式被產生,控制項ID就是固定的,所以這一招,用在非.NET程式中,那是再好也不過了。

  

   根據ID來得到控制項控制代碼的函式宣告如下:

[DllImport("user32.dll", EntryPoint = "GetDlgItem")]publicstaticexternIntPtr GetDlgItem( IntPtr hParent,intnIDParentItem);

   其中,第一個參數就是表單的控制代碼,第二個參數就是控制項ID。

   但是,顯然,這種方法不適用於我們的.NET程式,因為我們會發現,我們的.NET程式沒運行一次,這個ID是變化的。

2.2:擷取控制項位置

   所以,最終的一個方案是:根據控制項位置,人工比對後得到我們想要的控制項控制代碼。該函數的聲明如下:

好了,現在的關鍵就是怎麼取得這個控制項的位置。我們在VS中查看,某個控制項有X座標和Y座標,以上面程式的這個TextBox來說,其在VS中顯示的位置是“70,83”,但是而VS中顯示的,是不包含標題和邊框的座標值。但是這個座標值可以作為我們人工比對的參考。
更精確的座標值,我們寫代碼來實現,如下:
EnumChildWindows(mainWnd,newCallBack(delegate(IntPtr hwnd,intlParam)            {                listWnd.Add(hwnd);                StringBuilder className =newStringBuilder(126);                StringBuilder title =newStringBuilder(200);                GetWindowText(hwnd, title, 200);                RECT clientRect;                GetClientRect(hwnd,outclientRect);intcontrolWidth = clientRect.Width;intcontrolHeight = clientRect.Height;intx = 0, y = 0;                IntPtr parerntHandle = GetParent(hwnd);if(parerntHandle != IntPtr.Zero)                {                    GetWindowRect(hwnd,outclientRect);                    RECT rect;                    GetWindowRect(parerntHandle,outrect);                    x = clientRect.X - rect.X;                    y = clientRect.Y - rect.Y;                    Debug.Print(x.ToString());                    Debug.Print(y.ToString());                }returntrue;            }), 0);
    注意,上面代碼中的X和Y就是某個控制項的精確的X和Y值,記錄下來,比對一下,我們就能得到精確的座標值了。在上文的例子中,我們的文字框的座標最終得到為“78,113”。    有了這個座標值,我們便知道這個控制項的控制代碼,也就是hwnd是屬於哪個控制項的了。 2.3:根據EnumChildWindows枚舉次序得到控制代碼    如果你不想這麼麻煩,還有一種簡單的方案,那就是利用EnumChildWindows的枚舉順序。要知道,在不同的機器上,EnumChildWindows枚舉一個表單上子控制項的順序是相同的,也就是說,如果有兩個文字框,它們在這台機器上被枚舉的順序一個是2,一個是3,那麼,它們在其它機器上被枚舉的順序,也是這個固定次序。通過比對,我們也能得到它們各自的控制代碼。當然,如果我們有了這些控制代碼,還有什麼是不能做到的呢?2.4:使用SPY++

    SPY++是微軟的一個工具,使用者擷取表單上的ID或者類型或者控制代碼等資訊。因為在我們的這個例子裡,ID和控制代碼在每台機器上都是不變的,所以這個工具對於我們來說,沒有多大的用處。但是,當你HACK別人的程式的時候,它會發揮一定作用。

   

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.