Classes in a DLL / plugins

Started by
11 comments, last by SilentReaper 21 years, 6 months ago
Hello all. I am working on a game project where I''d like to exclusively use C++ and classes. One of the requirements of this project is that it must be easy to upgrade/patch the game components and add things to the game (such as new weapons, new characters, or new AI behaviors). To this end, each major component of the game will have a sub-directory where I''d like to read in and load *.DLL. I have read some of the prior posts on how to use C++ in DLL''s and how to access those classes in the hosting process, however these posts have all pretty much been what I consider "early bound". The classes are known and defined at compile time. That won''t necessarily be the case with my game. If a new DLL is dropped in a directory, its code should run. I was planning on making a basic parent class with at least 3 pure virtual functions: - init: set up any variables necessary, possibly pass in a "this" pointer from one of my master classes so that the class can access other game objects - process: called in the appropriate game logic section (a weapon class would be processed during the weapons stage of the game loop) - shutdown: release all allocated memory objects and handles. I don''t believe this type of scenario was answered in any prior posts (if so, someone please link me there), and I can''t seem to find any GOOD resource on Google. Is this possible? Could someone help me out with a short snippet? Doesn''t even have to be to my above specs as long as I learn the requirements for creating the DLL and how to load in the DLL into my game process.
Advertisement
What you are after is called a "pluggable factory", searching for that on Google will return you loads of articles. Indeed one of them is right here at GameDev.

[edited by - MonkeyChuff on August 14, 2002 11:08:17 AM]
Sweet! I will look for that. You are not, by any chance, referring to COM are you? I would like to steer clear of that since I have heard it is very slow. I know COM has things called class factories, and that is the only reason I ask.

[edited by - SilentReaper on August 14, 2002 11:13:15 AM]
No, not at all, factory''s are a common design pattern, it just so happens that COM uses the factory pattern quite extensively.
I haven''t found COM to be slow, most of DirectX uses COM, so if you are going down that route then it''s going to be in there somewhere.
Very true. Thanks for the help!
Its a peice of cake. Crystal space does a good job of explaining how its done: http://crystal.sourceforge.net/docs/online/manual/cs_139.php

If you want a more specific example:

iTest.h:
class iTest
{
public:
virtual int Init(void* stuff)=0;
virtual int Process()=0;
virtual int Shutdown()=0;
};


Test.h:
class Test: public iTest
{
public:
int Init(void* stuff);
int Process();
int Shutdown();
};



Test.cpp:

int Test::Init(void* stuff)
{
//do something
return 0;
}
int Test:: Process()
{
//do somthing
return 0;
}
int Test::Shutdown()
{
//do somthing
return 0;
}

//this is called the class factory
__declspec(dllexport) Test* ExportPlugin()
{
return new Test;
}




main.cpp:

#include "itest.h"
#include <windows.h> //yuck!
HINSTANCE handle;
typedef void* (*FuncPtr)();


int main()
{
FuncPtr funcpointer;
handle=LoadLibrary("test.dll");
funcpointer=GetProcAddress(handle,"ExportPlugin");
iTest* tester=funcpointer();
tester->Init(junk);
tester->Process();
tester->Shutdown();
FreeLibrary(handle);
return 0;
}


test.h and test.cpp are compiled into test.dll
main.cpp is compiled into the main program, both use itest.h

basically what you have to do, is search through all the dlls in your games 'plugin' folder and call GetProcAddress to get the pointer to the ExportPlugin function, if you get a null returned then that dll isnt one of your plugins, if a !null pointer is returned then call the function the pointer points to and store the returned pointer as a pointer to base plugin class...

'ExportPlugin' isnt a special function name, you can name it whatever you want...

a couple things to watch out for if you use MSVC...

In order to export a function with a specific name(MSVC likes to give functions semirandom names) you have to give it a specific option or use a .def file. not hard to do. You can check the actuall name that is exported by generating a .map file(its an option) and looking in there.

make sure the function calling convention in the dll class factory and the function pointer match otherwise you get nasty errors

ummm i think thats it, hope it helps!

[edited by - warpexplorer on August 14, 2002 5:33:04 PM]
Thank you to you both for your excellent resources! warpexplorer: I assume that if I derive from Test that iTest will still work? I assume that it would and the reason for it would be the vtable would have the proper location for my member functions?

Thanks again!
Yep and yep. You can derive from test, and from the derivatives of test and the itest* will still work.

I tried something very similar to that and got errors, which I think come from the fact that new uses some memory allocation function (malloc?), which uses global pointers (buckets, if anyone understands). The dll had it''s own copies of these variables, which meant there were two separate memory allocation functions each trying to allocate the same memory.

(I''m guessing this because when mallocing large (1mb - 256bytes) blocks, one from the dll and one from the main, the pointers were different by a number not divisible by 1meg. If you know malloc, this is wrong. Also, nonsensical pointer errors abounded)


Constructors are able to be exported themselves from the DLL, without the need for an encapsulating function like the one below.

If you use MSVC 6 or more, create a new project of type "Win32 dynamic link library", and chose "A dll that exports some symbols". It gives examples of how to create dlls "well" (the microsoft way). It shows how to export variables, functions and constructors.

Also, dlls shouldn''t call library functions unless you know what you''re doing. A simple call to printf() will not print anything when inside a dll, you have to import the printf function (or an encapsulating function) from the main program. I assume this doesn''t work for the same reason that I assume malloc doesn''t.

(These are just my findings, correct me if I''m wrong)
Krylloan,

well, im not sure about new using malloc.
but my above example works perfectly on my machine as long as the call convention is the same.

The examples microsoft give are for ''statically'' linked dlls (very oxymoron), they MUST be (and are) loaded when the program starts and are unloaded when it exits. Which doesnt work for what SilentReaper wants.

all the library functions that I''ve tried work in my dlls (SDL, OpenGL, stdio, ect.)
printf works great, all i have to do is #include <stdio.h>

I use MSVC 6 so I dont think its a compiler problem, are you sure you are using the same calling convention?

This topic is closed to new replies.

Advertisement