Compago

...free knowledge

 
  • Increase font size
  • Default font size
  • Decrease font size
Home Manuali Programmazione Esempio di Out-Of-Process Automation Server in Delphi

Esempio di Out-Of-Process Automation Server in Delphi

E-mail Stampa PDF

La OLE Automation permette alle applicazioni o alle librerie di "esporre" degli oggetti da usare nel loro codice.
Queste librerie o applicazioni che espongono questi oggetti programmabili sono chiamate Automation Server.
Le applicazioni che invece accedono e usano questi oggetti sono chiamate Automation Controller o client.
La tecnologia COM permette ai controller di accedere ai server usando le funzioni esposte da questi ultimi, e le convenzioni sono indipendenti dal linguaggio di programmazione, in questo modo un server o un client possono essere programmati in linguaggi differenti e interagire tramite la tecnologia COM.
Gli oggetti di tipo Automation sono essenzialmente oggetti COM che implementano l'interfaccia IDispatch oltre a quella classica IUnKnown.

Quello che andremo a fare in questo articolo è creare un Out-Of-Process server COM, registrare la sua interfaccia e successivamente creare un semplice client per un test.
I COM server Out Of Process si differenziano da queli In Process perché essi sono inseriti all'interno di una applicazione, che in quanto tale li caricherà nella propria area di memoria, esterna a quella del client. I server In-Process sono invece contenuti in particolari librerie che vengono caricate nell'area di memoria del processo chiamante.
Ora non entreremo nel dettaglio sui meccanismi che caratterizzano queste cose, dato che questo articolo vuole solamente dimostrare la semplicità nella creazione di un COM server.

Quindi procediamo con la creazione di una form che chiameremo FormServer che conterrà solo un editbox.

Per creare un oggetto COM un automation object deve essere incluso nell'applicazione.
Dal menu New->Other->Activex scegliamo Automation Object.

Verrà richiesto il nome dell'oggetto che stiamo creando (MioServerCom) e altri parametri che però lasceremo inalterati.
In particolare il parametro di istanziamento, che ha senso solo per gli out of process server, può avere tre valori:

  • Internal : Il sistema non viene informato dell'oggetto, il quale potrà essere usato solo dalle applicazioni che lo conoscono.
  • Single instance: Ogni volta che un client chiede di creare un automation server verrà avviata l'applicazione che lo contiene.
  • Multiple instance: Ogni volta che un client chiede di creare l'automation server verrà avviata solo una volta l'applicazione che contiene l'oggetto COM, che a questo punto ne conterrà diversi. Chiudendo l'applicazione server verranno distrutte tutte le istanze dell'automation server.

Verranno creati alcuni file in automatico:

  • Server.tlb             - contiene la descrizione dell'oggetto COM da registrare
  • Server_TLB.pas   - contiene le dichiarazioni di interfaccia corrispondenti all'oggetto COM
  • UnitServer.pas     - contiene l'implementazione della classe relativa all'interfaccia dichiarata

L'interfaccia grafica per la gestione di un COM object è solamente un aiuto per creare un file tlb secondo uno standard che è identico per tutti i linguaggi di programmazione che devono creare un oggetto COM.

A questo punto nella view TLB il nostro oggetto (server) contiene due cose: una interfaccia (IMioServerCom) e una classe (MioServerCom).
Aggiungiamo un metodo chiamato MioMetodo all'interfaccia IMioServerCom.

Questo metodo deve avere un parametro di tipo string, in modo che al momento della ricezione del testo esso possa essere utilizzato per aggiornare la FormServer.

Il contenuto del relativo file tlb è il seguente:

[
  uuid(14EE9CE6-142E-46F7-8768-5D2D0853BF4A),   version(1.0),   helpstring("Server Library")  
]
library Server
{

  importlib("STDOLE2.TLB");

  [
    uuid(402FC553-034D-46F9-8EC7-71E96314F5D4), version(1.0), helpstring("Dispatch interface for MioServerCom Object"), dual, oleautomation
  ]
   interface IMioServerCom: IDispatch
  {
    [
    id(0x000000C9)
    ]
    HRESULT _stdcall MioMetodo([in] LPSTR testo );
  };

  [
    uuid(1F37B25C-8438-4730-826B-00455C3DAA0F), version(1.0), helpstring("MioServerCom Object")
  ]
  coclass MioServerCom
  {
    [default] interface IMioServerCom;
  };
};

Il file di interfaccia TLB in pascal conterrà tutte le dichiarazioni e le definizioni necessarie ad altri moduli o altre applicazioni per poter utilizzare  il COM server creato:

unit Server_TLB;

{$TYPEDADDRESS OFF} // Unit must be compiled without type-checked pointers.
{$WARN SYMBOL_PLATFORM OFF}
{$WRITEABLECONST ON}
{$VARPROPSETTER ON}
interface

uses Windows, ActiveX, Classes, Graphics, StdVCL, Variants;

// *********************************************************************//
// GUIDS declared in the TypeLibrary. Following prefixes are used:
// Type Libraries : LIBID_xxxx
// CoClasses : CLASS_xxxx
// DISPInterfaces : DIID_xxxx
// Non-DISP interfaces: IID_xxxx
// *********************************************************************//
const
// TypeLibrary Major and minor versions
MioMetodoMajorVersion = 1;
MioMetodoMinorVersion = 0;

LIBID_MioMetodo: TGUID = '{14EE9CE6-142E-46F7-8768-5D2D0853BF4A}';

IID_IMioServerCom: TGUID = '{402FC553-034D-46F9-8EC7-71E96314F5D4}';
CLASS_MioServerCom: TGUID = '{1F37B25C-8438-4730-826B-00455C3DAA0F}';
type

// *********************************************************************//
// Forward declaration of types defined in TypeLibrary
// *********************************************************************//
IMioServerCom = interface;
IMioServerComDisp = dispinterface;

// *********************************************************************//
// Declaration of CoClasses defined in Type Library
// (NOTE: Here we map each CoClass to its Default Interface)
// *********************************************************************//
MioServerCom = IMioServerCom;

// *********************************************************************//
// Interface: IMioServerCom
// Flags: (4416) Dual OleAutomation Dispatchable
// GUID: {402FC553-034D-46F9-8EC7-71E96314F5D4}
// *********************************************************************//
IMioServerCom = interface(IDispatch)
['{402FC553-034D-46F9-8EC7-71E96314F5D4}']
procedure MioMetodo(testo: PChar); safecall;
end;

// *********************************************************************//
// DispIntf: IMioServerComDisp
// Flags: (4416) Dual OleAutomation Dispatchable
// GUID: {402FC553-034D-46F9-8EC7-71E96314F5D4}
// *********************************************************************//
IMioServerComDisp = dispinterface
['{402FC553-034D-46F9-8EC7-71E96314F5D4}']
procedure MioMetodo(testo: OleVariant); dispid 201;
end;

// *********************************************************************//
// The Class CoMioServerCom provides a Create and CreateRemote method to
// create instances of the default interface IMioServerCom exposed by
// the CoClass MioServerCom. The functions are intended to be used by
// clients wishing to automate the CoClass objects exposed by the
// server of this typelibrary.
// *********************************************************************//
CoMioServerCom = class
class function Create: IMioServerCom;
class function CreateRemote(const MachineName: string): IMioServerCom;
end;

implementation

uses ComObj;

class function CoMioServerCom.Create: IMioServerCom;
begin
Result := CreateComObject(CLASS_MioServerCom) as IMioServerCom;
end;

class function CoMioServerCom.CreateRemote(const MachineName: string): IMioServerCom;
begin
Result := CreateRemoteComObject(MachineName, CLASS_MioServerCom) as IMioServerCom;
end;
end.

Ora tra le altre cose è sta creata una unit (UnitServer) che contiene l'implementazione della classe TMioserverCom, la quale eredita la classe TAutoObject e sopratutto l'interfaccia che abbiamo descritto nei file precedenti IMioServerCom:

unit UnitServer;

{$WARN SYMBOL_PLATFORM OFF}

interface

uses
ComObj, ActiveX, Server_TLB, StdVcl;

type
TMioServerCom = class(TAutoObject, IMioServerCom)
protected
procedure MioMetodo(testo: PChar); safecall;
end;

implementation

uses ComServ,UnitForm;

procedure TMioServerCom.MioMetodo(testo: PChar);
begin
FormServer.Edit1.Text:=testo;
end;

initialization
TAutoObjectFactory.Create(ComServer, TMioServerCom, Class_MioServerCom, ciMultiInstance, tmApartment);
end.

Le parti in nero sono state create automaticamente mentre le parti in rosso sono quelle inserite da me per interagire con la form della applicazione, in pratica tramite questo server COM è possibile invocare la procedura MioMetodo e modificare il testo nell'editbox sulla form.
L'unica cosa che rimane da fare è registrare l'oggetto com apppena creato, in modo che un qualsiasi altro programma lo possa usare.

Con un visualizzatore per gli oggetti com di windows potremo verificare l'effettiva creazione:

Qui possiamo in effetti vedere come è stato registrato il nostro server, ed in particolare di come il sistema ad una eventuale richiesta di quella determinata interfaccia vada a caricare in memoria l'applicazione che la contiene (server.exe).
Da notare che nella terza riga il nostro COM server è riconosciuto come Out Of Process e quindi indicato come LocalServer32.

Ora per provare il funzionamento del server com creiamo un semplice client che consiste in una form con un pulsante ed un testo.

Per poter usare il server COM creato precedentemente dovremo importare il suo file TLB (Server_TLB.pas) contenente la descrizione dell'interfaccia.
Il compito del client è quello di prendere il testo che noi inseriremo nella form client e visualizzarlo sulla form server:

procedure TFormClient.Button1Click(Sender: TObject);
var
Server: IMioServerCom; //Notare che la variabile è di tipo IMioServerCom, cioè una istanza di una interfaccia
begin
Server := CoMioServerCom.Create; //Connessione al server com e se non in esecuzione viene creato
//Notare che quella che viene creata è una istanza della classe
//che implementa l'interfaccia con la funzione CoMioserverCom.Create

Server.MioMetodo(PChar(Edit1.Text)); //viene invocato un suo metodo
//Notare che noi possiamo vederlo tra i metodi perché appartiene ad una interfaccia
//ma possiamo usare il suo codice perché è implementato in una classe

end;

Quello che accadrà è semplice se avviamo il server.exe e poi il client.exe, premendo il pulsante sul client verrà creata una connessione con una istanza del server e il testo sul client e verrà scritto sul server.

Se il server non fosse stato avviato, al momento della connessione esso verrebbe creato, ma dato che la variabile Server di tipo IMioServerCom all'interno della funzione Button1Click è locale, al termine della funzione l'oggetto COM verrà distrutto e l'applicazione server.exe terminata.
Questo accade perché questo tipo di oggetti sono garbage collected o life managed, che in pratica significa che non è necessari pensare a distruggere queste variabili dato che tutto il processo viene gestito in automatico.
Una piccola modifica che potremo fare è quella di rendere la variabile Server globale:

unit UnitClient;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs , Server_TLB, StdCtrls;

type
TFormClient = class(TForm)
Button1: TButton;
Edit1: TEdit;
procedure Button1Click(Sender: TObject);
end;

var
FormClient: TFormClient;
Server: IMioServerCom;

implementation

{$R *.dfm}

procedure TFormClient.Button1Click(Sender: TObject);
begin
Server := CoMioServerCom.Create;
Server.MioMetodo(PChar(Edit1.Text));
end;

end.

In questo caso il server com rimarrà in vita fino alla terminazione del client.

Naturalmente questo è solo un esempio di come procedere poi ognuno è libero di personalizzare il procedimento, anche perché il contesto può essere molto diverso e complicato a seconda dei casi.
In particolare a seconda del modello di threading scelto occorre fare particolare attenzione alle risorse condivise dai vari thread e client.

Ultimo aggiornamento ( Sabato 31 Luglio 2010 10:21 )  
Loading

Login




Chiudi