Using a custom DLL in LoadRunner

Sometimes LoadRunner does not provide all the needed features that one needs when doing performance testing. One option is to write DLL’s to handle the needed stuff and writing your own custom DLL’s is really easy once you get the hang of it!

I needed to do a few HTTP calls “under the radar” from a script so using Delphi 2009 I wrote my own HTTPClient DLL that allows me to do a HTTP GET to a specific URL without adding to the statistics (Pages and Hits/Sec stats).

Creating DLL’s in Delphi is really simple. One of the things I had to keep in mind was that D2009 uses unicode internally so I had to take care to convert any internal strings to non-unicode before returning them to LR. I opted for using the Indy 10 component TIdHTTP as the base HTTP client, adding some Cookie handling and Compression support (gzip,deflate) and making sure the DLL was thread safe (as many client threads/process would be using it). I also soon realized I needed HTTP Proxy Support so in the end I added that too.

Finally had a DLL that exported the following methods:

  • http_Initialize( VUserID: Integer )
  • http_WebProxy( VUser: Integer; Host:PAnsiChar; Port: Integer )
  • http_WebGet( VUserID: Integer; URL: PAnsiChar; DestBuf: PAnsiChar; BufSize: Integer )
  • http_Finalize( VUserID: Integer )

The VUserID is the identifier for the connection since the DLL supports keep-alive. Calling Finalize() will destroy all cookies and disconnect the client from the server.

I also made a small sample script that utilizes all the methods:

vuser_init()
{
    int ret, VUserID;
    char buf[10240];

	// Get VUserID
    VUserID = atoi(lr_eval_string("{VUserID"));

	// Load the DLL
    lr_load_dll("HTTPClient.dll");

	// Initialize the VUser object inside the DLL
    http_Initialize( VUserID );

	// Set the HTTP Proxy
	http_WebProxy( VUserID, "127.0.0.1", 8080);

	// Clear buffer, and get the URL's response
	memset(buf,0,sizeof(buf));
    ret = http_WebGet( VUserID, "http://www.celarius.com", buf, sizeof(buf) );

	// Check for error
	// Returns
	//  0 != Insufficient Buffer (Returned value is needed size)
	if (ret != 0) lr_error_message("Error: RetCode=%d", ret)
	else lr_output_message("%s", buf);

	// Finalize the VUser (free the VUser object inside the DLL)
    http_Finalize( VUserID );

	return 0;
}

An additional good thing with this is that the DLL can be loaded under ANY protocol, so now HTTP calls can be made in any script type!

Click here to download the DLL


Advertisements

17 thoughts on “Using a custom DLL in LoadRunner

  1. I added a link for the download of v1.0.1.36 of the DLL. If you have the time, please let me know if you have any problems with it, if you find it useful or if there’s anything you think is missing from it (I bet there’s lots missing … ๐Ÿ™‚ )

  2. meset…brilliant, now why couldn’t have Merccury/HP had that clearly defined in the help? They were telling me to clear the buffer with free(buff); which didn’t work…but that must be for static pointer variables, and not arrays.

  3. Hi,

    When i created a simple dll (of adding numbers using vc++) and called it LR after lr_load_dll, i get Error — vuser_init.c (7): Unresolved symbol : sum. ๐Ÿ˜ฆ

    It loads ok but doesnt work. Is it mandatory to register the dll, when i tried to register i got dllregisterserverentrypoint not found…

    any help is really appreciated
    vignesh

  4. Hi,
    I am currently working in LR 9.1/9.5 to performance test a website that had a lot of duplicate code in the VUser scripts and so have created “.h” files to remove this duplication and to store and access the functions from within my scripts. (the duplication was mainly to do with site navigation and validation checks for search functionality.)

    I have 5 scripts in total that will access these libraries, so store the libraries in a separate folder alongside the script folders.
    I have the necessary #include statements in each script and this all works fine when I run the scripts in VuGen, but once I try to execute them through the Contoller in a scenario (unless running Vusers on localhost), I get compilation failures from the start, relating to the “combined_……c” file of each script (which gets generated on a compile i believe).

    I was wondering if you would be able to tell me how to go about using the “.h” files correctly in my tests which will use remote load generator machines for script execution?
    – Do I need to create “.dll” files to use them in the Scenario?
    – If so, How do I create the “.dll” files? (I don’t have a seperate C compiler, just LR, but imagine I should be able to use this if needed?)
    – Where do I need to place the dll files so that each script can access them?
    – If I don’t need to create dll files to make it work, what do I need to do to make them work correctly in my scenarios and on remote Load Generators?

    Any help would be greatly appreciated!

    Thanks
    Mark

    • For standalone controller:
      1st off, the .h include files do not need to be converted to DLL files to work on the controller.
      To use the Includes in a script on the controller the whole directory structure needs to be copied to the controller. Example:

      c:\projects\xx\Scripts\Includes
      c:\projects\xx\Scripts\Script1
      c:\projects\xx\Scripts\Script2
      c:\projects\xx\Scripts\Script3

      Then in the scripts you need to include the files with #include “..\includes\filename.h”. Note the ..\ and not a complete path here. This makes the includes load dynamically based on script location.

      For Performance Center:
      If you are using Performance Center the solution is to use “Add files to script” in vugen to have it copy the script to the local script folder. You also need to change the include statements to #include “filename.h” without any path info.

  5. Hey Kim,
    Thanks for your quick response.

    Yes it is a standalone controller that I am using, with no performance centre instance involved.
    I forgot to say in my original post that your solution was what I had been trying – i.e. copying the whole directory structure to the controller with the relative paths to the libraries in place in the script.

    For example, in the init() Action of each script, I have the following statements to include my User-Defined Function (UDF) Libraries (Note the double-backslash for C notation with the escape character ‘\’)

    //*******************************************************************************************
    #include “\\..\\UDF_Libraries\\qual_validation_checks.h”
    #include “\\..\\UDF_Libraries\\qual_general_navigation.h”

    vuser_init()
    {
    qual_login();
    return 0;
    }
    //*******************************************************************************************

    I have also tried moving these includes into the globals.h file, re-compiling and re-running the scenario, but to no avail.
    Is there some other switch or setting that needs to be altered in the Controller to allow this method to work? Would the “run vuser as a process” vs “run Vuser as a thread” setting have any effect on this? We currently run as a thread.

    As a work-around, we have placed the .h files onto the Load Generator machines in the LR\include directory and altered the #include statements accordingly. i.e. #include “qual_general_navigation.h”. This works ok.

    However it seems counter-intuitive and is also bad practice as the potential for error increases relative to the number of load generators in use – it also seems to me that maybe the compile action is taking place on the load generators and not on the controller machine if this solution works, and it does….

    Here are the error messages we get in the controller when we attempt to run with relative paths to the libraries on the controller machine:

    Error: CCI compilation error -In file included from c:\documents and settings\norbertl\local settings\temp\brr_hgt.400\global_dir\165800437\combined_QUAL_APP_Advanced_Search.c:3:

    Error: CCI compilation error -vuser_init.c:25: qual_general_navigation.h: No such file or directory

    -19799, -19800 Error codes

    The contents of the combined_QUAL_APP_Advanced_Search.c file are as follows:
    #include “lrun.h”
    #include “globals.h”
    #include “vuser_init.c”
    #include “APP_Adv_Search_SSB_Apps_Over_1_Week.c”
    #include “APP_Adv_Search_Compiler_Over_1_Week.c”
    #include “APP_Adv_Search_SSB_Count_1_Week.c”
    #include “APP_Adv_Search_Specific_App.c”
    #include “APP_Adv_Search_All_Active_Count.c”
    #include “APP_Quick_Search.c”
    #include “vuser_end.c”

    -Notice that the #includes for the 2 UDF Libraries are not present, but given that the init() is in there – which contains our #includes – I would expect it to be ok(?)

    Any thoughts or suggestions would again be very much appreciated.

    Cheers
    Mark

      • Hey Kim,

        I wasn’t able to download your example script..(?) I just received a blank page when I attempted to get it. However our Controller version is 9.10 – am wondering if maybe there were issues with this type of script architecture in this and/or earlier versions..

        Would you be able to post the contents of your script/.h up on this forum? I can then compare it to mine and try it also.

        Thanks Again
        Mark

      • That is rather strange, as I can download from 2 different locations? The direct link is http://www.celarius.com/files/testscript1.zip. Version 9.1 or v9.52 should not matter, I do believe the script works in v8.1 and up.

        In any case, here are the essentials of the script:

        1) Created a new blank HTML/HTTP script
        2) Saved it as TESTSCRIPT1 in a suitable folder
        3) Copy/Create the MyIncludeFile.h into/in the script folder
        4) Used File|Add files to script and selected the include file
        5) Change vuser_init.c as per below
        6) Compiled

        vuser_init.c

        //
        // Include the file from the SCRIPT DIRECTORY
        //
        // For this to work, the file must be added to the tree on the left with
        // the "Files | Add Files to Script" function. This makes the script retain
        // a link to the file, copying it to controllres/loadgenerators automatically
        // without any overhead for the tester
        // 
        #include "MyIncludeFile.h"
        
        vuser_init()
        {
        	lr_output_message("func1=%d, func2=%d", testfunc1(), testfunc2() );
        
        	return 0;
        }
        

        MyIncludeFile.h

        //
        // This is the custom include file
        // 
        
        int testfunc1()
        {
        	return 1;
        }
        
        int testfunc2()
        {
        	return 2;
        }
        
  6. Hi Kim,

    Apologies for not getting back to you sooner – havent been able to get at this issue over the last couple of days, running other tests took priority.
    I have made the changes that you have suggested in your comments of your vuser_init example above – the step that I was missing was adding the files to the scripts. I had relative references to the libraries in the script that pointed to the UDF_Libraries folder on the hard drive as described previously (i.e. no adding of h files to the script, just #includes to the relative folder) – which all works fine in VUGen, but not in the Controller.

    Making the change to add the files to the scripts has worked and the scripts now work on the Controller. I had been thinking that the method I was using would allow us to change the .h files on the fly and the scripts would pick up the changes using the relative paths without having to alter the script files themselves… nice thought I suppose but not the way it works ๐Ÿ™‚

    Thanks for the help, very much appreciated!!

    Cheers
    Mark

    • I’m glad to hear all worked out for you!

      The way I imagine the controller and loadgenerators work is that they receive a ZIP of the script (same as VuGen ZIP files). The files included in the zip are only those that are referred to by the script with “Add Files” or through Parameters. This ZIP thing “forgets” about the #include files and thus does not compile when attempted. Remember that this is only the way I image it works, I have no proof of this.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s