• 用Delphi创建DLL在LoadRunner中使用


    参考:

    Creating a DLL with Delphi for LoadRunner

    http://ptfrontline.wordpress.com/2010/04/13/creating-a-dll-with-delphi-for-loadrunner/

    This article extends my previous post on Using a Custom DLL in LoadRunner and now shows by example how a DLL is actually created in Delphi and used from LoadRunner.

    The DLL I’ll create here is a simple stub with a few basic functions and can serve as a template for any type of DLL that you want to create.

    I’ll show how to avoid the most common problems of sending/retrieving strings to/from the DLL as well as how to handle the Unicode situation with D2009 and D2010 (LR is not Unicode enabled). 

    Some of the things a LR DLL developer needs to remember are:

    • LR is Muti-Threaded and Multi-processed, make the DLL thread-safe
    • When using LoadGenerators the DLL must be distributed to several machines
    • When using Performance Center the lr_load_dll() function needs to be white-listed on every LoadGenerator. The file to manipulate is “C:\Program Files\HP\Load Generator\merc_asl\lrun_api.asl”
    • The DLL may be unloaded at any time, if the script fails/aborts (=> YOU must release allocated memory)
    • If eternal loops or lockups inside the DLL occur, the whole controller/loadgenerator is lost until rebooted (really bad when under PerfCenter)

    Keeping the above in mind, also remember the following general rules

    • LR script makers make mistakes too by sending INVALID input to functions or using them in an unintended way. This means that you as a DLL developer NEED TO VERIFY the input params for every function call!
    • The DLL may be installed on a variety of windows platforms (Win2000->Win7) so make sure you check the OS Version if you are using Windows API calls that are version dependent
    • There is no guarantee that the DLL has Administrator privilidges!
    • The DLL is restricted to the same Win32 memory as the host process (2 Gig max allocation)

    Creating the DLL in Delphi

    Close all projects and then choose “FILE | OTHER …“, select “DELPHI PROJECTS” and then “Dynamic Link Library“. You should now have the following skeleton (excluding all comments):

    1 library Project1;
    2 uses
    3   SysUtils,
    4   Classes;
    5 {$R *.res}
    6 begin
    7 end.

    Now copy & paste the following instead of the above:

    001 library MyDLL;
    002   
    003 uses
    004   SysUtils, Classes, Windows,
    005   VUManager in 'VUManager.pas';
    006   
    007 Const
    008   { Version Constants }
    009   DLLVer_Major                 = 1;
    010   DLLVer_Minor                 = 0;
    011   DLLVer_Copyright             = 'Copyright (C) by Celarius Oy';
    012   
    013   { DLL Errors - Negatives are errors }
    014   ERR_None                     =  0;
    015   ERR_Unknown                  = -1;
    016   ERR_Unknown_Object           = -2;
    017   ERR_Insufficient_Buffer      = -3;
    018   
    019 ////////////////////////////////////////////////////////////////////////////////
    020   
    021 Var
    022   VUMgr : TVUManager;
    023   
    024 {$R *.res}
    025   
    026 ////////////////////////////////////////////////////////////////////////////////
    027   
    028 function dll_Initialize( VUserID: Cardinal ): Integer; stdcall;
    029 // Initialize DLL, Adds the Vuser with the ID
    030 //
    031 // Returns
    032 //    Integer    The Error code
    033 begin
    034      If VUMgr.AddVUser( VUserID ) then
    035         Result := ERR_None
    036      Else
    037         Result := ERR_Unknown_Object;
    038 end;
    039   
    040 ////////////////////////////////////////////////////////////////////////////////
    041   
    042 function dll_Finalize( VUserID: Cardinal ): Integer; stdcall;
    043 // Finalize DLL, removes the VUser with the ID
    044 //
    045 // Returns
    046 //    Integer    Error code
    047 begin
    048      If VUMgr.RemoveVUser( VUserID ) then
    049         Result := ERR_None
    050      Else
    051         Result := ERR_Unknown_Object;
    052 end;
    053   
    054 ////////////////////////////////////////////////////////////////////////////////
    055   
    056 function dll_GetVersion: Integer; stdcall;
    057 // Get Library version
    058 //
    059 // Returns
    060 //    Integer    Version Number
    061 begin
    062      Result := (DLLVer_Major SHL 16) + DLLVer_Minor;
    063 end;
    064   
    065 ////////////////////////////////////////////////////////////////////////////////
    066   
    067 function dll_GetCopyright( DestStr: PAnsiChar; DestSize: Integer): Integer; stdcall;
    068 // Get Library Copyright String
    069 //
    070 // Returns
    071 //    Integer    Version Number
    072 begin
    073      if (DestSize>Length(DLLVer_Copyright)) then
    074      Begin
    075           StrCopy( DestStr, PAnsiChar(DLLVer_Copyright) );
    076           Result := ERR_None;
    077      End Else Result := ERR_Insufficient_Buffer;
    078 end;
    079   
    080 ////////////////////////////////////////////////////////////////////////////////
    081   
    082 function dll_TrimStr(ID:Cardinal; SourceStr: PAnsiChar; DestStr: PAnsiChar; DestSize: Integer): Integer; stdcall;
    083 // Trims a String (removes special chars in beginning/end)
    084 //
    085 // Returns
    086 //    Integer    Error Code
    087 Var
    088    VU : TVirtualUser;
    089    S  : AnsiString;
    090 begin
    091      { Find the Virtual User }
    092      VU := VUMgr.FindVU(ID);
    093      if Assigned(VU) then
    094      Begin
    095           S := VU.TrimString( SourceStr ); // process the string
    096   
    097           if (DestSize>Length(S)) then
    098           Begin
    099                StrCopy( DestStr, PAnsiChar(S) );
    100                Result := ERR_None;
    101           End Else Result := ERR_Insufficient_Buffer;
    102   
    103      End Else Result := ERR_Unknown_Object;
    104 end;
    105   
    106 ////////////////////////////////////////////////////////////////////////////////
    107   
    108 { Export functions }
    109 exports
    110   { General Functions }
    111   dll_Initialize       name 'dll_Initialize',
    112   dll_Finalize         name 'dll_Finalize',
    113   dll_GetVersion       name 'dll_GetVersion',
    114   dll_GetCopyright     name 'dll_GetCopyright',
    115   dll_TrimStr          name 'dll_TrimStr'
    116   ;
    117   
    118 ////////////////////////////////////////////////////////////////////////////////
    119   
    120 procedure DLLEntryProc(EntryCode: integer);
    121 //
    122 // DLL Entry/Exit, Process and Thread Attach/Detach procedures }
    123 //
    124 Var
    125    Buf           : Array[0..MAX_PATH] of Char;
    126    LoaderProcess : String;
    127    DLLPath       : String;
    128 begin
    129      Try
    130         Case EntryCode of
    131              { The DLL has just been loaded with LoadLibrary()               }
    132              { either by the main process, or by a vuser - we do not know    }
    133              { which one                                                     }
    134              DLL_PROCESS_ATTACH:
    135              Begin
    136                   { Get the Path where the DLL is }
    137                   GetModuleFileName(HInstance, buf, Length(buf)) ;
    138                   DLLPath := ExtractFilePath(StrPas(buf));
    139                   { Get the name of the module that loaded us }
    140                   GetModuleFileName(HInstance, buf, Length(buf)) ;
    141                   LoaderProcess := StrPas(buf);
    142                   { Create handler }
    143                   VUMgr:= TVUManager.Create( NIL );
    144              End;
    145   
    146              { The Process Detached the DLL - Once per MMDRV Process }
    147              { Application called Unload - Perform Cleanup }
    148              DLL_PROCESS_DETACH:
    149              Begin
    150                   If Assigned(VUMgr) then FreeAndNil(VUMgr);
    151              End;
    152   
    153              { A Thread has just loaded the DLL }
    154              DLL_THREAD_ATTACH:
    155              Begin
    156                   { This occures when a VUser calls lr_load_dll() }
    157              End;
    158   
    159              { A Thread has just unloaded the DLL }
    160              DLL_THREAD_DETACH:
    161              Begin
    162                   { This occures when a VUser exits }
    163              End;
    164         End;
    165      Except
    166         // TODO: Write an Exception Handler
    167         On E: Exception do ;
    168      End;
    169 end;
    170   
    171 ////////////////////////////////////////////////////////////////////////////////
    172 //
    173 // DLL Initialization - When DLL is loaded the first time by any process }
    174 //
    175 ////////////////////////////////////////////////////////////////////////////////
    176 begin
    177 {$IFDEF Debug}
    178           { True means Delphi checks & reports memleaks }
    179           ReportMemoryLeaksOnShutdown := True;
    180 {$ENDIF}
    181           { Make the Memory-Manager aware that this DLL is used in Thread-environments }
    182           IsMultiThread := True;
    183           VUMgr:= NIL;
    184   
    185           { If we have not already set te DLLProc, do it }
    186           if NOT Assigned(DllProc) then
    187           begin
    188                DLLProc := @DLLEntryProc;         // install custom exit procedure
    189                DLLEntryProc(DLL_PROCESS_ATTACH);
    190           end;
    191 end.

    You can now save the project as “MyDLL” in a folder of your choice.

    Now we’ll need to create the VUManager unit with “FILE | NEW UNIT” and save the new unit as “VUManager.pas” in the same directory as the rest of the Delphi files. Copy & Paste the following into the new unit.

    001 unit VUManager;
    002   
    003 Interface
    004   
    005 Uses SysUtils, Classes, Windows;
    006   
    007 Type
    008   { Forward Declarations }
    009   TVirtualUser = class;
    010   TVUManager = class;
    011   
    012 ////////////////////////////////////////////////////////////////////////////////
    013   
    014   TVirtualUser = class(TComponent)
    015   private
    016     { Private declarations }
    017   protected
    018     { Protected declarations }
    019   public
    020     { Public declarations }
    021     VUserID               : Cardinal;
    022     Constructor Create(AOwner:TComponent; ID:Cardinal); ReIntroduce;
    023     Destructor Destroy; Override;
    024     Function TrimString(InStr: AnsiString): AnsiString;
    025   end;
    026   
    027 ////////////////////////////////////////////////////////////////////////////////
    028   
    029   TVUManager = class(TComponent)
    030   private
    031     { Private declarations }
    032     fVirtualUsers          : TThreadList;
    033   protected
    034     { Protected declarations }
    035   public
    036     { Public declarations }
    037     Constructor Create(AOwner:TComponent); Override;
    038     Destructor Destroy; Override;
    039     { Methods }
    040     Function FindVU(ID:Cardinal):TVirtualUser;
    041     Function AddVUser(ID: Cardinal): Boolean;
    042     Function RemoveVUser(ID: Cardinal): Boolean;
    043   end;
    044   
    045 ////////////////////////////////////////////////////////////////////////////////
    046   
    047 Implementation
    048   
    049 ////////////////////////////////////////////////////////////////////////////////
    050   
    051 { TVirtualUser }
    052   
    053 constructor TVirtualUser.Create(AOwner: TComponent; ID:Cardinal);
    054 begin
    055      inherited Create(AOwner);
    056      VUserID := ID;
    057 end;
    058   
    059 destructor TVirtualUser.Destroy;
    060 begin
    061      Try
    062         { Cleanup anything that is allocated by the VUser }
    063      Finally
    064         inherited;
    065      End;
    066 end;
    067   
    068 function TVirtualUser.TrimString(InStr: AnsiString): AnsiString;
    069 begin
    070      Result := Trim(InStr);
    071 end;
    072   
    073 ////////////////////////////////////////////////////////////////////////////////
    074   
    075 { TVUManager }
    076   
    077 constructor TVUManager.Create(AOwner: TComponent);
    078 begin
    079      Try
    080         inherited;
    081         fVirtualUsers  := TThreadList.Create();
    082      Finally
    083      End;
    084 end;
    085   
    086 destructor TVUManager.Destroy;
    087 Var
    088    I : Integer;
    089 begin
    090      Try
    091         { Clear and Free the VUsers List }
    092         If Assigned(fVirtualUsers) then
    093         Begin
    094              With fVirtualUsers.LockList do
    095              Try
    096                 for I := 0 to Count - 1 do
    097                     TVirtualUser(Items[I]).Free;
    098                 Clear;
    099              Finally
    100                 fVirtualUsers.UnLockList;
    101              End;
    102              FreeAndNil(fVirtualUsers);
    103         End;
    104      Finally
    105         inherited;
    106      End;
    107 end;
    108   
    109 function TVUManager.FindVU(ID: Cardinal): TVirtualUser;
    110 Var
    111    I : Cardinal;
    112 begin
    113      Result := NIL;
    114      With fVirtualUsers.LockList do
    115      Try
    116         for I := 0 to Count - 1 do
    117         Begin
    118              if TVirtualUser(Items[I]).VUserID=ID then
    119              Begin
    120                   Result := TVirtualUser(Items[I]);
    121                   Break;
    122              End;
    123         End;
    124      Finally
    125         fVirtualUsers.UnlockList;
    126      End;
    127 end;
    128   
    129 function TVUManager.AddVUser(ID: Cardinal): Boolean;
    130 Var
    131    VU : TVirtualUser;
    132 begin
    133      Result := False;
    134      With fVirtualUsers.LockList do
    135      Try
    136         VU := TVirtualUser.Create(NIL, ID);
    137         Add( VU );
    138         Result := True;
    139      Finally
    140         fVirtualUsers.UnlockList;
    141      End;end;
    142   
    143 function TVUManager.RemoveVUser(ID: Cardinal): Boolean;
    144 Var
    145    I : Cardinal;
    146    VU : TVirtualUser;
    147 begin
    148      Result := False;
    149      With fVirtualUsers.LockList do
    150      Try
    151         VU := NIL;
    152         for I := 0 to Count - 1 do
    153             if TVirtualUser(Items[I]).VUserID = ID then
    154             Begin
    155                  VU := TVirtualUser(Items[I]);
    156                  Delete(I);
    157                  Break;
    158             End;
    159      Finally
    160         fVirtualUsers.UnlockList;
    161         if Assigned(VU) then
    162         Begin
    163              FreeAndNil(VU);
    164              Result := True;
    165         End;
    166      End;
    167 end;
    168   
    169 // END OF SOURCE
    170 End.

    An short explanation of what’s going on …

    The main project file contains the functions that are exported by the DLL. These are available to the LR script. These in turn call methods from the VUManager object where the actual magic of the DLL will happen.

    The VUManager has several useful methods (Find/Add/Remove vuser) and then the TVirtualUser object contains the actual methods that perform VUser specific stuff. To extend the DLL one can add methods to the TVirtualUser object and create a small Export interface in the main project file for the method (see the dll_TrimStr() for more details).

    The dll_GetVersion() and dll_GetCopyright() functions are there only to provide the script with details of the DLL, and are not mandatory to use when a script uses the DLL.

    The dll_Initialize() function

    This function is responsible for allocating an instance of a VUser in the VUManager object. It accepts the VUserID that is a unique identifier across all running vusers in a test.

    The dll_Finalize() function

    The finalize function deallocates (or frees) the VUser instance in the VUManager object. It accepts the VUserID. After this call any dll functions that depend on a valid VUserID will fail with the corresponding error code.

    The dll_TrimStr() function

    The TrimStr function demonstrates how to “read” a string sent from LoadRunner, manipulate it and then return it to LoadRunner. The PAnsiChar is important here to make Delphi handle the string as an 8-bit null-terminated string, instead of a Unicode string.

    How LoadRunner uses the DLL (in theory)

    As soon as the MMDRV.EXE process loads the DLL, the DLL creates the VUManager object. This object is responsible for “Managing” the actual VUsers we are going to be servicing. Each VUser Thread also “loads” the DLL but in reality it only gets a copy of it in memory (this is not 100% accurate but good enough for now).

    The actual LR script then uses the dll_Initialize(VUserID) function to initialize a VUser in the VUManager, that will allocate an TVirtualUser object for it, with the given ID. This way we can “keep track” of the individual VUsers later when needed. The dll_TrimStr() function takes the ID as the first parameter to identify the VUser.

    A LoadRunner script using the DLL

    The vuser_init() action:

    01 int VuserID; // Script Global Variable
    02 vuser_init()
    03 {
    04     int ret;
    05   
    06     // Get VUserID (Needs to be created as parameter)
    07     VUserID = atoi(lr_eval_string("{VUserID"));
    08   
    09     // Load the DLL
    10     ret = lr_load_dll("MyDLL.dll");
    11     if (!ret==0)
    12     {
    13         lr_error_message("Could not load DLL");
    14         lr_abort;
    15     }
    16   
    17     // Initialize the VUser object inside the DLL
    18     dll_Initialize( VUserID );
    19   
    20     return 0;
    21 }

    The Action() action:

    01 Action()
    02 {
    03     char buf[1024];
    04   
    05     // Trim a string
    06     dll_TrimStr( VUserID, "  this string will be trimmed!!  ", buf, sizeof(buf) );
    07   
    08     lr_output_message("Trimmed String='%s'", buf);
    09   
    10     return 0;
    11 }

    The vuser_end() action:

    1 vuser_end()
    2 {
    3     // Finalize the VUser (free the VUser object inside the DLL)
    4     dll_Finalize( VUserID );
    5   
    6     return 0;
    7 }

    I hope this brief introduction to DLL writing for LoadRunner is of help to those who seek information regarding this subject.

    Enjoy!

  • 相关阅读:
    js实现H5、触摸屏数字键盘
    MAC 下node.js初体验 开发环境搭建
    手动搭建一个完整的angular实践项目
    js实现H5页面手指滑动刻度尺
    JavaScript中的事件循环机制
    CentOS6.5安装教程
    通过ecplise导入mysql的jar包时,右键找不到build path问题
    Java数据库之数据库的连接操作
    Java基础之文件的输入输出流操作
    数据库约束
  • 原文地址:https://www.cnblogs.com/preftest/p/2053893.html
Copyright © 2020-2023  润新知