Compago

...free knowledge

 
  • Increase font size
  • Default font size
  • Decrease font size
Home Manuali Programmazione Registrare un oggetto COM

Registrare un oggetto COM

E-mail Stampa PDF

Introduzione

Registrare un oggetto COM

A volte ci può essere confusione su cosa si intenda per registrare un oggetto COM, per questo motivo definiremo come registrare un oggetto l'azione di registrare la sua classe in modo che i vari client, o forse sarebbe meglio chiamarli controller, possano creare e usare una o più istanze dell'oggetto.
Infatti il destino della maggior parte di questi oggetti COM è quella di essere esposti, in modo da poter essere usati da altre applicazioni.

Registrare l'istanza di un oggetto COM

La registrazione della classe viene spesso confusa con la registrazione dell'istanza dell'oggetto. Infatti un'altra tecnica, usata molte volte quando si vuole far connettere più controller allo stesso server, è quella di registrare l'istanza dell'oggetto COM nella ROT, ovvero Running Object Table.
In questo modo una singola istanza dell'automation object sarà accessibile da più client usando la funzione GetActiveOleObject,  e tutti potranno usare lo stesso oggetto, come verrà spiegato più avanti.

Esempi

Registrare un oggetto COM con l'IDE Delphi

Per registrare o cancellare dai registri di sistema un automation server, che esso sia un in-process o out of process, può essere fatto tramite l'IDE di Delphi coi comandi:

Run->Register ActiveX Server.
Run->Unregister ActiveX Server.

Se di tratta di un out of process server, quindi di un eseguibile, allora è possibile registrarlo modificando i parametri di avvio:

Run->Parameters

A seconda che si voglia registrare o cancellare un oggetto impostare:

/regserver
/unregserver

Registrare un oggetto COM da riga di comando

La stessa tecnica descritta per gli out of process server può essere attuata che da riga di comando; infatti se l'applicazione che contiene l'automation server si chiama MioServer.exe, per registrarla basterà dare il comando:

MioServer.exe /regserver

per cancellarla dai registri usare il parametro /unregserver

Se si vuole registrare una DLL o un eseguibile che contiene un oggetto COM sempre da riga di comando è possibile usare il comando RegSvr32.
La sintasi del comando RegSvr32 è la seguente:

Regsvr32 [/u] [/s] [/n] [/i[:cmdline]] dllname
 /s - Silent; non viene mostratto alcun messaggio
 /u - Unregister server
 /i - Chiama la funzione DllInstall passandogli i parametri opzionali [cmdline];
      se usata insieme all'opzione /u chiamerà la funzione dll uninstall
 /n - non chiama la funzione DllRegisterServer; questa opzione deve essere usata insieme a /i

Quindi per registrare il mio server o la mia DLL :

regsvr32 mioserver.exe

Mentre per cancellarlo dai registri:

regsvr32 /u mioserver.exe

In pratica questo comando non fa altro che caricare la libreria e richiamare il suo metodo DllRegisterServer.

Registrare un oggetto COM da codice

Questa modalità ci permette di registrare o cancellare un oggetto com dai registri dalla stessa applicazione o da un'altra anche a run time.
In pratica si tratta di richiamare i comandi descritti nei paragrafi precedenti e per fare questo il modo più semplice è quello di usare la funzione ShellExecute contenuta nella unit ShellApi:

ShellExecute(Handle,'open',Pchar(Application.ExeName),'/regserver',nil,SW_SHOWNORMAL);

Nel caso di una libreria:

ShellExecute(Handle,'open','regsvr32',Pchar(Application.ExeName),nil,SW_SHOWNORMAL);

Ora se vogliamo che il COM server , che sia esso una libreria o un eseguibile, la cosa migliore è quela di usare la funzione DllRegisterServer, che è già pronta all'uso, dato che sicuramente avremo importato la unit ComObj.
Quindi non dovremo fare altro che richiamare questa funzione in fase di caricamento o inizializzazione.
Riporto in seguito uno schema di codice:

unit UnitServer;

{$WARN SYMBOL_PLATFORM OFF}

interface

uses
ComObj, ActiveX, Server_TLB, StdVcl;

type
TMioServerCom = class(TAutoObject, IMioServerCom)
protected
....
end;

implementation

uses ComServ;

....

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

DllRegisterServer;
end.

In questo modo quando viene avviato l'eseguibile, l'automation server si auto-registra.

Allo stesso modo l'applicazione può chiamare la funzione DllUnregisterServer per cancellare se stessa dai registri.

Registrare una istanza  di un oggetto COM

Solitamente ogni volta che un controller (client) si connette ad un Automation Server viene creata una istanza dell'oggetto COM che viene definito da una particolare classe. Questo permette di condividere il codice dell'oggetto tra più applicazioni, ma ogni istanza ha i propri dati. Se si vuole fare in modo che la stessa istanza dell'oggetto venga usata da più controller, allora è necessario che essa venga "esposta" registrandola nella ROT. In questo modo i client potrebbero usare l'istanza dell'oggetto attivo piuttosto che ricrearlo di nuovo.

Per registrare una istanza di un oggetto COM useremo la funzione RegisterActiveObject, la cui sintassi è:

HRESULT RegisterActiveObject
(
LPUNKNOWN punk,
REFCLSID rcid,
DWORD dwFlags,
LPDWORD pdwRegister
)

importata in pascal come:

function RegisterActiveObject( unk: IUnknown; 
                               const clsid: TCLSID;
                               dwFlags: Longint;
                               out dwRegister: Longint): HResult; stdcall;

I parametri sono:

unk [In] L'oggetto da registrare
clsid [In] Il suo CLSID.
dwFlags [In] Opzioni
dwRegister [Out] Handle della registrazione dell'oggetto

In particolare il primo parametro è un riferimento all'interfaccia IUnkown dell'oggetto da registrare (tutti gli oggetti COM implementano questa interfaccia).
Il terzo parametro definisce il tipo di registrazione che può essere:

  • 0 - ACTIVEOBJECT_STRONG
  • 1 - ACTIVEOBJECT_WEAK

La registrazione di tipo strong significa che la API chiamerà la funzione _AddRef dell'oggetto e quindi verrà incrementato il contatore dei riferimenti. Quando tutti i client di questo oggetto si saranno disconnessi l'oggetto non verrà distrutto perché il contatore non è ancora zero. Quindi per rimuoverlo dalla ROT bisognerà chiamare esplicitamente la funzione RevokeActiveObject.
Una registrazione weak invece non verrà incrementato il contatore dei riferimenti, quindi questo contatore verrà incrementato per ogni client che fa riferimento all'oggetto e decrementato quando il client rilascia l'oggetto. Quando tutti i client avranno rilasciato l'oggetto questo verrà rimosso e non sarà più presente nella ROT.
L'ultimo parametro è solamente una double word che contiene un numero che identificherà la registrazione appena eseguita e che è utile per la funzione RevokeActiveObject per revocare la registrazione nella ROT.

function RevokeActiveObject(dwRegister: Longint; pvReserved: Pointer): HResult; stdcall;

La registrazione è bene che sia eseguita al momento dell'inizializzazione dell'oggetto, mentre la revoca al momento della sua distruzione.
Esempio:

type
TMioServerCom = class(TAutoObject, IConnectionPointContainer, IMioServerCom)
private
FObjRegHandle: Integer;
public
procedure Initialize; override;
destructor Destroy; override;
end;

implementation
 
procedure TMioServerCom.Initialize;
begin
inherited Initialize;
RegisterActiveObject(Self as IUnknown, CLASS_MioServerCom, ACTIVEOBJECT_WEAK, FObjRegHandle);
end;

destructor TMioServerCom.Destroy;
begin
RevokeActiveObject(FObjRegHandle, nil);
inherited;
end;

Sarebbe buona regola ogni qualvolta si chiama una funzione che restituisce un HResult usare la funzione OleCheck per controllare questo risulato e mostrare all'utente un messaggio comprensibile. Esempio:

OleCheck(RevokeActiveObject(FObjRegHandle, nil));



Ora dal punto di vista del controller che vorrà usare l'oggetto potrà farlo andando a prenderlo dalla ROT, sempre che ve ne sia uno attivo, con la funzione GetActiveObject:

function GetActiveObject(const clsid: TCLSID; pvReserved: Pointer;
out unk: IUnknown): HResult; stdcall;

Esempio:

procedure ConnectCOM;
var

ActiveObj: IUnknown;
begin
GetActiveObject(CLASS_MioServerCom, nil, ActiveObj);
if ActiveObj <> nil then FServer := ActiveObj as IMioServerCom
else FServer := CoMioServerCom.Create;
end;

In questo esempio la funzione cerca di connettersi a una istanza dell'oggetto della ROT, se però la funzione non ha un esito positivo, magari perché non è ancora stata creata nessuna istanza, allora l'oggetto verrà creato.


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

Login