Server Help Forum Index Server Help
Community forums for Subgame, ASSS, and bots
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   StatisticsStatistics   RegisterRegister 
 ProfileProfile   Login to check your private messagesLogin to check your private messages   LoginLogin (SSL) 

Server Help | ASSS Wiki (0) | Shanky.com
Making a DLL with multiple classes?

 
Post new topic   Reply to topic Printable version
 View previous topic  Kick out delay exceeded Error Post :: Post ASSS vs Python(bugs) vs C++ vs merv vs...  View next topic  
Author Message
Guest



Offline

PostPosted: Mon Dec 12, 2005 12:16 pm    Post subject: Making a DLL with multiple classes? Reply to topic Reply with quote

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:

Code: Show/Hide
#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:Gender:Male
Joined: Jun 11 2004
Posts: 1826
Location: USA
Offline

PostPosted: Mon Dec 12, 2005 12:50 pm    Post subject: Reply to topic Reply with quote

works fine for me, I #included it at the top of spawn.h:

Code: Show/Hide
#ifndef SPAWN_H
#define SPAWN_H

#define STRING_CAST_CHAR
#include "action.h"
#include "..\dllcore.h"

#include "..\clientprot.h"
Back to top
View users profile Send private message Add User to Ignore List AIM Address
Guest



Offline

PostPosted: Mon Dec 12, 2005 1:12 pm    Post subject: Reply to topic Reply with quote

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:Gender:Male
Joined: Jun 11 2004
Posts: 1826
Location: USA
Offline

PostPosted: Mon Dec 12, 2005 8:35 pm    Post subject: Reply to topic Reply with quote

ohhh... so it's not really a bot question icon_wink.gif

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:
Code: Show/Hide
// 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:
Code: Show/Hide
// 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
View users profile Send private message Add User to Ignore List AIM Address
Guest



Offline

PostPosted: Mon Dec 12, 2005 10:24 pm    Post subject: Reply to topic Reply with quote

Wow, thanks bak! Great post! Only one real part I don't get:

Quote:
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.


I've never done anything like that before. From the sound of it though, it seems that you would need to have the class be static or mutable? If that's true, then I'm afraid that isn't going to help me, since I need to be able to create my own instances of the classes outside the DLL. icon_confused.gif

For example, the function which the AI-writer has to define is:
Code: Show/Hide
Action DecisionManager::getDecision( StatsDB* stats )


[/code]
Back to top
Bak
?ls -s
0 in


Age:26
Gender:Gender:Male
Joined: Jun 11 2004
Posts: 1826
Location: USA
Offline

PostPosted: Mon Dec 12, 2005 10:48 pm    Post subject: Reply to topic Reply with quote

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

Code: Show/Hide
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:

Code: Show/Hide
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
View users profile Send private message Add User to Ignore List AIM Address
Bak
?ls -s
0 in


Age:26
Gender:Gender:Male
Joined: Jun 11 2004
Posts: 1826
Location: USA
Offline

PostPosted: Mon Dec 12, 2005 11:57 pm    Post subject: Reply to topic Reply with quote

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
View users profile Send private message Add User to Ignore List AIM Address
Guest



Offline

PostPosted: Tue Dec 13, 2005 12:55 am    Post subject: Reply to topic Reply with quote

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. icon_confused.gif
Back to top
Bak
?ls -s
0 in


Age:26
Gender:Gender:Male
Joined: Jun 11 2004
Posts: 1826
Location: USA
Offline

PostPosted: Tue Dec 13, 2005 1:44 am    Post subject: Reply to topic Reply with quote

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
View users profile Send private message Add User to Ignore List AIM Address
Mine GO BOOM
Hunch Hunch
What What
Hunch Hunch<br>What What


Age:41
Gender:Gender:Male
Joined: Aug 01 2002
Posts: 3615
Location: Las Vegas
Offline

PostPosted: Tue Dec 13, 2005 2:21 am    Post subject: Reply to topic Reply with quote

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
View users profile Send private message Add User to Ignore List Send email
Guest



Offline

PostPosted: Tue Dec 13, 2005 4:49 am    Post subject: Reply to topic Reply with quote

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:
Code: Show/Hide

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:

Code: Show/Hide
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
Power attack


Joined: Jun 19 2005
Posts: 820
Offline

PostPosted: Tue Dec 13, 2005 10:46 am    Post subject: Reply to topic Reply with quote

Bak, i hate your tutorials, they are always better than mine and more neat icon_razz.gif lol jk nice post tho
Back to top
View users profile Send private message Add User to Ignore List
Guest



Offline

PostPosted: Tue Dec 13, 2005 2:17 pm    Post subject: Reply to topic Reply with quote

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? icon_confused.gif
Back to top
Bak
?ls -s
0 in


Age:26
Gender:Gender:Male
Joined: Jun 11 2004
Posts: 1826
Location: USA
Offline

PostPosted: Tue Dec 13, 2005 2:35 pm    Post subject: Reply to topic Reply with quote

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:

Code: Show/Hide
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:

Code: Show/Hide
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
View users profile Send private message Add User to Ignore List AIM Address
Bak
?ls -s
0 in


Age:26
Gender:Gender:Male
Joined: Jun 11 2004
Posts: 1826
Location: USA
Offline

PostPosted: Tue Dec 13, 2005 3:30 pm    Post subject: Reply to topic Reply with quote

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
View users profile Send private message Add User to Ignore List AIM Address
Guest



Offline

PostPosted: Tue Dec 13, 2005 4:18 pm    Post subject: Reply to topic Reply with quote

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:Gender:Male
Joined: Jun 11 2004
Posts: 1826
Location: USA
Offline

PostPosted: Tue Dec 13, 2005 5:28 pm    Post subject: Reply to topic Reply with quote

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
View users profile Send private message Add User to Ignore List AIM Address
Guest



Offline

PostPosted: Tue Dec 13, 2005 6:36 pm    Post subject: Reply to topic Reply with quote

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! icon_biggrin.gif icon_biggrin.gif icon_biggrin.gif
Back to top
Display posts from previous:   
Post new topic   Reply to topic    Server Help Forum Index -> Bot Questions All times are GMT - 5 Hours
Page 1 of 1

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You can attach files in this forum
You can download files in this forum
View online users | View Statistics | View Ignored List


Software by php BB © php BB Group
Server Load: 54 page(s) served in previous 5 minutes.

phpBB Created this page in 0.485113 seconds : 44 queries executed (89.7%): GZIP compression disabled