How to export/import a C struct from a DLL/ to a console application using __declspec( dllexport/import )

wotann07 picture wotann07 · Mar 11, 2013 · Viewed 13.6k times · Source

This is my first time dealing with DLLs. Following the MSDN documentation I created a header file fooExports.h with macros defined according to a preprocessor definition:

#ifdef FOODLL_EXPORTS
    #define FOO_API __declspec( dllexport )
#else
    #define FOO_API __declspec( dllimport )

My intention was to use this header both in my DLL implementation as well as in the console application. So far importing and exporting functions works just fine. The problem arrises when I try to export an already defined struct that I need as parameter for one of the exported functions. For example, in the aforementioned header file I declare FOO_API void foo( FooParams *args ) and args is a struct defined as follows:

typedef struct FooParams
{
    char *a;
    char *b;
    void *whatever; //some other type
} FooParams;

This struct has to be defined in foo.h rather than in fooExports.h. Is there any way to export this struct without taking it out of it's original header file (taking into consideration that I want to keep the exports/imports centralized in fooExports.h). What would be a better approach to doing this? The DLL is all C as well as the client application using it.

Answer

aschepler picture aschepler · Mar 11, 2013

If the only use the client will ever have for FooParams is to get pointers to it returned from DLL functions and to pass those pointers to other DLL functions, you can make it an "opaque type": Put

typedef struct FooParams FooParams;

in fooExports.h. The FOO_API macro does not belong on that declaration. An opaque type means the client code cannot:

  • Create any variables of type FooParams (but FooParams * ptr = NULL; is okay).
  • Do anything at all with any member of FooParams.
  • Find sizeof(FooParams) - and therefore cannot correctly malloc space for one or more FooParams objects.

You can't #define macros visible to the client which do any of the above, either. So your DLL would need to have one or more "constructor" or "factory" functions, maybe something like

FOO_API FooParams* CreateFooParams(const char * input);

It's also good practice to define a matching "destructor" function like

FOO_API void DestroyFooParams(FooParams * p);

even if the definition is as simple as { free(p); }, because there can sometimes be issues if memory allocated inside a DLL is freed by code outside it or vice versa (because not all Windows code uses identical definitions of malloc and free).

If all this is too extreme, the only other option is to put or #include the struct definition in the exported header file and make it visible to clients. Without that, any attempt to do something to a FooParams, other than passing pointers around, is going to be impossible because the compiler won't know what's in a FooParams. The compiler (as opposed to the linker) takes information only from commandline arguments and #include-d files, not from libraries or DLLs.