Thursday 12 September 2013

How To Build Windows Static And Dynamic Link Libraries With The Microsoft Compiler

Creating a C/C++ statically linked library is easy : compile the library code to object code (without creating an executable) then call the lib utility. In this case the same header file that declares the function can be used for both the library and the application.
Creating a dynamic linked library is a little bit more involved because the header file needs to be able to declare the functions for both "export" (from the DLL) and "import" (into the application). In this case the functions must be declared differently depending on whether they are being used in the library or the application.
Using some simple conditional compilation techniques allows the library functions to be written in a way that will support both static and dynamic linking.

In this example I am going to implement a common DSP function, vector scalar multiplication and call the function VectorScalarMultiply().

Using floating point data this function has the following declaration :

void VectorScalarMultiply (const double *,  /* Pointer to source array */
    const double,                           /* Multiplier */
    double *,                               /* Pointer to destination array */
    const long);                            /* Array length */

There are several things we can do to improve this function and the first is to make it portable to use different data types. We will use a typedef for this and the following code shows how to declare the typedef for double precision floating point numbers :

typedef double  Data_t;                 /* Data type */

Now, if we want to change the data type of the function all we need to do is change the typedef.

Secondly we can write this function declaration so that it is compilable into both static and dynamic libraries, as described above. For this we will use the following conditional compilation code :

#if defined (_MSC_VER)                  /* Defined by Microsoft compilers */
#include <windows.h>                    /* Required for Windows applications */

#if defined (STATIC_LIB)                /* Create statically linked library */
#define FUNC_DECL                       /* Not used with statically linked library */
#else                                   /* Create dynamically linked  library */
#if defined (DLL_SOURCE)                /* Defined on command line, if rebuilding DLL */
#define FUNC_DECL       __declspec(dllexport) WINAPI    /* DLL export function - used in DLL source */
#else
#define FUNC_DECL       __declspec(dllimport) WINAPI    /* DLL import function - used in Application */
#endif      // DLL_SOURCE
#endif      // STATIC_LIB
#endif      // _MSC_VER

In the above code, we will use a declaration on the compiler command line for the following values (-D is the Microsoft command line switch):
-DSTATIC_LIB=1
This is used to tell the compiler that the function is being compiled into a static library and the result is that the value FUNC_DECL becomes undeclared.
-DDLL_SOURCE=1
This is used to tell the compiler that the function is being compiled into a dynamic library and the result is that the value FUNC_DECL becomes "__declspec(dllexport) WINAPI".
The other option (if neither of these is declared) is : "__declspec(dllimport) WINAPI", which is used to include the header into the application.

This results in the following declaration :

void FUNC_DECL VectorScalarMultiply (const Data_t *,    /* Pointer to source array */
    const Data_t,                           /* Multiplier */
    Data_t *,                               /* Pointer to destination array */
    const long);                            /* Array length */

So the complete header file (which we will call Multiply.h) is as follows and we have defined a constant PI so that we can use it in the test application later on :

#define PI      3.141592

typedef double  Data_t;                 /* Data type */

#if defined (_MSC_VER)                  /* Defined by Microsoft compilers */
#include <windows.h>                    /* Required for Windows applications */

#if defined (STATIC_LIB)                /* Create statically linked library */
#define FUNC_DECL                       /* Not used with statically linked library */
#else                                   /* Create dynamically linked  library */
#if defined (DLL_SOURCE)                /* Defined on command line, if rebuilding DLL */
#define FUNC_DECL       __declspec(dllexport) WINAPI    /* DLL export function - used in DLL source */
#else
#define FUNC_DECL       __declspec(dllimport) WINAPI    /* DLL import function - used in Application */
#endif      // DLL_SOURCE
#endif      // STATIC_LIB
#endif      // _MSC_VER

#if defined (SWIG)                      /* Is this header included by SWIG ? */
#define FUNC_DECL
#endif

void FUNC_DECL VectorScalarMultiply (const Data_t *,    /* Pointer to source array */
    const Data_t,                           /* Multiplier */
    Data_t *,                               /* Pointer to destination array */
    const long);                            /* Array length */

Now we need to write the function, as follows, which we will put in a source file called Multiply.c :

#include "Multiply.h"

void FUNC_DECL VectorScalarMultiply (const Data_t * pSrc,
    const Data_t Multiplier,
    Data_t * pDst,
    const long SampleLength)

{
    register long   i;

    for (i = 0; i < SampleLength; i++)
    {
        *pDst++ = *pSrc++ * Multiplier;
    }

}       /* End of VectorScalarMultiply() */

This includes FUNC_DECL, for using the function in a static or dynamic link library. This also includes the data type declared as Data_t.

Finally we need to write a test application (Test.c), as follows :

#include "Multiply.h"
#include <stdio.h>

#define ARRAY_LENGTH    5L

void main (void)

{
    Data_t  a[] = {1.0, 2.0, 3.0, 4.0, 5.0};
    Data_t  b[ARRAY_LENGTH];
    long     i;

    VectorScalarMultiply (a, PI, b, ARRAY_LENGTH);

    for (i = 0; i < ARRAY_LENGTH; i++)
    {
        printf ("b[%ld] = %lf\n", i, b[i]);
    }
}

Now all we need to do is compile and test our library and application. The appropriate command lines are as follows :

Static Linked Library :
To build the library :
cl /c -DSTATIC_LIB=1 Multiply.c
To add the module into the library (called Multiply.lib) :
lib Multiply.obj
To build the application :
cl -DSTATIC_LIB=1 Test.c Multiply.lib

Dynamic Linked Library :
To build the library :
cl /LD -DDLL_SOURCE=1 Multiply.c
This creates a dll called Multiply.dll and a library called Multiply.lib. The lib is a wrapper for the dll. It is entirely possible to access Multiply.dll directly from a C application but this is the easiest method.
To build the test application :
cl Test.c Multiply.lib

Now just execute Test.exe and the application should call the library function.

If you have found this solution useful then please do hit the Google (+1) button so that others may be able to find it as well.
Numerix-DSP Libraries : http://www.numerix-dsp.com/eval/

No comments:

Post a Comment