I have to develop plugin system for non-gui app that I'm developing. I envisioned this as core app that has basic set of features, and which is extendable with plugins.
Now, I figured out that probably best way to do this is to make plugins as DLLs, and to load them within host app. Plugins should be able to modify some parts of app core (access to certain methods/variables), and this is tricky part.
What I've thought is to make THost class, which implements IHost and IHostExposed interfaces. When host loads plugin, it will pass IHostExposed to plugin, and plugin could call methods/access to variables in that interface. Something like this:
Interface declarations:
unit uHostInterfaces;
interface
type
IHost = interface
['{BAFA98BC-271A-4847-80CE-969377C03966}']
procedure Start;
procedure Stop;
end;
// this intf will get exposed to plugin
IHostExposed = interface
['{1C59B1A9-EC7A-4D33-A574-96DF8F5A7857}']
function GetVar1: Integer;
function GetVar2: String;
procedure SetVar1(const AValue: Integer);
procedure SetVar2(const AValue: String);
property Var1: Integer read GetVar1 write SetVar1;
property Var2: String read GetVar2 write SetVar2;
end;
implementation
end.
Host class declaration:
unit uHost;
interface
uses
Winapi.Windows, Winapi.Messages,
uHostInterfaces, uInstanceController, uSettings;
type
THost = class(TInterfacedObject, IHost, IHostExposed)
private
FVar1 : Integer;
FVar2 : String;
FWindowHandle : HWND;
FInstanceController: TInstanceController;
FSettings : TSettings;
procedure WndProc(var AMessage: TMessage);
public
constructor Create;
destructor Destroy; override;
// methods from IHost
procedure Start;
procedure Stop;
// methods from IHostExposed, which get set Var1/Var2
function GetVar1: Integer;
function GetVar2: string;
procedure SetVar1(const AValue: Integer);
procedure SetVar2(const AValue: string);
end;
implementation
...
...and how i'd use it:
type
TRegisterPlugin = procedure(const AHostExposed: IHostExposed);
var
hdll : THandle;
RegisterPlugin: TRegisterPlugin;
host : IHost;
begin
host := THost.Create;
hdll := LoadLibrary('plugin.dll');
if hdll <> 0 then
begin
#RegisterPlugin := GetProcAddress(hdll, 'RegisterPlugin');
if Assigned(RegisterPlugin) then
begin
// call the plugin function and pass IHostExposed interface to it
// from there on, plugin can use this interface to interact with core app
RegisterPlugin(host as IHostExposed);
...
What I would like to hear is any suggestions about this approach, and if there are better solutions for what I'm trying to achieve?
Apparently you've worked with interfaces before, but you don't know the component registration features of COM? Use the new project wizard to start an ActiveX library with Automation Objects, have a look around the Type Library editor, and see what happens when the library runs and registers itself (it's all there in System.Win.ComServ.pas)
Related
I have to call many DLL functions of a class.
here is a sample of function signature:
type
loaderClass = class
public
procedure proceA(x : Integer); virtual; stdcall; abstract;
procedure proceA1(x : Integer); virtual; stdcall; abstract;
procedure proceB(y : Integer; name : PAnsiChar); virtual; stdcall; abstract;
procedure proceB1(y : Integer; name : PAnsiChar); virtual; stdcall; abstract;
procedure proceC(z : Integer; name : string); virtual; stdcall; abstract;
procedure proceC1(z : Integer; name : string); virtual; stdcall; abstract;
procedure proceD(d : Double; c : Char); virtual; stdcall; abstract;
procedure proceD1(d : Double; c : Char); virtual; stdcall; abstract;
.....
.....
.....
end;
I can use if-else or case to call each procedure. But I want to skip the long list of if-else block.
at runtime, my program can find the name of the procedure to be called.
So is it possible to call all functions by passing with its name string and parameters.
Somthing like this sample:
//Variant parameters
args: array of const;
cArray : array of AnsiChar;
functionName: string;
loader := loaderClass.Create;
functionName := 'proceB';
args[0] := 10;
cArray := 'Hello';
args[1] := cArray;
//runtime it should call proceB(10, 'Hello');
loader.functionName(args[0], args[1]);
There are different ways of doing that.
One of the best IMO is to create an "automation server" with Delphi to put your code in an external file. This is part of Windows COM features. See Automation Servers and Automation Clients in Microsoft website: Automation makes it possible for your application to manipulate objects implemented in another application, or to expose objects so they can be manipulated.
Once your code is in an automation server, it is automatically auto-descriptive, that is the caller - in another application - can discover all the methods, their arguments, return values and call it.
Delphi has no only what is required to create automation server and automation client without writing much code yourself!
Calling an automation server is frequently used to call Office applications from a Delphi program.
Writing an automation server can do just the reverse: have Microsoft Office or any application able to use automation server (And I just said Delphi can do it easily) to call methods in the automation server.
Long time ago I wrote blog posts about both:
Automation server:
http://francois-piette.blogspot.com/2013/01/microsoft-word-or-excel-calls-delphi.html
Automation client:
http://francois-piette.blogspot.com/2013/01/automate-microsoft-office-from-delphi.html
Those blog posts use Microsoft Office for the second tier but of course you can have Delphi in both client and server side.
If the DLL is already written, then you can encapsulate it in an automation server to benefit for the features.
in order to create a component, I created a designtime and runtime packages, runtime package (lets call it RP140) contains the code of my component and requires rtl.dcp, designtime package (lets call it DclRP140) contains register procedure and requires DesignIDE, runtime package and rtl.dcp. Now I need to access private variables declared in the unit that belongs to "DclRP140" package, from another unit that belongs to "RP140", I created a simple code which contains the relevant part, just to make it easier to understand:
unit MyComponentRegister;
interface
uses Classes, MyComponent;
type
TEvent = procedure(sender: TObject) of object;
TMyComponent = class(TComponent)
private
FMyproperty: String;
FMyEvent: TEvent;
public
constructor Create(AOwner: TComponent); override;
published
property myProperty: String read FMyproperty write FMyproperty
default initial_value;
property myEvent: TEvent read FMyEvent write FMyEvent;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [TMyComponent]);
end;
Constructor TMyComponent.Create(AOwner: TComponent);
begin
inherited;
FMyproperty := initial_value;
end;
end.
the other unit contains the main code of my component:
unit myComponent;
interface
uses
SysUtils, Classes;
type
TMyComponent = class(TComponent)
public
procedure myProcedure(avalue: string);
end;
implementation
procedure TMyComponent.myProcedure(avalue: string);
begin
FMyproperty := avalue; // I want to access to FMyproperty
if assigned(Fmyevent) then // I want to access to fMyEvent
// do some work
end;
end.
So first, I have to tell you that I'm new to creating packages and components, so am I doing it the right way? or is there something wrong?
Second,as I said before, what I want to do is to access to private variables declared in 'MyComponentRegister' from 'myComponent', I tried many tricks but none of them worked, for sure there is a way to do that, but I cant find it with my limited experience.So, how I can I solve this one??
Your problem is that you are trying to define your component in multiple places. That's not possible. Your code declares two distinct classes. That's one more than you need.
Do it like so:
unit MyComponent;
interface
uses
Classes;
type
TEvent = procedure(sender: TObject) of object;
TMyComponent = class(TComponent)
private
FMyproperty: String;
FMyEvent: TEvent;
public
constructor Create(AOwner: TComponent); override;
procedure myProcedure(avalue: string);
published
property myProperty: String read FMyproperty write FMyproperty;
property myEvent: TEvent read FMyEvent write FMyEvent;
end;
implementation
constructor TMyComponent.Create(AOwner: TComponent);
begin
inherited;
end;
procedure TMyComponent.myProcedure(avalue: string);
begin
FMyproperty := avalue;
if assigned(FMyEvent) then
; // do some work
end;
end.
This unit is included in both design time and run time packages.
unit MyComponentRegister;
interface
uses
Classes, MyComponent;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [TMyComponent]);
end;
end.
This second unit is included only in your design time package. Note that it does not defined the component because that is defined in the MyComponent which is uses.
I offloaded all ADO hood in a separate Data Module, so a single module can be referred by several applications. All my applications basically need only two worker methonds to access data:
AdoQuery delivers a result set in a form of TADODataSet.
AdoExecute performs simple update/delete queries without fetching any results.
Here is the class structure:
type
TMyDataModule = class(TDataModule)
procedure DataModuleCreate(Sender: TObject);
procedure DataModuleDestroy(Sender: TObject);
private
procedure pvtAdoConnect;
procedure pvtAdoExecute(const sql: string);
function pvtAdoQuery(const sql: string): TADODataSet;
public
AdoConnection: TADOConnection;
end;
Then I added two publicly exposed wrappers to class methods. I used that to avoid long class references in the calls:
function AdoQuery(const sql: string): TADODataSet;
procedure AdoExecute(const sql: string);
implementation
function AdoQuery(const sql: string): TADODataSet;
begin
Result := MyDataModule.pvtAdoQuery(sql);
end;
Above are the worker function which I call from within all my forms.
AdoConnect runs only once on DataModuleCreate event. TDatModule derived from TPersistent, which allows to persist the single instance of connection throughout a runtime.
The only thing that annoys me so far - is a useless .DFM which I don't need at all.Is there any option to get rid of it?
I would handle this type of thing in one of two ways, with interfaces or with inheritance. I prefer not to expose classes to the outside world in these cases. The second one could almost be called an interface without interfaces :)
Interfaces
This version returns an interface that includes the required methods. The outside world only needs to use the interface. We keep the implementation details private. Our TMyDBClass implements the interface that we have exposed to the outside world and our global function GetDBInterface returns the single instance.
interface
uses
ADODB;
type
IMyDBInterface = interface
['{2D61FC80-B89E-4265-BB3D-93356BD613FA}']
function AdoQuery(const sql: string): TADODataSet;
procedure AdoExecute(const sql: string);
end;
function GetDBInterface: IMyDBInterface;
implementation
type
TMyDBClass = class(TInterfacedObject, IMyDBInterface)
strict private
FConnection: TADOConnection;
protected
procedure AfterConstruction; override;
procedure BeforeDestruction; override;
public
function AdoQuery(const sql: string): TADODataSet;
procedure AdoExecute(const sql: string);
end;
var
FMyDBInterface: IMyDBInterface;
procedure TMyDBClass.AdoExecute(const sql: string);
begin
// ...
end;
function TMyDBClass.AdoQuery(const sql: string): TADODataSet;
begin
// ...
end;
procedure TMyDBClass.AfterConstruction;
begin
inherited;
FConnection := TADOConnection.Create(nil);
end;
procedure TMyDBClass.BeforeDestruction;
begin
FConnection.Free;
inherited;
end;
// Our global function
function GetDBInterface: IMyDBInterface;
begin
if not Assigned(FMyDBInterface) then
FMyDBInterface := TMyDBClass.Create;
Result := FMyDBInterface;
end;
initialization
finalization
FMyDBInterface := nil;
end.
Inheritance
This version uses a base class that has the required methods. This is a bit easier for people to deal with because it excludes the interface which can be complex to people starting out. Again we hide the implementation details from the user and only expose a shell of a class that includes the two methods we want people to access. The implementation of these methods is performed by a class in the implementation that inherits from the exposed class. We also have a global function that returns the instance of this class. The big advantage that the interface approach has over this approach is that the user of this object can't free the object by accident.
interface
uses
ADODB;
type
TMyDBClass = class(TObject)
public
function AdoQuery(const sql: string): TADODataSet; virtual; abstract;
procedure AdoExecute(const sql: string); virtual; abstract;
end;
function GetDBClass: TMyDBClass;
implementation
type
TMyDBClassImplementation = class(TMyDBClass)
strict private
FConnection: TADOConnection;
protected
procedure AfterConstruction; override;
procedure BeforeDestruction; override;
public
function AdoQuery(const sql: string): TADODataSet; override;
procedure AdoExecute(const sql: string); override;
end;
var
FMyDBClass: TMyDBClassImplementation;
procedure TMyDBClassImplementation.AdoExecute(const sql: string);
begin
inherited;
// ...
end;
function TMyDBClassImplementation.AdoQuery(const sql: string): TADODataSet;
begin
inherited;
// ...
end;
procedure TMyDBClassImplementation.AfterConstruction;
begin
inherited;
FConnection := TADOConnection.Create(nil);
end;
procedure TMyDBClassImplementation.BeforeDestruction;
begin
FConnection.Free;
inherited;
end;
// Our global function
function GetDBClass: TMyDBClass;
begin
if not Assigned(FMyDBClass) then
FMyDBClass := TMyDBClassImplementation.Create;
Result := FMyDBClass;
end;
initialization
FMyDBClass := nil;
finalization
FMyDBClass.Free;
end.
Usage
Usage of these are really easy.
implementation
uses
MyDBAccess; // The name of the unit including the code
procedure TMyMainForm.DoSomething;
var
myDataSet: TADODataSet;
begin
myDataSet := GetDBInterface.AdoQuery('SELECT * FROM MyTable');
...
// Or, for the class version
myDataSet := GetDBClass.AdoQuery('SELECT * FROM MyTable');
...
end;
If you don't have any design-time non-visual components dropped onto your data module, and don't plan to ever do so, then you shouldn't need a data module at all. The whole purpose is for design-time components, and other implementations such as a Web Module or even a Windows Service Application. But not for wrapping pure code without design-time components.
Also, as mentioned in the comments, don't be confused about the meaning of TPersistent. This class is used entirely differently, and can be integrated into the IDE Object Inspector (as sub-properties within a component).
So the ideal thing for you to do is encapsulate everything in just a single class. For your purpose, a database connection...
type
TMyData = class(TObject)
private
FConnection: TADOConnection;
public
constructor Create;
destructor Destroy; override;
procedure pvtAdoConnect;
procedure pvtAdoExecute(const sql: string);
function pvtAdoQuery(const sql: string): TADODataSet;
...
end;
implementation
{ TMyData }
constructor TMyData.Create;
begin
FConnection:= TADOConnection.Create(nil);
end;
destructor TMyData.Destroy;
begin
FConnection.Connected:= False;
FConnection.Free;
inherited;
end;
As for the interpretation of being "persistent", you can create/destroy an instance of it in many ways. For example, you could use a unit's initialization and finalization sections (requiring CoInitialize) or you can have your main form initialize a global instance upon creation.
One common way of doing so is to add...
interface
function MyData: TMyData;
implementation
var
_MyData: TMyData;
function MyData: TMyData;
begin
if not Assigned(_MyData) then
_MyData:= TMyData.Create;
Result:= _MyData;
end;
initialization
_MyData:= nil;
finalization
_MyData.Free;
end.
The first time you call MyData from anywhere, a new global instance will be instantiated. Then, every further time it re-uses the same instance. This also solves the need of ActiveX and CoInitialize etc. because at this point, COM is expected to already be instantiated (which is required for ADO).
Usage of this unit would be extremely simple - use include it in the uses anywhere, and access its instance through the MyData function.
Notes
You should get out of the habit of global variables. This is asking for trouble down the road when trying to do later work. The example above shows how to accommodate for a global object instance. All other variables should be self-contained within that object, or in general one of scope / relevance. The whole control of your TADOConnection should be within here, including connecting/disconnecting, exception handling, assigning the connection string.
In case you might be interested in an alternative without DataModules alltogether, have a look at this:
https://github.com/stijnsanders/xxm/blob/master/Delphi/demo2/03%20Data%20ADO/xxmData.pas
Queries are stored in a single .sql file, which is handy to edit it in specific SQL editors or workbenches. Queries are separated with a line with --"QueryName", and loaded in a query-store on start-up. Assuming you query for smaller recordsets most of the time, the best lock and open style is read-only and static, which offers the best possible performance and least load on the database server. Getting field values uses the Collect call which also offers a little performance gain.
I have a parent class that can have 2 possible child classes:
TEmailBaseAccount = class
Connected: boolean;
setting: TEmailAccountSettings;
folders: TEmailAccountFolders;
procedure Connect; virtual; abstract;
end;
TEmailIMAPAccount = class(TEmailBaseAccount)
IdIMAP4: TIdIMAP4;
OpenSSLHandler: TIdSSLIOHandlerSocketOpenSSL;
procedure Connect; override;
end;
TlEmailPOP3Account = class(TEmailBaseAccount)
IdPOP3: TIdIPOP3;
OpenSSLHandler: TIdSSLIOHandlerSocketOpenSSL;
procedure Connect; override;
end;
I'm maintaining a list of the email accounts using a generic TList:
TEmailAccountList = class(TList<TEmailBaseAccount>)
procedure SaveToStream(Stream: TStream);
procedure LoadFromStream(Stream: TStream);
constructor Create(AOwner: TObject);
destructor Destroy;
end;
and adding the email accounts to the list using the following code:
procedure TEmailAccountList.LoadFromStream(Stream: TStream);
var
a, c: Integer;
e: TEmailBaseAccount;
begin
c := ReadStreamInt(Stream);
for a := 0 to c - 1 do
begin
e := TEmailBaseAccount.Create(FOwnerEmailEngine);
e.LoadFromStream(Stream);
Add(e);
end;
end;
procedure TEmailAccountList.SaveToStream(Stream: TStream);
var
a, c: Integer;
e: TEmailBaseAccount;
begin
c := Count;
WriteStreamInt(Stream, c);
for a := 0 to Count - 1 do
Items[a].SaveToStream(Stream);
end;
At runtime I need to differentiate between the 2 types of child classes using something like:
if account is TEmailIMAPAccount then
...
else if account is TEmailPOP3Account then
...
I am sure that my original class declarations and the TList declaration is not suited to this requirement. What changes are needed in this scenario?
TIA.
Your type declarations are absolutely fine. Your problem is presumably that when you read an item from the stream, you don't know what type it is. You cannot use is since you don't have an instance yet.
Solve that problem by writing a type code to the stream for each instance. When you read from the stream, read the type code and use that to determine which type to instantiate.
This sort of persistence streaming is so much easier using a persistence framework that emits XML, JSON, YAML etc.
Instead of serializing the complete, highly implementation-specific objects, I would only write the account properties (mail account type, user credentials, server / port / security settings) to a file.
This allows to modify the implemenation without breaking existing setting file compatibility.
Also I would not even think about a if <object> is <class> ... else if <object> is <otherclass> ... solution. Instead, define a simple enumeration type TAccountType = (atPOP3, atIMAP) and then branch in a case structure depending on a account type property of the Account, or use the Strategy pattern.
I know Delphi XE2 has the new TVirtualInterface for creating implementations of an interface at runtime. Unfortunately I am not using XE2 and I'm wondering what kind of hackery is involved in doing this sort of thing in older versions of Delphi.
Lets say I have the following interface:
IMyInterface = interface
['{8A827997-0058-4756-B02D-8DCDD32B7607}']
procedure Go;
end;
Is it possible to bind to this interface at runtime without the help of the compiler?
TMyClass = class(TObject, IInterface)
public
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
procedure Go; //I want to dynamically bind IMyInterface.Go here
end;
I've tried a simple hard cast:
var MyInterface: IMyInterface;
begin
MyInterface := IMyInterface(TMyClass.Create);
end;
but the compiler prevents this.
Then I tried an as cast and it at least compiled:
MyInterface := TMyClass.Create as IMyInterface;
So I imagine the key is to get QueryInterface to return a valid pointer to an Implementation of the interface being queried. How would I go about constructing one at runtime?
I've dug through System.pas so I'm at least vaguely familiar with how GetInterface, GetInterfaceEntry and InvokeImplGetter work. (thankfully Embacadero chose to leave the pascal source along with the optimized assembly). I may not be reading it right but it appears that there can be interface entries with an offset of zero in which case there is an alternative means of assigning the interface using InvokeImplGetter.
My ultimate goal is to simulate some of the abilities of dynamic proxies and mocks that are available in languages with reflection support. If I can successfully bind to an object that has the same method names and signatures as the interface it would be a big first step. Is this even possible or am I barking up the wrong tree?
Adding support for an interface to an existing class at runtime can theoretically be done, but it would be really tricky, and it would require D2010 or later for RTTI support.
Each class has a VMT, and the VMT has an interface table pointer. (See the implementation of TObject.GetInterfaceTable.) The interface table contains interface entries, which contain some metadata, including the GUID, and a pointer to the interface vtable itself. If you really wanted to, you could create a copy of the interface table, (DO NOT do this the original one; you're likely to end up corrupting memory!) add a new entry to it containing a new interface vtable with the pointers pointing to the correct methods, (which you could match by looking them up with RTTI,) and then change the class's interface table pointer to point to the new table.
Be very careful. This sort of work is really not for the faint of heart, and it seems to me it's of kind of limited utility. But yes, it's possible.
I'm not sure, what you want to accomplish and why you want to dynamically bind that interface, but here is a way to do it (don't know if it fits your need):
type
IMyInterface = interface
['{8A827997-0058-4756-B02D-8DCDD32B7607}']
procedure Go;
end;
TMyClass = class(TInterfacedObject, IInterface)
private
FEnabled: Boolean;
protected
property Enabled: Boolean read FEnabled;
public
constructor Create(AEnabled: Boolean);
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
procedure Go; //I want to dynamically bind IMyInterface.Go here
end;
TMyInterfaceWrapper = class(TAggregatedObject, IMyInterface)
private
FMyClass: TMyClass;
protected
property MyClass: TMyClass read FMyClass implements IMyInterface;
public
constructor Create(AMyClass: TMyClass);
end;
constructor TMyInterfaceWrapper.Create(AMyClass: TMyClass);
begin
inherited Create(AMyClass);
FMyClass := AMyClass;
end;
constructor TMyClass.Create(AEnabled: Boolean);
begin
inherited Create;
FEnabled := AEnabled;
end;
procedure TMyClass.Go;
begin
ShowMessage('Go');
end;
function TMyClass.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
if Enabled and (IID = IMyInterface) then begin
IMyInterface(obj) := TMyInterfaceWrapper.Create(Self);
result := 0;
end
else begin
if GetInterface(IID, Obj) then
Result := 0
else
Result := E_NOINTERFACE;
end;
end;
And this is the corresponding test code:
var
intf: IInterface;
my: IMyInterface;
begin
intf := TMyClass.Create(false);
if Supports(intf, IMyInterface, my) then
ShowMessage('wrong');
intf := TMyClass.Create(true);
if Supports(intf, IMyInterface, my) then
my.Go;
end;