Author |
Message |
Guest
Offline
|
Posted: Mon Dec 12, 2005 12:16 pm Post subject: Making a DLL with multiple classes? |
 |
|
|
|
I recently wrote a game of my own which I want people to be able to write their own AI for. I have a function, getDecision, which people must define. When I had set everything up, I noticed that MERVBot has a very similar setup.
What I would like to do is to setup a DLL that has all the necessary header files people will need, but I'm getting a lot of errors. I tried this tutorial but I still couldn't add my own separate files to the project and get it to compile.
Right now I am just trying to get a very basic container class to compile in the tutorial project I linked to above. If one of you guys has any idea how to help me, I'd greatly appreciate it. The class I'm trying to export is Action, here's the header:
#ifndef ACTION_H
#define ACTION_H
/**
* File - Action.h
* A very simple class which is used in my game. I need to be able to
* export the enum ActionType, the class Action, and the 3 global << operator functions.
*/
#include <string>
enum ActionType
{
ACTION_None,
ACTION_Punch,
ACTION_Kick,
ACTION_Dive,
ACTION_Jump,
ACTION_Shoot
};
class Action
{
private:
ActionType action;
std::string player;
double amount;
public:
Action(){}
Action( std::string NameOfPlayer, ActionType ActionByPlayer, double AmountOfForce = 0.0 )
{
player = NameOfPlayer;
action = ActionByPlayer;
amount = AmountOfForce;
}
virtual ~Action(){}
//Accessor Methods
std::string getPlayerName() const { return player; }
ActionType getAction() const { return action; }
double getAmount() const { return amount; }
bool operator==( const Action& act );
bool operator!=( const Action& act );
};
std::ostream& operator<<( std::ostream& out, const ActionType act );
std::ostream& operator<<( std::ostream& out, const Action& act );
std::ostream& operator<<( std::ostream& out, const Action* act );
#endif
|
The function declarations are defined in Action.cpp and my project is called Test just like the tutorial project I linked to.
Thanks!
|
|
Back to top |
|
 |
Bak ?ls -s 0 in

Age:26 Gender: Joined: Jun 11 2004 Posts: 1826 Location: USA Offline
|
|
Back to top |
|
 |
Guest
Offline
|
Posted: Mon Dec 12, 2005 1:12 pm Post subject: |
 |
|
|
|
Hey Bak,
I'm not trying to include it in a MERVBot project, I simply noticed that MERV has the same setup as the game I wrote. This is a completely different game, not ss-related. I'm trying to make a DLL that has that that class exported. Thanks.
|
|
Back to top |
|
 |
Bak ?ls -s 0 in

Age:26 Gender: Joined: Jun 11 2004 Posts: 1826 Location: USA Offline
|
Posted: Mon Dec 12, 2005 8:35 pm Post subject: |
 |
|
|
|
ohhh... so it's not really a bot question
basicly start a project as a win32 project VC++:
then select dll in the options and empty project, press finish
add a new item to your source files
type in a name with a .c extention, this will be the file that defines what the dll does.
then you write the code:
// Bak
// Dll implementation
// 12-12-05
#include <stdio.h>
__declspec(dllexport) void myDllFunction()
{
printf("in dll!\n");
} |
Compile it
now it placed a built .dll in my debug folder (I compiled in debug mode, if you use release mode it'll be in the release folder, just like any .exe).
The next step is to make the program that will open the dll. Close the solution and create a new project that's a win32 project, console project, empty project.
Add a new item to the source files (like before)
write the code in the source file you created:
// Bak
// 12-12-05
// This program imports a dll and calls a function from it
#include <stdio.h>
#include <windows.h>
int main()
{
const char* dllName = "dlltest.dll";
const char* functionName = "myDllFunction";
HMODULE module = LoadLibrary(dllName);
// our function pointer for the function we defined in the dll
void (*theDllFunc)() = 0;
if (!module)
{
printf("%s not found or load failed.\n", dllName);
exit(1);
}
// retrieve our function from the .dll by name
theDllFunc = (void (*)())GetProcAddress(module, functionName);
if (!theDllFunc)
{
printf("%s not found inside %s or load failed.\n", functionName, dllName);
}
else
{ // load was sucessful
// we can now use the function pointer to call the function
printf("hello dll!\n");
theDllFunc();
}
// important, free the dll
if (!FreeLibrary(module))
printf("error releasing the module loaded from %s\n", dllName);
return 0;
} |
Then compile, it should create an executable and put it in your debug or release folder (depending on how you compiled it).
Put both the exe and the dll in the same folder
and finally run the .exe (from cmd, since if you run it in windows it will quickly disappear)
now to make the function return values or pass parameters into it, you just change the type on the function pointer in the exe and obviously the type of the function in the .dll source.
To make it use classes, you may need to have an instance of the class defined and send a pointer to the class from the dll to the exe (as a void*). Then the exe will cast the pointer back to the class pointer type and should be able to run functions on it.
Notice the examples are in c and you're using c++. Just use extern "C" if it won't compile in c++.
If you have trouble with that part of it, post here and I'll run through it step by step.
|
|
Back to top |
|
 |
Guest
Offline
|
|
Back to top |
|
 |
Bak ?ls -s 0 in

Age:26 Gender: Joined: Jun 11 2004 Posts: 1826 Location: USA Offline
|
Posted: Mon Dec 12, 2005 10:48 pm Post subject: |
 |
|
|
|
Firstly, let me say you never want to return classes or structures (or pass them in as parameters for that matter). There are many reasons for this, most dealing with speeding your program up. You can usually accomplish what you need to do by passing a pointer to an object rather than the entire object. Notice this also applies to your accessor std::string getPlayerName(). A much better way to do it would be to do
const char* getPlayerName() const
{
return player.c_str();
} |
and create a copy in the calling function if you want to.
That aside, if you dont want to refactor anything (always a poor practice), you could implement getDecision like this:
Action DecisionManager::getDecision( StatsDB* stats )
{
Action a;
dllFunction(&a, stats);
return a;
} |
in this way it's not static, just modified by the function you're getting from the dll. You'll probably want to add ways to modify the variables in action to the Action class (setters) so that the function can actually make changes based on stats.
If this isn't clear just say so and I'll run through an example.
|
|
Back to top |
|
 |
Bak ?ls -s 0 in

Age:26 Gender: Joined: Jun 11 2004 Posts: 1826 Location: USA Offline
|
Posted: Mon Dec 12, 2005 11:57 pm Post subject: |
 |
|
|
|
Well I was running through making an example with the above method when I realized you can't gain any custom behavior with the way it's designed. This is because you need the header and implementation of your class both in the dll project and the exe project. So the natural answer was to use inheritance and virtual functions to allow custom behaviors. Unfortunately that made it a little complicated. The source code for both the dll and the exe is attached.
However, for your purpose I do not recommend this method. It looks like Action is just three pieces of data, a string, an int (the enum) and double. It would be much simpler to pass these as pointers to the dllFunction and have it modify the values based on stats.
dll-inheritance.zip - 16.33 KB
File downloaded or viewed 17 time(s)
|
|
Back to top |
|
 |
Guest
Offline
|
Posted: Tue Dec 13, 2005 12:55 am Post subject: |
 |
|
|
|
Quote: | Firstly, let me say you never want to return classes or structures (or pass them in as parameters for that matter). |
I should have caught myself returning strings..oops. Normally I don't return objects by value.
Quote: | However, for your purpose I do not recommend this method. It looks like Action is just three pieces of data, a string, an int (the enum) and double. It would be much simpler to pass these as pointers to the dllFunction and have it modify the values based on stats. |
That's a good point. However, won't I still need to include the implementation for StatsDB, and all the subsequent (20'ish) class implementations which it uses?
What I am really looking for is a way to only have to give some header files to the person defining getDecision, and I'm not sure if that is possible or not.
|
|
Back to top |
|
 |
Bak ?ls -s 0 in

Age:26 Gender: Joined: Jun 11 2004 Posts: 1826 Location: USA Offline
|
Posted: Tue Dec 13, 2005 1:44 am Post subject: |
 |
|
|
|
You will need to include the implementation of StatsDB, or else how could the dll compile (since you're using a class that isn't defined anywhere). But at least you'll have the functionality you wanted.
|
|
Back to top |
|
 |
Mine GO BOOM Hunch Hunch What What

Age:41 Gender: Joined: Aug 01 2002 Posts: 3615 Location: Las Vegas Offline
|
Posted: Tue Dec 13, 2005 2:21 am Post subject: |
 |
|
|
|
Bak wrote: | ohhh... so it's not really a bot question ;)
basicly start a project as a win32 project VC++: |
If you could, would you like to convert this into a wiki entry on the ASSS Wiki?
|
|
Back to top |
|
 |
Guest
Offline
|
Posted: Tue Dec 13, 2005 4:49 am Post subject: |
 |
|
|
|
I've set up both projects (the exe and dll). Both projects compile, but when I try to load the getBotDecision function at run time, it seems that "theDllFunc" is null. Here's my function definition in the dll:
BOTAI_API Action getBotDecision(StatsDB* stats, bool verbose); |
The verbose boolean is just there to print stuff out if true.
The code I'm using to call it then looks like this:
const char* dllName = "BotAI.dll";
const char* functionName = "getBotDecision";
HMODULE module = LoadLibrary(dllName);
// our function pointer for the function we defined in the dll
void (*theDllFunc)(StatsDB*,bool) = 0;
if (!module)
{
cout << dllName << " not found or load failed.\n" << endl;
Sleep( 10000 );
exit(1);
}
// retrieve our function from the .dll by name
theDllFunc = (Action (*)(StatsDB*,bool))GetProcAddress(module, functionName);
if (!theDllFunc)
{
cout << functionName << " not found inside " << dllName << " or load failed.\n" << endl;
Sleep(10000);
exit(1);
}
else
{ // load was sucessful
// we can now use the function pointer to call the function
return theDllFunc( stats, verbose );
}
// important, free the dll
if (!FreeLibrary(module))
cout << "error releasing the module loaded from " << dllName << endl; |
I must be doing something wrong with regards to getting the function pointer, but I have no idea what it is.
|
|
Back to top |
|
 |
Chambahs Power attack

Joined: Jun 19 2005 Posts: 820 Offline
|
Posted: Tue Dec 13, 2005 10:46 am Post subject: |
 |
|
|
|
Bak, i hate your tutorials, they are always better than mine and more neat lol jk nice post tho
|
|
Back to top |
|
 |
Guest
Offline
|
Posted: Tue Dec 13, 2005 2:17 pm Post subject: |
 |
|
|
|
Ah, I answered my own question. I downloaded a tool called Dependency Walker which tells you the functions contained in a DLL and their names. Apparently it was some garbled name which I had to request.
I still don't like that I have to give away the 20'ish class implementations that are behind my StatsDB class. Isn't there some way I could have them be compiled into a library and then just give the header and the library instead of giving away the cpp files?
|
|
Back to top |
|
 |
Bak ?ls -s 0 in

Age:26 Gender: Joined: Jun 11 2004 Posts: 1826 Location: USA Offline
|
Posted: Tue Dec 13, 2005 2:35 pm Post subject: |
 |
|
|
|
If you want to use a non-garbled function name, in your dll code, use a C function (using extern "C") like in the inheritance example. Of course you won't be able to return a class of type Action, but you shouldn't be returning classes anyways:
void cpp_myDllFunction(void** myClassPtr)
{
*myClassPtr = new ChildClass;
ChildClass* c = (ChildClass*)*myClassPtr;
c->functionToOverload();
}
extern "C"
{
__declspec(dllexport) void myDllFunction(void** myClassPtr)
{
cpp_myDllFunction(myClassPtr);
}
} |
Anyways in your code you have a problem:
if (!theDllFunc)
{
cout << functionName << " not found inside " << dllName << " or load failed.\n" << endl;
Sleep(10000);
exit(1);
} |
If this line of execution is taken FreeLibrary is never called.
I'll look into making implementations into libs.
|
|
Back to top |
|
 |
Bak ?ls -s 0 in

Age:26 Gender: Joined: Jun 11 2004 Posts: 1826 Location: USA Offline
|
Posted: Tue Dec 13, 2005 3:30 pm Post subject: |
 |
|
|
|
You can put your implementation into a lib so that it is not necessary to have mutliple files.
The first thing you need is to create a project which will create the static library. Uncheck Precompiled Header to prevent extra files. Delete the automatically generated readme.
Then put your header files and implementations into that project, compile it and a .lib should be created instead of a .exe or .dll.
Then create another project that will use the .lib. I used a regular win32 console project, empty project. I created a main that used the class defined in the lib and added the header to the project (and included it). Then right click project -> properties
Then in the Linker properties, under input -> additional dependencies, add the name of your lib (make sure all configurations is selected in the upper left combo box).
Press ok to save your changes
Put the .lib file in the project folder of the executable (or I guess you could give an absolute path in the above property).
Now your program should compile correctly without needing to put the implementation of the class into the project.
|
|
Back to top |
|
 |
Guest
Offline
|
Posted: Tue Dec 13, 2005 4:18 pm Post subject: |
 |
|
|
|
Thanks again Bak! I got my static library compiled. However, when I tried to include it in my dll like you did with your win32 exe project, I got a lot of LNK2005 errors. Any idea how I get my DLL project to use my .lib?
|
|
Back to top |
|
 |
Bak ?ls -s 0 in

Age:26 Gender: Joined: Jun 11 2004 Posts: 1826 Location: USA Offline
|
Posted: Tue Dec 13, 2005 5:28 pm Post subject: |
 |
|
|
|
I do not get this error when I use my lib in my dll project.
http://support.microsoft.com/default.aspx?scid=kb;en-us;q148652
Solution One: Force Linker to Link Libraries in Correct Order
1. On the Project menu, click Settings.
2. In the Settings For view of the Project Settings dialog box, click to select the project configuration that is getting the link errors.
3. On the Link tab, click to select Input in the Category combo box.
4. In the Ignore libraries box, insert the library names
|
|
Back to top |
|
 |
Guest
Offline
|
Posted: Tue Dec 13, 2005 6:36 pm Post subject: |
 |
|
|
|
Ah, I did some googling and figured it out. It turned out that the default settings for the static library and my dll were different with regards to code generation. One was using Single-Threaded and the other was using Multi-Threaded. I fixed that, and it all works! Finally!!!!!
Thank you bak!
|
|
Back to top |
|
 |
|