For a far amount of years I've been trying to come up with a way to get Delphi class from a standard Win32 DLL. A quick Google will reveal a few common ways you can do this (as flatten routines, all virtual methods or COM), but none really allows you to extend those classes in the natural Delphi OOP manner. Over the past few months I've developed bit and pieces to this puzzle that finally come together recently.
This is what I've come up with:
1. On the DLL side you have a base class (in my case TPyroObject) that has all virtual methods. There is a field call FIntfObject of type TPyroObject. There are two methods call CallingBaseMethod and IsCallingBaseMethod. I will explain about these a bit later.
2. Each class interface will have to exist on both the dll and the client side and this shared FIntfObject pointer will allow the client and dll versions to shadow each other.
3. You have one exported routine from the dll that can return an instance of each class interface. From this routine you also pass the client side version of this interface to the dll side. The FIntfObject field will hold the instance pointer to each other. In each method of the class on the client side you call FIntfObject.MethodName which will call the DLL side version of the code (having all virtual methods allow this to work). At this point you can create an instance of a class that reside inside the dll and use it on the client side in a natural way.
4. Now, how can we extend a class on the client side and still have the dll side be able to call the extended method in the expected manor? Well we have a copy of the client side class in the FIntfObject field so we can call any method we wish from the DLL side. So by default if the method is has been extended this extended method would be called unless you call the inherited version of it. So for us to do this we simply call FIntfObject.Methodname on the DLL side which will call into the client side version of the method and if it's extended this new code will be executed.
5. The problem now is how do we emulate a call to an inherited method? In this case the only time this construct will be called is in the base class on the client side. Since all methods are virtual we have to do something a little different. On the client side base method you would mark it's call such as this:
[pascal]// client side
procedure TMyClass.MyMethod;
begin
FIntfObject.CallingBaseMethod;
FIntfObject.MyMethod;
end;[/pascal]
Now when the method on the DLL side is called, it will check CallingBaseMethod and see if we mean to call the base code or the extended code such as this:
[pascal]// DLL side
procedure TMyClass.MyMethod
begin
if not IsCallingBaseMethod then // after this call the flag will be reset
begin
FIntfObject.MyMethod; // call extended method on client side
Exit;
end;
// call your base method code here
...
end;
[/pascal]
I must add to that as an initial requirement for this to work as natural as possible on startup I make the PyroGine DLL share memory with the client EXE this way strings and other managed data types can be passed back and forth without problems. The SDK will handle setting this up automatically. If you create additional DLLs that you wish to share memory with the main client EXE you would only need to add PyroUseShareMem as the first unit in the DLL project.
I would like to use packages but here are the problems for us:
a) We develop downloadable games and gamedev tools from our website and the IDE by default will require those run-time BPLs. Combined they alone becomes larger than the game itself.
b) if I use one package, then we run into the problem if multiple units in a package if we want to use other packages.
c) They are version specific and we need our game engine to work with all versions of Delphi without having to maintain different packages for each version.
Standard win32 DLLs solves these problems, but of coarse Delphi has no support what so ever (that I'm aware of) at exposing classes or even variable (why not variables?). It would be soooo nice if it did. I've now converted the SDK to use this model and all seems to be working great. Every class inside the PyroGine DLL can now be easily extended on the client side. The method calls in the DLL can safely call the extended client side methods and vise versa. So for example if you create a new Archive class (RAR maybe) it will continue to work through the whole SDK. Sweet! I hope to release version 1.1 in the coming weeks.
Bookmarks