Skip to main content
GameDev.net gamedev.net
🔒 Locked

Plugins 2.0 Updated

Started by Drew_Benton Dec 26, 2004 at 10:56 PM 16 replies 2.7k views
Original Post
Drew_Benton
Drew_Benton
Recently I have collaborated with quite a few people on various forums on using a "plug-in" styled design for programs/engines/games/etc... However, it seems that a lot of people have a quite hard time with it! I know I did - it took me over 15 hours to fully learn all of the concepts. Using the sources from MSDN, Flip Code, and GameDev, I have put together a little documented project showing the creating of and use of functions and classes from .dll’s at runtime. The advantages of this system are that there is a level of abstraction - you can easily switch between using specific functions or classes without making anything dependent or non-abstract. You can easily add components to a project in a "plug-in manner". There are tons of uses for it overall. You will have to decide how you want to use it. I hope you all will find it helpful! I have made two versions, one for Dev-CPP users and one for VC7 users. I have also included the executable demo - so you can see for yourself that it works (Just right click on the .dll and look at the dependencies). When you run the program you will see it in action. The files are avaliable on my web page here. There's not much on my page because I'm redoing it - so it will be a quick click and download. Please feel free to give me any feed back on it. [smile] Thanks for your time as always! - Drew [Edited by - Drew_Benton on December 28, 2004 9:07:21 PM]
Kibble
Kibble
The claim of no libraries or header files is extremely dubious because you avoid them with a terrible hack. You'll have to maintain that 'prototype' (quoted for stretching that a bit) in many places (the host program, and any implementing DLLs ), and if it ever gets out of sync you will have very hard time debugging hard to find crashes. You might call a function in the program, but it could call another function entirely. If the arguments don't match it will crash one way or another. You might address memory outside of the vtable of the class in the DLL, interpreting arbitrary memory as a function pointer.
Drew_Benton
Drew_Benton
Quote:
Original post by Kibble
The claim of no libraries or header files is extremely dubious because you avoid them with a terrible hack. You'll have to maintain that 'prototype' (quoted for stretching that a bit) in many places (the host program, and any implementing DLLs ), and if it ever gets out of sync you will have very hard time debugging hard to find crashes. You might call a function in the program, but it could call another function entirely. If the arguments don't match it will crash one way or another. You might address memory outside of the vtable of the class in the DLL, interpreting arbitrary memory as a function pointer.


I know what you are saying - but that is addressed in the code itself. The traditional .dll header file is not needed - because you are not exporting out the class functions - instead you do need just the prototyped abstract base class. The .h file you would use is just a little bit different than normal. That statement was more of a marketing thing buddy [smile], *but* remember that in any program, you do not need to have a .h file.

- Drew

Kibble
Kibble
Quote:
Original post by Drew_Benton
I know what you are saying - but that is addressed in the code itself. The traditional .dll header file is not needed - because you are not exporting out the class functions - instead you do need just the prototyped abstract base class. The .h file you would use is just a little bit different than normal. That statement was more of a marketing thing buddy [smile], *but* remember that in any program, you do not need to have a .h file.

- Drew

I understand how it works. I'm saying that what you are doing is pointless over the traditional method, so there are no pros, but a very big con is that the error checking to make sure that all the functions exist and they are in the right place is gone.

Try it for yourself: First make the IWindow deal the traditional way - an exported/imported interface in the host program's header files, include that file in the DLL, and implement it. If you change either without recompiling the other, you will compile time errors like abstract class cannot be instantiated, or runtime errors stating so and so symbol mangled function name could not be found. If you do it with your method, it will happily compile it resulting in potentially dangerous hard to debug errors and crashes.

The reason it is pointless is the dependancy is still very much there. You require the host program to know the interface of the class in order to use it. So why go through the trouble of maintaining 2+ interfaces?
Maega
Maega
In defense of the concept (but not his implementation), it is only meant to be used widely when the interface is solid and not likely to change for some time. It is meant to be used to simplify updates in which you're changing the implementation of the interface, not the interface itself.

The way you are talking, Kibble, makes it looks like it is useless because the interfaces change too quick. Not always :D.
Drew_Benton
Drew_Benton
Quote:
Original post by Maega
You did a templated version of this

Using Interfaces with Dlls

However, yours didn't come out as clean.


Hmmm. Well I made my version based on MSDN b/c that one was confusing the heck out of me as well as the FlipCode. There is so much more (at least it seems to me when I learned this) than is gone over in either article. I don't know, I understand all the concepts now after doing it the way I did - and I was hoping to present alternative code for people if they were intrested with pre-done projects they can follow instead of looking at a web page

Kibble - Don't get me wrong - I **strongly** agree with what you are saying and understand what you are saying. BUT I did things this way to help people learn the basic concepts of plug-ins. I presented in a manner that was as simple as possible - based on how I learned how to use them. This is by no means professional, clean, or the best way to do things - but, if you have no clue of what to do with plugins - as I did, at least this should be a decent stepping stone to see the process. Remember in comp sci class how they first teach you the beginning concepts - and you put all your code above the main function? Then later you learn about prototypes, and finally you learn about header files, etc... I hope you can see that this is what I did on purpose - tried to start at the very beginning. I feel that by this way - looking at just one .cpp file that has everything, the beginner can at least see everything at once reather than go between a .h and .cpp file.

I appreciate the feedback from you both [smile]! It's good to hear what other people are thinking. I will work on a more appropriate version and try to write my own version of the articles on plugins. Thanks for your time and responses!

- Drew
Kibble
Kibble
Quote:
Original post by Maega
In defense of the concept (but not his implementation), it is only meant to be used widely when the interface is solid and not likely to change for some time. It is meant to be used to simplify updates in which you're changing the implementation of the interface, not the interface itself.

The way you are talking, Kibble, makes it looks like it is useless because the interfaces change too quick. Not always :D.

I can see the pattern being used in such a situation, but I still don't see an advantage, neither method requires a new distribution of the host DLL/program. The only thing I can see is that it avoids requiring the source for where the interface is defined.
Quote:
Original post by Drew_Benton
Kibble - Don't get me wrong - I **strongly** agree with what you are saying and understand what you are saying. BUT I did things this way to help people learn the basic concepts of plug-ins. I presented in a manner that was as simple as possible - based on how I learned how to use them. This is by no means professional, clean, or the best way to do things - but, if you have no clue of what to do with plugins - as I did, at least this should be a decent stepping stone to see the process. Remember in comp sci class how they first teach you the beginning concepts - and you put all your code above the main function? Then later you learn about prototypes, and finally you learn about header files, etc... I hope you can see that this is what I did on purpose - tried to start at the very beginning. I feel that by this way - looking at just one .cpp file that has everything, the beginner can at least see everything at once reather than go between a .h and .cpp file.

I have never taken a programming class so I can't comment on that. However, as a beginner I wouldn't dare think about DLLs. I think it is fairly unlikely that someone that isn't comfortable with multiple files is going to be poking around with DLLs.

You should mention the pitfalls of this method somewhere. I don't see it anywhere in the source/sample programs. IMO as it is presented now, it is going to create a lot of headaches for people that are using your sample as something to work off of.
Drew_Benton
Drew_Benton
Quote:
Original post by Kibble
You should mention the pitfalls of this method somewhere. I don't see it anywhere in the source/sample programs. IMO as it is presented now, it is going to create a lot of headaches for people that are using your sample as something to work off of.


Advice taken [smile]. I will work on a new version tonight and explain some more. Thanks for your replies, you've been very helpful.

- Drew
Drew_Benton
Drew_Benton
Ok guys, I have made a new and improved version that should be much better! If you can, please have a look and tell me what you think. Thanks!

- Drew
Kibble
Kibble
I still think it should use exported interfaces, another plus to them is you can use a registration system to create classes by string without the exported factory function. This is what MFC does to allow you to create classes from a string. I use the same system, it is incredibly convenient. I have been meaning to write an article on it for like, years, but I haven't gotten around to it. Maybe I'll do it since I have noticed a big spike in DLL related threads lately.

I don't think such a system is possible with 'prototyped' interfaces because each dll would be using its own copy of the 'registry' of classes, making it pointless.
Drew_Benton
Drew_Benton
Quote:
Original post by Kibble
I still think it should use exported interfaces, another plus to them is you can use a registration system to create classes by string without the exported factory function. This is what MFC does to allow you to create classes from a string. I use the same system, it is incredibly convenient. I have been meaning to write an article on it for like, years, but I haven't gotten around to it. Maybe I'll do it since I have noticed a big spike in DLL related threads lately.

I don't think such a system is possible with 'prototyped' interfaces because each dll would be using its own copy of the 'registry' of classes, making it pointless.


Hmmm can you give me an example of what you mean by the exported interfaces? Is the flaws in the CPlugin system or just how I'm making the plugins themselves? Thanks for your time!

- Drew

[Edited by - Drew_Benton on May 25, 2005 9:46:13 PM]
Kibble
Kibble
Most articles that talk about classes/DLLs do it this way.

Your CPlugin class is just a thin wrapper for LoadLibrary/GetProcAddress/FreeLibrary, so its not that. Its how the plugins are designed, just look at any plugin SDK for most programs, for example the 3D studio SDK. You don't copy + paste the interface of the class you want to implement into your program and derive from it, you derive from the interface they have defined. If they ever decided to change it, you would only have to rebuild your DLL, not go and copy + paste their interface again. When it changed, you would get compiler errors about not being able to instantiate an abstract class when you tried to rebuild your DLL. If your plugin had its own copy of the interface, this wouldn't happen, and it would crash when 3D studio tried to use functions it thought were supplied by your implementation of whatever interface.

They are not going to be changing the interfaces for their plugins any time soon, but it illustrates my point.

edit: Actually this is a bad example, you have to link to the 3Ds SDK libs for other reasons also. Hopefully you see what I mean though.
Drew_Benton
Drew_Benton
Ok I think I see what you are saying - please tell me if I'm wrong -

* I just need to add some form of "abstract" plugin interface to the CPlugin class instead of having it as a separate class.

If this is correct - I do not see the advantage of this. Right now as is, the CPlugin class only assumes one thing - that there will be some void GetMemory( void** memory) function exported from a .dll in order to allocate a class. Everything else is open for anything.

Quote:
You don't copy + paste the interface of the class you want to implement into your program and derive from it, you derive from the interface they have defined. If they ever decided to change it, you would only have to rebuild your DLL, not go and copy + paste their interface again.


I see what you are saying - but aren't I not doing that by allowing the end user to add their own interface into the CPlugin class for their specific program and then send that out for the end users?

In regards to the current code - how do you describe the relationsships between IWindow, OpenGLWindow, and DirectXWindow? Isn't IWindow just an interface that does the same functionality as I_MyInterface (link) (Except the fact I made it a class by mistake, I just realized that). Then the GetMyInterface is synonymous with the Get_OGLWindow/Get_DXWindow functions? Instead of having a FreeMyInterface, the "Release" function is part of the interface - or maybe now I can see that as a flaw.

Once again thank you for your continued feedback!

- Drew
Kibble
Kibble
No, it has nothing to do with CPlugin, like I said before it is just a wrapper for the windows API functions. The way you would change your demo app is create a header specifically for IWindow (and more classes if you want), in the program that will use it. Then have your DLLs include that header file and implement it. Depending on what you have in the 'interface' (adding such things makes it technically not an interface anymore), you will have to export it.

This will prevent the interfaces for IWindow from getting out of sync (adding a function in one and forgetting to add it in the other), and it is just less tedious since you won't have to copy and paste functions from one to the other all the time.

It will help to find problems when they do occur. Take for example a crash caused by this exact problem (the interfaces not being exact copies of eachother). With the 'prototyped' interface method, you would have to go around and compare them all to see if there is something different, and subtle errors might go undetected forever (signed vs. unsigned arguments or something). With the way I'm describing, just do a batch build and compiler errors will occur showing you where the problem is. Because of this I don't even use regular build in my project (11 DLLs, 2 exes, and a 3D studio plugin), I just hit F6 which I have bound to 'batch build', and it will skip the ones without changes instantly anyways.

It is basically the same thing since headers are essentially just pasted into the cpp file before compiling, the difference is in how much code there is to maintain. You will also have to do the whole exporting deal if you ever want to do something more complicated than true interfaces through the whole hierarchy.

You WILL forget to update an interface at some point or another, I gaurentee it. I know I certainly could not always remember every instance of the interface, and sometimes I would just plain forget to update any of the others. Lke Maega said before, it is not unreasonable to use this when the interface is not going to be changing often, but if it is going to be in development, it is almost impossible to avoid this, and once it is developed, it is only extra work to convert it to the prototyped interface method.
Drew_Benton
Drew_Benton
Quote:
Original post by Kibble
No, it has nothing to do with CPlugin, like I said before it is just a wrapper for the windows API functions. The way you would change your demo app is create a header specifically for IWindow (and more classes if you want), in the program that will use it. Then have your DLLs include that header file and implement it. Depending on what you have in the 'interface' (adding such things makes it technically not an interface anymore), you will have to export it.

This will prevent the interfaces for IWindow from getting out of sync (adding a function in one and forgetting to add it in the other), and it is just less tedious since you won't have to copy and paste functions from one to the other all the time.


**Lightbulb clicks** Ok I think I see exactly what you are saying now and it makes perfect sense now. Essentially I need to do this -

* The IWindow.h file should be made as part of the app itself. It will not conttain the OpenGLWindow and DXWindow classes.
* Instead of making the IWindow plugin - I need to make 2 plugins instead, OpenGLWindow and DXWindow, which use the IWindow.h file from the app.
* Redo the demo so it loads one of the two .dlls as the example and runs it. As said before, the IWindow.h 'interface' class is for this specific app.

Is this basically what you've been saying all along - but I've been misinterpreting [embarrass]? I can see now how this design is making a an app use plugins rather than how I had it, plugins using an app. Thanks a bunch! I don't know what I'd do without you [wink].

- Drew
Kibble
Kibble
Quote:
Original post by Drew_Benton
* The IWindow.h file should be made as part of the app itself. It will not conttain the OpenGLWindow and DXWindow classes.

Yes
Quote:
* Instead of making the IWindow plugin - I need to make 2 plugins instead, OpenGLWindow and DXWindow, which use the IWindow.h file from the app.

Yes, although I am curious as to how you were planning to use the IWindow plugin on an OS without DirectX (ie not windows). It was defeating one of the purposes of a design like this.
Quote:
* Redo the demo so it loads one of the two .dlls as the example and runs it. As said before, the IWindow.h 'interface' class is for this specific app.

Yes
Quote:
Is this basically what you've been saying all along - but I've been misinterpreting [embarrass]? I can see now how this design is making a an app use plugins...

Yes, generally an application needs to know why and how to use a plugin...
Drew_Benton
Drew_Benton
Thanks a bunch! I will get right to work!

[Edited by - Drew_Benton on May 25, 2005 9:31:21 PM]

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.