I'm trying to figure out a safe way of passing objects from a main app to a DLL (for a plugin system).
The idea is to use the main app's TZConnection (database access from Zeos Lib) in the dll's.
I'd rather not use Runtime Packages, and DLL must be the way to go (I don't need BPL's need to recompile each time, and have no idea of how to use COM).
I've been told it's possible to do it with Interfaces.
I've never used them before, but been messing around with it, and managed to do it... But, I don't know if I did it right (as in, safe).
Here's my Interface unit.
unit PluginIntf;
interface
uses
ZConnection, ZDataSet;
type
IQueryResult = interface ['{743AB77E-7897-403E-A0D9-4D02748E565D}']
function GetRecordCount: Integer;
function GetDataSet: TZQuery;
end;
IPluginHost = interface ['{A5A416B4-437E-4A1E-B228-0F94D54840B0}']
function RunSql(const SQLString:WideString): IQueryResult;
end;
IPlugin = interface ['{8D9591C3-5949-4F0A-883E-6ABD02597846}']
function GetCaption: WideString;
end;
implementation
end.
In IQueryResult, I'm passing a TZQuery. It works, but... Is it safe?
Is there any other way to wrap it in the Interface?
Thank you
Nuno Picado
TZQuery is a class. Therefore it is not safe to pass one across the module boundary unless you use runtime packages. Use a class with DLLs and you actually have two separate types, one in each module.
You are correct that interfaces are safe for this but you need to restrict yourself to DLL interop safe types. That is simple types, records, pointers to arrays, interfaces, or combinations of these types.
You need to wrap TZQuery with an interface that exposes it's functionality.
Related
I downloaded the OTL http://www.omnithreadlibrary.com/
and compile the D2007 grouproj, install the package, without problem.
I then create a simple console application that uses OtlParallel unit, of course, I add the OtlParallel and some other pas files to the project.
But it complains that Generics.Collections is not found.
The documentation says:
High-level abstractions are implemented in the OtlParallel unit. They are all created through the factory class Parallel. High-level code intensively uses anonymous methods and generics which makes Delphi 2009 the minimum supported version.
This us of both generics and anonymous methods makes this unit completely incompatible with Delphi 2007.
If you wish to use a construct like Parallel.For with Delphi 2007 and OTL then you will have to back-port OtlParallel yourself. Without anonymous methods this is very difficult to do and achieve the same fluid style of code. You would have to use procedural types instead of anonymous methods. And you would have to implement closures manually.
So instead of using
TProc = reference to procedure;
you would use
TMethod = procedure of object;
And then to implement this you create a class or record with a parameterless method. You'll need to add whatever state is needed as members of the type, and populate those members. That is in essence a manual implementation of a closure with variable capture. And you'll need to deal with lifetime. Make sure that the instances outlive the parallel loop.
Good luck!
I'm currently struggling with the following:
I need to create two different DLL's, which do exactly the same but are looking to a different DB. The two DB's are nothing alike.
My dll's should be handling the communication with those different DB's.
So that the main program chooses which dll he wants to use.
I want to be sure each dll has exactly the same procudes/functions/...
I was thinking of using interfaces.
But I can't figure out how to create global interfaces. the dll's belong to the same projectgroup.
I do believe you're making a "mountain out of a molehill" thinking you need 2 different DLLs. But if you choose the last of my suggested options, you should find it fairly easy to switch between a 2 DLL solution and 1 DLL solution.
Option 1
This is the most straightforward:
Create a new unit.
Add your DLL interface (the exports).
Include the unit in both projects.
unit DllExportIntf;
interface
uses
DllExportImpl;
exports DoX;
implementation
end.
Note that this unit uses DllExportImpl which will also have to be included in both projects. However, you'll need 2 different files with the same name in 2 different locations in your file system. So each DLL project will have different implementations.
Now whenever you make a change to your interface, your projects won't compile until you've updated each of the DllExportImpl units.
What I don't particularly like about this solution is the need for units with the same name but different behaviour. Since you intend having both DLLs in the same project group: I should warn you that I've experienced the IDE getting confused by duplicate unit names.
Option 2
Place the exports into a shared include file.
library DllSharedExportsImpl1;
uses
DllExportImpl1 in 'DllExportImpl1.pas';
{$I ..\Common\DllExports.inc}
The DllExports.inc file will only include your exports clauses. E.g.
exports DoX;
This has the advantage that now each DLL can use different unit names for the different implementations. And if you change your include file, neither project will compile until you've updated its implementation unit to accommodate the change.
Note that this does come with its own set of problems. The way includes work: the compiler effectively shoves the contents of the include file into the unit at compile time. So what looks like line 7 to the IDE is entirely different to the compiler. Also editing include files can be a bit of a nuisance because context can only be determined where the file is included making editor support quite impractical.
Option 3
This option is a little more work, but provides much better long-term maintainability.
You do this by implementing your interface via polymorphic objects. In this way, both DllProjects will also share the routines that are actually exported. When each DLL initialises, it sets the concrete implementation to be used.
Your DLL interface could look something like this.
unit DllExportIntf;
interface
type
TAbstractImpl = class(TObject)
public
procedure DoX; virtual; abstract;
end;
procedure AssignDllImpl(const ADllImpl: TAbstractImpl);
procedure DoX;
exports DoX;
implementation
var
GDllImpl: TAbstractImpl;
procedure AssignDllImpl(const ADllImpl: TAbstractImpl);
begin
if Assigned(GDllImpl) then
begin
GDllImpl.Free;
end;
GDllImpl := ADllImpl;
end;
procedure DoX;
begin
GDllImpl.DoX;
end;
end.
When you initialise your DLL, you can call:
AssignDllImpl(TDllImpl_1.Create);
A clear advantage of this approach is that if there is any common code between your 2 DLLs, it can be included in your base implementation. Also, if you can change an existing method DLL in such a way that it does not require a change to TAbstractImpl, you possibly will only need to recompile your DLLs.
Furthermore, if you need to change existing virtual abstract methods, you will have to update the overrides in your concrete implementations accordingly.
WARNING If you add a new virtual abstract method, your projects will still compile with warnings that you are creating objects with abstract methods. However, you should always treat warnings as errors. If you do, this caveat won't be a problem.
NOTE: As mentioned earlier, using this approach you should be able to fairly easily switch between single DLL and 2 DLL solutions. The difference basically boils down to which units are included in the project, and how you initialise the global.
It may also be worthwhile mentioning that you could even eliminate the global altogether by implementing a Handle to use with each of your DLL routines. (Similar to Windows.) Bear in mind that there are technical issues when trying to pass objects between DLL and application code. This is why instead of passing objects, you use a "handles" to objects and encapsulate the actual object instances internally.
Considering all that was said, I believe that you would be more successful if you design your solution with packages, not DLLs. A package is a DLL, but rich in symbols, so Delphi can be a better use of it. Particularly, the symbols declared inside the package will more easily be loaded by your application, with a much higher level of abstraction. It´s what the Delphi IDE uses to load components.
So, following this design, this is what you have to do:
Declare your interfaces in units existing in a package named (for instance) DBServices.dpk. Here is an example of such an unit:
unit DBService1;
interface
uses
....;
type
IService1 = interface
[....] // here goes the GUID
procedure ServiceMethod1;
procedure ServiceMethod2;
// and so on...
end;
implementation
end.
So, above you created an unit that declares an interface. Your aplication can use that interface anywhere, just reference the package in your application and use it in other units and you will have the access to the symbols declared.
Declare the implementation class for that very same interface in another unit of another package, for instance, dedicated to SQLServer (SQLServerServices.dpk):
unit SQLServerService1;
interface
uses
DBService1, ....;
type
TSQLServerService1 = class(TInterfacedObject, IService1)
protected // IService1
procedure ServiceMethod1;
procedure ServiceMethod2;
// and so on...
end;
implementation
procedure TSQLServerService.ServiceMethod1;
begin
// Specific code for SQL Server
end;
procedure TSQLServerService.ServiceMethod2;
begin
// Specific code for SQL Server
end;
...
end.
Above you declared an implementing class for the interface IService1. Now you have two packages, one declaring the interfaces and other implementing those interfaces. Both will be consumed by your application. If you have more implementations for the same interfaces, add other packages dedicated to them.
One important thing is: you have to have a factory system. A factory system is a procedure ou class that will create and return the implementations for your application from each package.
So, in terms of code, in each service package (the ones that implement the interfaces) add a unit named, for instance, xxxServiceFactories, like this:
unit SQLServerServiceFactories;
interface
uses
DBService1;
function NewService1: IService1;
implementation
uses
SQLServerService1;
function NewService1: IService1;
Result := TSQLServerService1.Create;
end;
end.
The code above declares a function that creates the SQL Server implementation and returns it as an interface. Now, if you call a method from the interface returned, you will be actually calling the specific implementation of it for SQL Server.
After loading the package, you will have to link to that function in the very same way you would do if working if a DLL. After you have the pointer for the function, you can call it and you will have the interface in your application's code:
...
var
service1: IService1;
begin
service1 := NewService1;
service1.ServiceMethod1; // here, calling your method!
end;
The model I described in this answer is the one I used in a similar scenario I had to deal with in the past. I presented general ideas that work, but you have to understand the fundamentals of packages and interfaces to really master the technique.
A comprehensive explanation on those matters would be very long for an answer here, but I guess it will be a good starting point for you!
What you want to do is create a COM component project. Define your methods on that & implementations for one DB. Then create a second COM component that uses the same interface.
On the off-chance that your question is more about the fundamentals of Delphi, I've added another answer which may be more helpful to you than the first one. My first answer focused on getting 2 DLLs to expose the same methods (as per the main body of your question). This one focuses on the last 2 sentences of your question:
But I can't figure out how to create global interfaces. The dll's belong to the same project group.
Based on this, it sounds like you're looking for an option to "mark an interface as global so that projects in the same group can use them". Delphi doesn't need a special feature to do this because it's trivially available if you understand certain fundamental principles.
When you create a new unit, it is by default added to the current project. However if you want to share the unit between multiple projects, it's a good idea to save it to a different folder so it's easy to see that it's shared. Your first DLLs project file should look something like this.
library Dll1;
uses
DllSharedIntf in '..\Common\DllSharedIntf.pas';
You can define your "global" interface in the DllSharedIntf unit. E.g.
unit DllSharedIntf;
interface
type
IDllIntf = interface
['{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}']
procedure DoX;
end;
implementation
end.
NOTE: Because the interface type is declared in the interface section of the unit, it is considered "global" because other units are able to use it. But this doesn't automatically make it available to other projects.
You now have to add the shared unit to your other project so it becomes available for use by other units in that project. To do this:
Activate Dll2
Select Project and Add to Project...
Find DllSharedIntf and add it.
Delphi will automatically update your project source file to include the unit.
library Dll2;
uses
DllSharedIntf in '..\Common\DllSharedIntf.pas';
Now in each DLL project you can add a separate implementation unit. E.g. For Dll1:
unit DllImpl1;
interface
uses
//It's very important to indicate that this unit uses the shared unit.
//Otherwise you won't be able to access the "global types" declared
//in the interface-section of that unit.
DllSharedIntf;
type
TDllImpl1 = class(TInterfacedObject,
//Any types defined in the interface-section of any units that
//this unit **uses**, can be accessed as if they were declared
//in this unit.
IDllIntf)
protected
//The fact that this class is marked as implementing the IDllIntf
//means that the compiler will insist on you implementing all
//methods defined in that interface-type.
procedure DoX;
end;
implementation
NOTE This answer only covers sharing an interface between projects. You'll still need to expose the functionality of the DLLs via appropriate exports. You'll need an approach similar to option 3 of my other answer.
Summary
We don't usually talk about "global interfaces" in Delphi. It's generally understood that anything declared in the interface section of a unit is globally accessible. (We do make more of an issue about global variables due to their dangers though; but that's an entirely different topic.)
In Delphi:
Whenever you want one unit (A) to make use of functionality defined in another unit (B), you need to add unit B to the uses clause of unit A.
Whenever you want a project to use a unit created in another project, you need to add the unit to the project. (TIP: It's a good idea to put such units in a separate folder.)
NOTE: When sharing units between projects, the project group is actually irrelevant. Projects don't need to be in the same group to share units. All you need to do is ensure the project can access the unit so that other units in your project can uses it.
When I want to export a class within a DLL, is it the right approach to derive it from an interface and return that interface by an exported function?
//exported dll function, which is used in the exe.
function MyClass_Create: IMyClass;
begin
result := TMyClass.Create;
end;
What about the memory management? Can I than pass in/out different interfaces and strings without worries and crashes?
IMyClass = interface
procedure SetString(aMsg: string);
function GetString: string;
procedure SetClass(aClass: ITestClass);
function GetClass: ITestClass;
end;
Interface references are orthogonal to memory management. Usually you export a function that returns interface reference from dll, and don't care about memory management. With reference counting interfaces you can be sure that object instance that implements interface will be freed in dll too.
Strings are different. It does not matter whether you export interface or export flat functions - the same restrictions applies.
BTW your question title is incorrect, there are no 'interface instances' in Delphi.
Using interfaces like this will ensure that the object implementing the interface will be created and freed on the same heap.
However, this will not solve the problem of dynamic string types being allocated and deallocated on different heaps. There are many possible solutions to this, but in my view the best approach is to use WideString across the module boundary.
The WideString type is a wrapper around the COM BSTR and is allocated on the shared COM heap. You only need to use WideString for the interface. The internals of the implementing classes can use native Delphi strings.
Just as strings present problems so do dynamic arrays. Attempting to pas dynamic arrays across module boundaries is not safe. There is no solution analagous to WideString that is as convenient. You can use variant arrays but that's pretty clunky in comparison to WideString.
I have used Delphi classes for a while now but never really got into using interfaces. I already have read a bit about them but want to learn more.
I would like to hear which pros and cons you have encountered when using interfaces in Delphi regarding coding, performance, maintainability, code clearness, layer separation and generally speaking any regard you can think of.
All I can think of for now:
Pros:
Clear separation between interface and implementation
Reduced unit dependencies
Multiple inheritance
Reference counting (if desired, can be disabled)
Cons:
Class and interface references cannot be mixed (at least with reference counting)
Getter and setter functions required for all properties
Reference counting does not work with circular references
Debugging difficulties (thanks to gabr and Warren for pointing that out)
Adding to the answers few more advantages:
Use interfaces to represent the behavior and each implementation of a behavior will implement the interface.
API Publishing: Interfaces are great to use when publishing APIs. You can publishing an interface without giving out the actual implementation. So you are free to make internal structural changes without causing any problems to the clients.
All I say is that interfaces WITHOUT reference counting are VERY HIGH on my wishlist for delphi!!!
--> The real use of interfaces is the declaration of an interface. Not the ability for reference counting!
There are some SUBTLE downsides to interfaces that I don't know if people consider when using them:
Debugging becomes more difficult. I have seen a lot of strange difficulties stepping into interfaced method calls, in the debugger.
Interfaces in Delphi come with IUnknown semantics, if you like it or not, you'r stuck with reference counting being a supported interface. And, thus, with any interfaces created in Delphi's world, you have to be sure you handle reference counting correctly, and if you don't, you'll end up with leaks. When you want to avoid reference counting, your only choice is to override addref/decref and don't actually free anything, but this is not without its own problems. I find that the more heavily interface-laden codebases have some of the hardest-to-find access violations, and memory leaks, and this is, I think because it is very difficult to combine the refcount semantics, and the default delphi semantics (owner frees objects, and nobody else does, and most objects live for the entire life of their parents.).
Badly-done implementations using Interfaces can contribute some nasty code-smells. For example, Interfaces defined in the same unit that defines the initial concrete implementation of a class, add all the weight of interfaces, without really providing proper separation between the users of the interfaces and the implementors. I know this isn't a problem with interfaces themselves, but more of a quibble with those who write interface-based code. Please put your interface declarations in units that only have those interface declarations in them, and avoid unit-to-unit dependency hell caused by glomming your interface declarations into the same units as your implementor classes.
I mostly use interfaces when I want objects with different ancestry to offer a common service. The best example I can think of from my own experience is an interface called IClipboard:
IClipboard = interface
function CopyAvailable: Boolean;
function PasteAvailable(const Value: string): Boolean;
function CutAvailable: Boolean;
function SelectAllAvailable: Boolean;
procedure Copy;
procedure Paste(const Value: string);
procedure Cut;
procedure SelectAll;
end;
I have a bunch of custom controls derived from standard VCL controls. They each implement this interface. When a clipboard operation reaches one of my forms it looks to see if the active control supports this interface and, if so, dispatches the appropriate method.
For a very simple interface you can do this with an of object event handler, but once it gets sufficiently complex an interface works well. In fact I think that is a very good analogue. Use an interface where you a single of object event won't fit the functionality.
Interfaces solves a certain kind of issues. The primary function is to... well, ...define interfaces. To distinguish between definition and implementation.
When you want to specify or check if a class supports a set of methods - use interfaces.
You cannot do that in any other way.
(If all classes inherits from the same base class, then an abstract class will define the interface. But when you are dealing with different class hierarchies, you need interfaces to define the methods thy have in common...)
Extra note on
Cons: Performance
I think many people are too blithely dismissing the performance penalty of interfaces. (Not that I don't like and use interfaces but you should be aware of what you are getting into). Interfaces can be expensive not just for the _AddRef / _Release hit (even if you are just returning -1) but also that properties are REQUIRED to have a Get method. In my experience, most properties in a class have direct access for the read accessor (e.g., propery Prop1: Integer read FProp1 write SetProp1). Changing that direct, no penalty access to a function call can be significant hit on your speed (especially when you start adding 10s of property calls inside a loop.
For example, a simple loop using a class
for i := 0 to 99 do
begin
j := (MyClass.Prop1 + MyClass.Prop2 + MyClass.Prop3) / MyClass.Prop4;
MyClass.Update;
// do something with j
end;
goes from 0 function calls to 400 function calls when the class becomes an interface. Add more properties in that loop and it quickly gets worse.
The _AddRef / _Release penalty you can ameliorate with some tips (I am sure there are other tips. This is off the top of my head):
Use WITH or assign to a temp variable to only incur the penalty of one _AddRef / _Release per code block
Always pass interfaces using const keyword into a function (otherwise, you get an extra _AddRef / _Release occurs every time that function is called.
The only case when we had to use interfaces (besides COM/ActiveX stuff) was when we needed multiple inheritance and interfaces were the only way to get it. In several other cases when we attempted to use interfaces, we had various kinds of problems, mainly with reference counting (when the object was accessed both as a class instance and via interface).
So my advice would be to use them only when you know that you need them, not when you think that it can make your life easier in some aspect.
Update: As David reminded, with interfaces you get multiple inheritance of interfaces only, not of implementation. But that was fine for our needs.
Beyond what others already listed, a big pro of interfaces is the ability of aggregating them.
I wrote a blog post on that topic a while ago which can be found here: http://www.nexusdb.com/support/index.php?q=intf-aggregation (tl;dr: you can have multiple objects each implementing an interface and then assemble them into an aggregate which to the outside world looks like a single object implementing all these interfaces)
You might also want to have a look at the "Interface Fundamentals" and "Advanced Interface Usage and Patterns" posts linked there.
For a server-side plugin framework I would like to implement DLLs which expose a RegisterPlugin method that returns a class reference (TInterfacedClass).
The host application then creates the instance(s) of this class, and instances will run in the context of the host thread(s). (This is different for example from the Jedi VCL plugin framework which instantiates the plugin in the DLL or BPL and returns the instance to the host.)
First tests showed no problems so far. However, are there hidden problems with memory management I should be aware of? As I am using Delphi 2009 for this project, FastMM4 is the default memory manager.
Here a sketch of the plugin DLL project:
library ExamplePlugin;
uses
...
type
TPluginOne = class(TInterfacedObject, ...)
...
end;
function RegisterPlugin: TInterfacedClass; stdcall;
begin
Result := TPluginOne;
end;
exports
RegisterPlugin;
{ TPluginOne }
// ... plugin class implementation
begin
end.
No problems with memory manager, because FastMM works as a shared memory manager between the EXE and the DLL. But I'm really not comfortable with the concept of passing pure objects, or (worst) metaclasses between the DLL and the EXE. The problem is, TInterfacedObject from the EXE is not the same as TInterfacedObject from the DLL! Sure, they might look exactly the same, but they're not! And if you ever upgrade the version of Delphi for the EXE or for any of the DLL's, you'll need to rebuild everything (thus losing whatever advantage you gained from implementing a Plugin framework).
A much more portable solution would be to return a "Factory Interface", something along the lines of:
IFactoryInterface = interface
[GUId-goes-here]
function MakeWhateverInterfaceYouNeed: IUnknownDerivate
end;
then export a function with this signature:
function RegisterPlugin: IFactoryInterface;
Your code is incomplete but from what you have included there is one obvious flaw.
You appear to be exporting a class (TInterfacedClass) from a DLL. This is going to cause problems when clients try to consume your class with a different version of Delphi. What's more it's going to leave them helpless if they want to write plug-ins in a different language.
Personally I'd go for a COM based interface which will allow plug-in authors to create plug-ins in any language. This is in fact the very problem that COM was invented to solve.
If you are happy to be constrained to using the same compiler for plug-ins and host app, and you prefer to expose classes to COM interfaces, then you need to make sure that all deallocation is performed with the same memory manager as allocated the memory. The simplest way is to use ShareMem and then you'll be safe.
UPDATE
Cosmin points out in a comment another flaw with exporting classes across module boundaries. It's basically something that you shouldn't do. COM was designed for this very purpose and it still should be the first choice for you. Delphi interfaces which are COM compatible so you can get the same benefits of binary interoperability without having to create servers, register CLSID's etc.
I think your plug-in should look like this:
library ExamplePlugin;
type
TPluginOne = class(TInterfacedObject, IPlugin)
[GUID]
public
constructor Create(const Host: THostApp);
end;
function RegisterPlugin(const Host: IHostApp): IPlugin; stdcall;
begin
Result := TPluginOne.Create(Host);
end;
exports
RegisterPlugin;
begin
end.