最近小研了一把軟件測試,用了兩囘LoadRunner,發現它一個測試軟件居然還可以調用DLL,灰常灰常之不可以肆意,雖不是自己所作,卻也把它貼出來,共賞之~!
場景介紹
最近在做類似於
QQ
的通信工具的
性能測試
時發現了一些問題,現總結出來與大家分享一下。希望大家在使用
LoadRunner
時不僅僅停在只是錄製
/
播放角本,而全面提升角本的編程技術,解決複雜場景。
本次測試中碰到的問題是這樣的,在消息的傳送過程中遇到了 DEC 加密的過程, LoadRunner 錄製到的全是加密的消息,比如我錄製了某一個用戶的登陸,發送消息,退出,但由於是加密的,只能單個用戶使用,但如果我想併發多少個用戶就存在很多問題,最直接的一個問題就是用戶名是加密的,密碼是加密的,當然你可以說讓程序那裏注掉加密的代碼進行明碼的測試,當然也是一種辦法。但程序組提出了要使用更真實的方法來模擬,這時就必需使用下面介紹的方法。
一開始是直接把 API 移植到 LoadRunner 中來,不過由於加密算法異常複雜,有幾層循環,而角本是解釋執行的,進行一次加密運算可能需要好幾分鐘,當然在角本裏可以把角本本身運行的時間去掉,但這樣做顯然沒有直接調用 DLL 來的效率高。由於程序組比較忙,所以無法提供 DLL 給測試,所以測試完成了 DLL 的編寫,並在 LoadRunner 中調用成功,高效的完成了用戶信息加密,參數關聯,成功的完成了測試。
動態鏈接庫的編寫
在 Visual C++6.0 開發環境下,打開 FileNewProject 選項,可以選擇 Win32 Dynamic-Link Library 建立一個空的 DLL 工程。
1 . Win32 Dynamic-Link Library 方式創建 Non-MFC DLL 動態鏈接庫
每一個 DLL 必須有一個入口點,這就象我們用 C 編寫的應用程序一樣,必須有一個 WINMAIN 函數一樣。在 Non-MFC DLL 中 DllMain 是一個缺省的入口函數,你不需要編寫自己的 DLL 入口函數,用這個缺省的入口函數就能使動態鏈接庫被調用時得到正確的初始化。如果應用程序的 DLL 需要分配額外的內存或資源時,或者說需要對每個進程或線程初始化和清除操作時,需要在相應的 DLL 工程的 .CPP 文件中對 DllMain() 函數按照下面的格式書寫。
本次測試中碰到的問題是這樣的,在消息的傳送過程中遇到了 DEC 加密的過程, LoadRunner 錄製到的全是加密的消息,比如我錄製了某一個用戶的登陸,發送消息,退出,但由於是加密的,只能單個用戶使用,但如果我想併發多少個用戶就存在很多問題,最直接的一個問題就是用戶名是加密的,密碼是加密的,當然你可以說讓程序那裏注掉加密的代碼進行明碼的測試,當然也是一種辦法。但程序組提出了要使用更真實的方法來模擬,這時就必需使用下面介紹的方法。
一開始是直接把 API 移植到 LoadRunner 中來,不過由於加密算法異常複雜,有幾層循環,而角本是解釋執行的,進行一次加密運算可能需要好幾分鐘,當然在角本裏可以把角本本身運行的時間去掉,但這樣做顯然沒有直接調用 DLL 來的效率高。由於程序組比較忙,所以無法提供 DLL 給測試,所以測試完成了 DLL 的編寫,並在 LoadRunner 中調用成功,高效的完成了用戶信息加密,參數關聯,成功的完成了測試。
動態鏈接庫的編寫
在 Visual C++6.0 開發環境下,打開 FileNewProject 選項,可以選擇 Win32 Dynamic-Link Library 建立一個空的 DLL 工程。
1 . Win32 Dynamic-Link Library 方式創建 Non-MFC DLL 動態鏈接庫
每一個 DLL 必須有一個入口點,這就象我們用 C 編寫的應用程序一樣,必須有一個 WINMAIN 函數一樣。在 Non-MFC DLL 中 DllMain 是一個缺省的入口函數,你不需要編寫自己的 DLL 入口函數,用這個缺省的入口函數就能使動態鏈接庫被調用時得到正確的初始化。如果應用程序的 DLL 需要分配額外的內存或資源時,或者說需要對每個進程或線程初始化和清除操作時,需要在相應的 DLL 工程的 .CPP 文件中對 DllMain() 函數按照下面的格式書寫。
(我注:熟悉DLL編程的同學這段就不必看了)
[WinSock]
ExtPriorityType=protocol
WINNT_EXT_LIBS=wsrun32.dll
WIN95_EXT_LIBS=wsrun32.dll
LINUX_EXT_LIBS=liblrs.so
SOLARIS_EXT_LIBS=liblrs.so
HPUX_EXT_LIBS=liblrs.sl
AIX_EXT_LIBS=liblrs.so
LibCfgFunc=winsock_exten_conf
UtilityExt=lrun_api
ExtMessageQueue=0
ExtCmdLineOverwrite=-WinInet No
ExtCmdLineConc=-UsingWinInet No
WINNT_DLLS=user_dll1.dll, user_dll2.dll, ...
//最後一行是加載你需要的DLL 參數中, hMoudle 是動態庫被調用時所傳遞來的一個指向自己的句柄 ( 實際上,它是指向 _DGROUP 段的一個選擇符 ) ; ul_reason_for_call 是一個說明動態庫被調原因的標誌,當進程或線程裝入或卸載動態鏈接庫的時候,操作系統調用入口函數,並說明動態鏈接庫被調用的原因,它所有的可能值為: DLL_PROCESS_ATTACH: 進程被調用、 DLL_THREAD_ATTACH: 線程被調用、 DLL_PROCESS_DETACH: 進程被停止、 DLL_THREAD_DETACH: 線程被停止; lpReserved 為保留參數。到此為止, DLL 的入口函數已經寫了,剩下部分的實現也不難,你可以在 DLL 工程中加入你所想要輸出的函數或變量了。
我們已經知道 DLL 是包含若干個函數的庫文件,應用程序使用 DLL 中的函數之前,應該先導出這些函數,以便供給應用程序使用。要導出這些函數有兩種方法,一是在定義函數時使用導出關鍵字 _declspec(dllexport) ,另外一種方法是在創建 DLL 文件時使用模塊定義文件 .Def 。需要讀者注意的是在使用第一種方法的時候,不能使用 DEF 文件。下面通過兩個例子來說明如何使用這兩種方法創建 DLL 文件。
1 )使用導出函數關鍵字 _declspec(dllexport) 創建 MyDll.dll ,該動態鏈接庫中有兩個函數,分別用來實現得到兩個數的最大和最小數。在 MyDll.h 和 MyDLL.cpp 文件中分別輸入如下原代碼: 該動態鏈接庫編譯成功後,打開 MyDll 工程中的 debug 目錄,可以看到 MyDll.dll 、 MyDll.lib 兩個文件。 LIB 文件中包含 DLL 文件名和 DLL 文件中的函數名等,該 LIB 文件只是對應該 DLL 文件的 " 映像文件 " ,與 DLL 文件中, LIB 文件的長度要小的多,在進行隱式鏈接 DLL 時要用到它。讀者可能已經注意到在 MyDll.h 中有關鍵字 "extern C" ,它可以使其他編程語言訪問你編寫的 DLL 中的函數。
LoadRunner 調用動態鏈接庫
上面完成動態鏈接庫開發後,下面就介紹動態鏈接庫如何被 LoadRunner 進行調用,其實也是很簡單的。在 LoadRunner 中的 DLL 調用有局部調用與全局調用,下面介紹局部調用。
首先把你編譯的 DLL 放在角本路徑下面,這裏是 MyDll.dll,MyDll.lib. 然後在 Action 中使用
lr_load_dll("MYDll.dll") ,此函數可以把 DLL 加載進來,讓你調用 DLL 裏面的函數,而 DLL 中的運算是編譯級的,所以效率極高,代碼樣例如下: 運行結果
比較的結果為 8
全局的動態鏈接庫的調用則需要修改 mdrv.dat ,路徑在 LoadRunner 的安裝目錄下麵( LoadRunner/dat directory );在裏面修改如例:
BOOL APIENTRY DllMain(HANDLE hModule,DWORD ul_reason_for_call,LPVOID lpReserved)
...
{
switch( ul_reason_for_call )...{
case DLL_PROCESS_ATTACH:
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
default:
break;
}
return TRUE;
}
switch( ul_reason_for_call )...{
case DLL_PROCESS_ATTACH:
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
default:
break;
}
return TRUE;
}
//
MyDLL.h
extern " C " _declspec(dllexport) int desinit( int mode);
extern " C " _declspec(dllexport) void desdone( void );
extern " C " _declspec(dllexport) void des_setkey( char * subkey, char * key);
extern " C " _declspec(dllexport) void endes( char * block, char * subkey);
extern " C " _declspec(dllexport) void dedes( char * block, char * subkey);
// MyDll.cpp
#include " MyDll.h "
// 這裏我用了比較大小的函數代替了我要實現的函數
int desinit( int a, int b) ... {
if(a>=b)...{
return a;
}else...{
return b;
}
}
int desdone( int a, int b) ... {
if(a>=b)...{
return b;
}else...{
return a;
}
}
extern " C " _declspec(dllexport) int desinit( int mode);
extern " C " _declspec(dllexport) void desdone( void );
extern " C " _declspec(dllexport) void des_setkey( char * subkey, char * key);
extern " C " _declspec(dllexport) void endes( char * block, char * subkey);
extern " C " _declspec(dllexport) void dedes( char * block, char * subkey);
// MyDll.cpp
#include " MyDll.h "
// 這裏我用了比較大小的函數代替了我要實現的函數
int desinit( int a, int b) ... {
if(a>=b)...{
return a;
}else...{
return b;
}
}
int desdone( int a, int b) ... {
if(a>=b)...{
return b;
}else...{
return a;
}
}
#include
"
lrs.h
"
Action()
... {
//
int nRet = 6;
char srckey[129];
memset(srckey, 'a', 128);
lr_message(lr_eval_string(srckey));
lr_load_dll("MyDLL.dll");
nRet = desinit(5,8);
lr_message("比較的結果為%d",nRet);
return 0;
}
Action()
... {
//
int nRet = 6;
char srckey[129];
memset(srckey, 'a', 128);
lr_message(lr_eval_string(srckey));
lr_load_dll("MyDLL.dll");
nRet = desinit(5,8);
lr_message("比較的結果為%d",nRet);
return 0;
}
這樣你就可以在LR中隨意的調用程序員寫的API函數,進行一些複雜的數據加密,準備的一些操作,進行複雜的測試。同時如果你覺的有大量高複雜的運算也可以放在DLL中進行封裝,以提高效率。
ExtPriorityType=protocol
WINNT_EXT_LIBS=wsrun32.dll
WIN95_EXT_LIBS=wsrun32.dll
LINUX_EXT_LIBS=liblrs.so
SOLARIS_EXT_LIBS=liblrs.so
HPUX_EXT_LIBS=liblrs.sl
AIX_EXT_LIBS=liblrs.so
LibCfgFunc=winsock_exten_conf
UtilityExt=lrun_api
ExtMessageQueue=0
ExtCmdLineOverwrite=-WinInet No
ExtCmdLineConc=-UsingWinInet No
WINNT_DLLS=user_dll1.dll, user_dll2.dll, ...
//最後一行是加載你需要的DLL 參數中, hMoudle 是動態庫被調用時所傳遞來的一個指向自己的句柄 ( 實際上,它是指向 _DGROUP 段的一個選擇符 ) ; ul_reason_for_call 是一個說明動態庫被調原因的標誌,當進程或線程裝入或卸載動態鏈接庫的時候,操作系統調用入口函數,並說明動態鏈接庫被調用的原因,它所有的可能值為: DLL_PROCESS_ATTACH: 進程被調用、 DLL_THREAD_ATTACH: 線程被調用、 DLL_PROCESS_DETACH: 進程被停止、 DLL_THREAD_DETACH: 線程被停止; lpReserved 為保留參數。到此為止, DLL 的入口函數已經寫了,剩下部分的實現也不難,你可以在 DLL 工程中加入你所想要輸出的函數或變量了。
我們已經知道 DLL 是包含若干個函數的庫文件,應用程序使用 DLL 中的函數之前,應該先導出這些函數,以便供給應用程序使用。要導出這些函數有兩種方法,一是在定義函數時使用導出關鍵字 _declspec(dllexport) ,另外一種方法是在創建 DLL 文件時使用模塊定義文件 .Def 。需要讀者注意的是在使用第一種方法的時候,不能使用 DEF 文件。下面通過兩個例子來說明如何使用這兩種方法創建 DLL 文件。
1 )使用導出函數關鍵字 _declspec(dllexport) 創建 MyDll.dll ,該動態鏈接庫中有兩個函數,分別用來實現得到兩個數的最大和最小數。在 MyDll.h 和 MyDLL.cpp 文件中分別輸入如下原代碼: 該動態鏈接庫編譯成功後,打開 MyDll 工程中的 debug 目錄,可以看到 MyDll.dll 、 MyDll.lib 兩個文件。 LIB 文件中包含 DLL 文件名和 DLL 文件中的函數名等,該 LIB 文件只是對應該 DLL 文件的 " 映像文件 " ,與 DLL 文件中, LIB 文件的長度要小的多,在進行隱式鏈接 DLL 時要用到它。讀者可能已經注意到在 MyDll.h 中有關鍵字 "extern C" ,它可以使其他編程語言訪問你編寫的 DLL 中的函數。
LoadRunner 調用動態鏈接庫
上面完成動態鏈接庫開發後,下面就介紹動態鏈接庫如何被 LoadRunner 進行調用,其實也是很簡單的。在 LoadRunner 中的 DLL 調用有局部調用與全局調用,下面介紹局部調用。
首先把你編譯的 DLL 放在角本路徑下面,這裏是 MyDll.dll,MyDll.lib. 然後在 Action 中使用
lr_load_dll("MYDll.dll") ,此函數可以把 DLL 加載進來,讓你調用 DLL 裏面的函數,而 DLL 中的運算是編譯級的,所以效率極高,代碼樣例如下: 運行結果
比較的結果為 8
全局的動態鏈接庫的調用則需要修改 mdrv.dat ,路徑在 LoadRunner 的安裝目錄下麵( LoadRunner/dat directory );在裏面修改如例: