• ### Latest Published Articles

• #### Casual Connect 2018 Coverage

Beth Feldman GameDev.net's coverage of Casual Connect 2018 from Anaheim, CA.
• 287 views
• #### Postmortem: I Am Overburdened, Recaps and Numbers

Spidi provides a fully detailed breakdown of the development and business results of the release of "I Am Overburdened".
• 497 views
• #### A LinkedIn Profile for Job Hunting and Networking

Marc Mencher GameRecruiter http://www.gamerecruiter.com Marc Mencher is founder and CEO of GameRecruiter and author of Get in the Game!, an instructional book on building a career in the video game industry. In this first in a series of articles on managing your career in the games industry, Marc offers advice on creating a LinkedIn profile for job hunting and networking.
• 1 comment
• 1260 views
• #### Android Debugging with Visual Studio Code

Eric Shaw Learn how to use Visual Studio code to debug native Android applications.
• 1 comment
• 1966 views
• #### 3 Game Design Mindsets

Cody Red http://www.xnahub.com Unless you are tracking, planning, and hitting your KPIs (the only things that matter in the initial phase), you’ll easily get sidetracked, overwhelmed, start looking at the wrong things, make bad design decisions, and eventually, lose focus.
• 456 views
• # Using Interfaces with Dlls

General and Gameplay Programming

Before we start, you should be aware that all DLLs can have a DllMain() implementation. This is similar to WinMain, or main() in terms that its the entry point function for the DLL. The Operating System automatically calls this if its defined whenever the DLL is loaded, freed or when any threads attach or detach to it. If all you need is to make the DLL aware of any of these events, then this is all you should need. Usually however, this function doesn't lend itself to provide any other use apart from getting the DLL to handle the aforementioned events. To get more functionality out of a DLL, the programmer is mostly better off exporting his own set of functions. There are two methods to using DLLs from client code. The client can make use of loadtime linking or runtime linking.

In loadtime linking the OS automatically loads the DLL for you when the program is starting up. However this requires that the client code be linked to the .lib file (library file) provided with the DLL during compilation. The .lib file defines all the items that the DLL exports. These may include normal C-style functions, or even classes. All the client code needs to do is link to the .lib file and include the header provided by the DLL and the OS will automatically load everything for you. As you can see, this method seems very easy to use, since everything is transparent. However it introduces dependencies so that the client code will have to be recompiled each time the DLL code changes and generates a new .lib file. This may or may not be a concern for your project. The DLL can define functions it wants to export using two methods. The standard way is to use .def files. The .def file of a DLL is simply a listing of the names of functions it wants to export.

//============================================================ //Dlls .def file LIBRARY myfirstdll.dll DESCRIPTION 'My first DLL' EXPORTS MyFunction //============================================================ //Dlls header file which would also be included in client code bool MyFunction(int parms); //============================================================ //Dlls implementation of the function bool MyFunction(int parms) { //do stuff ............ }
It goes without saying that there can only be one MyFunction in the global namespace of your DLL. The second way is Microsoft specific, but is more powerful as you can not only export function, but also Classes and variables. Lets take a look at some code which generated by creating a DLL using the VisualC++ AppWizard. The comments generated are quite enough to explain how this works

//============================================================ //Dlls header file which would also be included in client code /* The following ifdef block is the standard way of creating macros which make exporting from a DLL simpler. All files within this DLL are compiled with the MYFIRSTDLL_EXPORTS symbol defined on the command line. this symbol should not be defined on any project that uses this DLL. This way any other project whose source files include this file see MYFIRSTDLL_API functions as being imported from a DLL, where as this DLL sees symbols defined with this macro as being exported. */ #ifdef MYFIRSTDLL_EXPORTS #define MYFIRSTDLL_API __declspec(dllexport) #else #define MYFIRSTDLL_API __declspec(dllimport) #endif // This class is exported from the test2.dll class MYFIRSTDLL_API CMyFirstDll { public: CMyFirstDll(void); // TODO: add your methods here. }; extern MYFIRSTDLL_API int nMyFirstDll; MYFIRSTDLL_API int fnMyFunction(void);
When you compile the DLL the MYFIRSTDLL_EXPORTS symbol is defined, and the __declspec(dllexport) keyword is prefixed to classes/variables or functions you want to be exported. And when the client code is being compiled the symbol is undefined and __declspec(dllimport) is prefixed to the items being imported from the DLL so that the client code knows where to look for them.

In both the methods outlined above, all the client code needs to do is to link to the myfirstdll.lib file during compilation and include the given header file which defines the functions and/or classes, variables being exported and it can use the items normally as if they had been declared locally. Now lets take a look at the other method of using DLLs, which I personally believe is more open-ended.

DLLs designed for RunTime linking usually use .def files to define the functions they will export. If you don't want to use .def files then you can just prefix the C functions to be exported with the __declspec(dllexport) keyword. Both of these methods accomplish the same thing. The client loads a DLL by passing the name of the DLL to the Win32 LoadLibrary() function. This returns a HINSTANCE handle which you should keep track of, as its needed when you want to unload the DLL. Once it has loaded the DLL, the client can get a pointer to any of the exported functions by calling GetProcAddress() with the function name.

//============================================================ //Dlls .def file LIBRARY myfirstdll.dll DESCRIPTION 'My first DLL' EXPORTS MyFunction //============================================================ /* Dlls implementation of the function */ bool MyFunction(int parms) { //do stuff ............ } //============================================================ //Client code /* The function declaration really isn't required but the client needs to be of the function parameters. These are usually supplied in a header file accompanying the DLL. The extern C with the function decleration tells the compiler to use C-style linkage. */ extern "C" bool MyFunction(int parms); typedef bool (*MYFUNCTION)(int parms); MYFUNCTION pfnMyFunc=0; //pointer to MyFunction HINSTANCE hMyDll = ::LoadLibrary("myfirstdll.dll"); if(hMyDll != NULL) { //Get the functions address pfnMyFunc= (MYFUNCTION)::GetProcAddress(hMyDll, "MyFunction"); //Release DLL if we werent able to get the function if(pfnMyFunc== 0) { ::FreeLibrary(hMyDll); return; } //Call the Function bool result = pfnMyFunc(parms); //Release the DLL if we dont have any use for it now ::FreeLibrary(hMyDll); }
As you can see the code is very straight forward. Lets see how to get "classes" working by building on the same concept. As mentioned earlier, there is no way to directly import classes from a DLL when using RunTime loading, so what we can do is to expose the "functionality" of a class by defining an interface which contains all of its public functions apart from the constructor and destructor. The interface will be a typical C/C++ structure containing only pure virtual functions. The actual class in the DLL will inherit from this struct and it will implement the functions defined in it. Now to allow access to this class from the client, all we need to do is to export a standard C-style function that instantiates the class, casts it to the interface so that the client can use it, and returns it. Another function should be exported that deletes the interface once the client is done with it. A sample implementation is given below.

//============================================================ //Dlls .def file LIBRARY myinterface.dll DESCRIPTION 'provides interface to I_MyInterface EXPORTS GetMyInterface FreeMyInterface //============================================================ //Shared header between Dll and Client which defines the interface //I_MyInterface.h struct I_MyInterface { virtual bool Init(int parms)=0; virtual bool Release()=0; virtual void DoStuff() =0; }; /* Declarations for the Dlls exported functions and typedef'ed function pointers to make it easier to load them. Note the extern "C" which tell the compiler to use C-style linkage for these functions */ extern "C" { HRESULT GetMyInterface(I_MyInterface ** pInterface); typedef HRESULT (*GETINTERFACE)(I_MyInterface ** pInterface); HRESULT FreeMyInterface(I_MyInterface ** pInterface); typedef HRESULT (*FREEINTERFACE)(I_MyInterface ** pInterface); } //============================================================ //Dlls implementation of the interface // MyInterface.h class CMyClass: public I_MyInterface { public: bool Init(int parms); bool Release(); void DoStuff(); CMyClass(); ~CMyClass(); //any other member funcs ............ private: //any member vars ............ }; //============================================================ //Dlls exported functions which create and delete the interface //Dllmain.h HRESULT GetMyInterface(I_MyInterface ** pInterface) { if(!*pInterface) { *pInterface= new CMyClass; return S_OK; } return E_FAIL; } HRESULT FreeMyInterface(I_MyInterface ** pInterface) { if(!*pInterface) return E_FAIL; delete *pInterface; *pInterface= 0; return S_OK; } //============================================================ //Client code //How to load the interface and call functions GETINTERFACE pfnInterface=0; //pointer to GetMyInterface function I_MyInterface * pInterface =0; //pointer to MyInterface struct HINSTANCE hMyDll = ::LoadLibrary("myinterface.dll"); if(hMyDll != NULL) { //Get the functions address pfnInterface= (GETINTERFACE)::GetProcAddress(hMyDll,"GetMyInterface"); //Release Dll if we werent able to get the function if(pfnInterface == 0) { ::FreeLibrary(hMyDll); return; } //Call the Function HRESULT hr = pfnInterface(&pInterface); //Release if it didnt work if(FAILED(hr)) { ::FreeLibrary(hMyDll); return; } //Interface was loaded, we can now call functions pInterface->Init(1); pInterface->DoStuff(); pInterface->Release(); //How to release the interface FREEINTERFACE pfnFree = (FREEINTERFACE )::GetProcAddress(hMyDll,"FreeMyInterface"); if(pfnFree != 0) pfnFree(&hMyDll); //Release the DLL if we dont have any use for it now ::FreeLibrary(hMyDll); }
Ths information should be enough to get you really comfortable when using DLLs. Feel free to contact me if you have any questions, corrections or improvements. Happy coding.

[email="skid@planetquake.com"]Gaz Iqbal[/email]

Report Article

## User Feedback

You need to be a member in order to leave a review

## Create an account

Register a new account