Capítulo 4. Automatización

Anuncio
156 Capítulo 4. Automatización
Automatizando una aplicación existente
En esta sección vamos a tomar una aplicación existente y añadirle capacidades de
automatización. La aplicación es un programa muy sencillo, que presenta un editor
multilíneas y dos botones sobre la ventana principal. Un botón cambia el tipo de
letra del editor, y el otro su color. Nada excepcional, pero servirá para ilustrar los
puntos que deseamos presentar.
Existen muchos casos en los que podríamos des ear automatizar una aplicación
existente. Por ejemplo, suponga que hemos escrito una aplicación de contabilidad
(tipo ContaWin) que ofrece la posibilidad de buscar precios de productos online. Si
se automatiza la funcionalidad de búsqueda, Usted (u otro programador) podrá
posteriormente crear una aplicación cliente que ordene a la aplicación de
contabilidad descargar los últimos precios a intervalos de tiempo regulares.
En este ejemplo aprenderemos no sólo cómo añadir capacidades de
automatización a una aplicación existente, sino también cómo pasar listas de
cadenas, colores y fuentes como parámetros a un método de automatización.
Nota
En la actualidad, muchos de los servidores de automatización externos al proceso, a
diferencia del ejemplo que mostramos aquí, son programas completos en sí mismos. Por
ejemplo, Microsoft Word es un servidor de automatización. Word es un programa muy
completo, y proporciona la mayoría de su funcionalidad a otras aplicaciones a través de
automatización.
El Listado 4.2 muestra el código fuente para el formulario principal de la aplicación
MemoDemo.
Listado 4.2 : Aplicación MemoDemo - MainForm.pas
unit MainForm;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs,
StdCtrls;
type
TForm1 = class (TForm)
Memo1: TMemo;
btnFont: TButton;
btnColor: TButton;
FontDialog1: TFontDialog;
ColorDialog1: TColorDialog;
procedure btnFontClick(Sender: TObject);
procedure btnColorClick(Sender: TObject);
private
Servidores de automatización externos al proceso 157
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.btnFontClick(Sender: TObject);
begin
if FontDialog1.Execute then
Memo1.Font.Assign(FontDialog1.Font);
end;
procedure TForm1.btnColorClick(Sender: TObject);
begin
if ColorDialog1.Execute then
Memo1.Color := ColorDialog1.Color;
end;
end.
Como podemos ver, el código fuente de la aplicación es casi insignificante. La
Figura 4.8 muestra el formulario principal de MemoDemo en tiempo de ejecución.
Figura 4.8: MemoDemo no hace
mucho, pero es un buen punto
de partida.
Escriba algún texto dentro del editor. Pulse el botón ‘ Set Font‘ para cambiar el tipo
de letra que utiliza el editor, o el botón ‘SetColor ‘ para cambiar su color de fondo.
Añadiendo un objeto de automatización
Ahora que hemos visto MemoDemo – que, mucho me temo, no ganará ningún
premio de la industria, veamos qué es necesario para añadir capacidades de
automatización a éste.
Una de las cosas más importantes que puede hacer cuando esté añadiendo
capacidades de automatización a una aplicación existente es hacer uso del código
que ya forma parte de la aplicación. Idealmente, los cuerpos de nuestros métodos
de automatización deben contener una sola línea de código, que contiene una
llamada a una función ya existente en la aplicación principal.
158 Capítulo 4. Automatización
Si estamos desarrollando una aplicación teniendo en mente la automatización, no
es difícil tener esto en cuenta desde el principio. Sin embargo, supongamos que la
automatización era algo que estaba muy lejos de nuestras mentes cuando
escribimos MemoDemo. La forma más rápida y sencilla de preparar una aplicación
para la automatización es añadirle las funciones de soporte necesarias. Por ejemplo,
en el caso de MemoDemo, deseamos ser capaces de leer y controlar el tipo de letra,
el color y el texto del editor desde otra aplicación. Como debe ser obvio, debemos
añadir los métodos GetColor, SetColor , GetFont, SetFont, GetText y SetText al
formulario principal. El Listado 4.3 muestra el resultado de esta modularización.
Listado 4.3 : Aplicación MemoSrv - MainForm.pas
unit MainForm;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs,
StdCtrls;
type
TForm1 = class (TForm)
Memo1: TMemo;
btnFont: TButton;
btnColor: TButton;
FontDialog1: TFontDialog;
ColorDialog1: TColorDialog;
procedure btnFontClick(Sender: TObject);
procedure btnColorClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
function GetColor: TColor;
procedure SetColor(AColor: TColor);
function GetFont: TFont;
procedure SetFont(AFont: TFont);
function GetText: TStrings;
procedure SetText(AText: TStrings);
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.btnFontClick(Sender: TObject);
begin
if FontDialog1.Execute then
Memo1.Font.Assign(FontDialog1.Font);
end;
procedure TForm1.btnColorClick(Sender: TObject);
begin
Servidores de automatización externos al proceso 159
if ColorDialog1.Execute then
Memo1.Color := ColorDialog1.Color;
end;
procedure TForm1.SetColor(AColor: TColor);
begin
Memo1.Color := AColor;
end;
procedure TForm1.SetFont(AFont: TFont);
begin
Memo1.Font.Assign(AFont);
end;
procedure TForm1.SetText(AText: TStrings);
begin
Memo1.Lines.Assign(AText);
end;
function TForm1.GetColor: TColor;
begin
Result := Memo1.Color;
end;
function
begin
Result
end;
function
begin
Result
end;
end.
TForm1.GetFont: TFont;
:= Memo1.Font;
TForm1.GetText: TStrings;
:= Memo1.Lines;
Ahora que el formulario principal soporta las funciones de acceso necesarias, es
hora de añadir el objeto de automatización a MemoSrv. Seleccione FileNew en el
menú principal de Delphi. Pulse sobre la página ActiveX del Depósito de Objetos,
y seleccione Automation Object. Llame al objeto MemoIntf, y pulse OK. Delphi crea
un nuevo fichero fuente para MemoIntf y presenta el Editor de Bibliotecas de Tipos.
Vamos a añadir tres propiedades a la biblioteca de tipos: Color, Font y Text.
1. Pulse el botón New Property. Si se despliega un menú contextual, seleccione
Read/Write. Llame a la propiedad Color, y asígnele el tipo OLE_COLOR .
2. Añada una nueva propiedad de lectura y escritura de nombre Font , y asígnele
el tipo IFontDisp. No encontrará IFontDisp en la lista de tipos, pero el Editor
de Bibliotecas de tipos aceptará la declaración si Ud. la escribe.
3. Añada una nueva propiedad de lectura y escritura, llámela Text, y asígnele el
tipo IStrings.
4. Pulse el botón Refresh Implementation y cierre el Editor de Bibliotecas de
Tipos.
5. Guarde el fichero fuente como MemoIntf.pas.
160 Capítulo 4. Automatización
6. Añada ActiveX, StdVCL y AxCtrls a la cláusula uses de la sección interface
de la unidad, ya que en éstas están definidas IFont e IStrings, así como los
procedimientos GetOLExx.
7. Añada MainForm a la cláusula uses de la sección implementation.
8. Reprograme los métodos de MemoIntf de forma tal que llamen a sus métodos
correspondientes en TForm1 (ver Listado 4.4).
Ahora ya puede compilar el programa. Ejecútelo para registrar el servidor de
automatización en el registro de Windows, y salga de la aplicación.
Nota
Recuerde que los servidores COM externos al proceso se registran en el Registro de
Windows cada vez que son ejecutados. Para registrar el servidor externo al proceso sin
ejecutar la aplicación, puede ejecutar MemoSrv.exe /regserver. Para desregistrarlo, se debe
ejecutar MemoSrv.exe /unregserver.
El código fuente para MemoIntf se muestra en el Listado 4.4.
Listado 4.4 : Aplicación MemoSrv – MemoIntf.pas
unit MemoIntf;
interface
uses
ComObj, ActiveX, AXCtrls, StdVCL, MemoSrv_TLB;
type
TMemoIntf = class(TAutoObject, IMemoIntf)
protected
function Get_Color: OLE_COLOR; safecall;
procedure Set_Color(Value: OLE_COLOR); safecall ;
function Get_Font: IFontDisp; safecall;
function Get_Text: IStrings; safecall;
procedure Set_Font(const Value: IFontDisp); safecall;
procedure Set_Text(const Value: IStrings); safecall;
{ Protected declarations }
end;
implementation
uses ComServ, MainForm;
function TMemoIntf.Get_Color: OLE_COLOR;
begin
Result := Form1.GetColor;
end;
procedure TMemoIntf.Set_Color(Value: OLE_COLOR);
begin
Form1.SetColor(Value);
end;
function TMemoIntf.Get_Font: IFontDisp;
begin
GetOleFont(Form1.GetFont, Result);
Servidores de automatización externos al proceso 161
end;
function TMemoIntf.Get_Text: IStrings;
begin
GetOleStrings(Form1.GetText, Result);
end;
procedure TMemoIntf.Set_Font(const Value: IFontDisp);
begin
SetOleFont(Form1.GetFont, Value);
end;
procedure TMemoIntf.Set_Text(const Value: IStrings);
begin
SetOleStrings(Form1.GetText, Value);
end;
initialization
TAutoObjectFactory.Create(ComServer, TMemoIntf, Class_MemoIntf,
ciMultiInstance, tmApartment);
end.
Creando un cliente de automatización
Ahora que tenemos una aplicación a automatizar, vamos a concentrarnos en
desarrollar una aplicación cliente que la automatice.
En la mayoría de los casos en que se desea utilizar un servidor de automatización,
no se dispone del código fuente de las interfaces que proporciona el servidor. En
este libro hemos desarrollado tanto el servidor como los clientes, así que el código
fuente estaba disponible. Pero, ¿qué pasaría si MemoSrv hubiera sido creado por
otro programador?
Vamos a mostrar cómo podemos importar bibliotecas de tipos en Delphi y hacer
que éste reconstruya automáticamente el código fuente necesario.
Cree una aplicación nueva en Delphi, y seguidamente seleccione Project Import Type
Library desde el menú principal de Delphi. Aparecerá la pantalla que se muestra en
la Figura 4.9.
Desplácese a través de la lista hasta encontrar MemoSrv (Version 1.0) . Si no aparece
en la lista, pulse el botón Add... , y seleccione el fichero MemoSrv.exe en el directorio
donde éste se encuentre.
Seleccione MemoSrv (Version 1.0) y pulse OK. Delphi generará una biblioteca de
importación llamada MemoSrv_TLB.pas para el servidor de automatización
MemoSrv. Por defecto, el fichero será colocado en el directorio Imports del disco
duro. Si ha instalado Delphi con la configuración por defecto, este directorio será
C:\Archivos de programa\Borland\DelphiX\Imports, donde DelphiX representa la
versión de Delphi que esté utilizando. El Listado 4.5 muestra la biblioteca de tipos
importada para MemoSrv.
162 Capítulo 4. Automatización
Figura 4.9: Importando una
biblioteca de tipos.
Listado 4.5 : Aplicación MemoCli - MemoSrv_TLB.pas
unit MemoSrv_TLB;
// ********************************************************************
// WARNING
// ———// The types declared in this file were generated from data read from
// a Type Library. If this type library is explicitly or indirectly
// (via another type library referring to this type library)
// re-imported, or the ‘Refresh’ command of the Type Library Editor
// activated while editing the Type Library, the contents of this file
// will be regenerated and all manual modifications will be lost.
// ********************************************************************
// PASTLWTR : $Revision:
1.11.1.75 $
// File generated on 5/15/01 10:46:43 AM from Type Library described
below.
// ********************************************************************
// Type Lib: C:\Book\samples\Chap04\MemoSrv\MemoSrv.tlb
// IID\LCID: {A1E420C7-F75F-11D2-B3B9-0040F67455FE}\0
// Helpfile:
// HelpString: MemoSrv Library
// Version:
1.0
// ********************************************************************
interface
uses Windows, ActiveX, Classes, Graphics, OleCtrls, StdVCL;
// ********************************************************************
// GUIDS declared in the TypeLibrary. Following prefixes are used:
//
//
//
//
//
//
//
//
//
//
//
//
//
//
Servidores de automatización externos al proceso 163
//
Type Libraries
: LIBID_xxxx
//
CoClasses
: CLASS_xxxx
//
DISPInterfaces
: DIID_xxxx
//
Non-DISP interfaces: IID_xxxx
// ********************************************************************
const
LIBID_MemoSrv: TGUID = ‘{A1E420C7-F75F-11D2-B3B9-0040F67455FE}’;
IID_IMemoIntf: TGUID = ‘{A1E420C8-F75F-11D2-B3B9-0040F67455FE}’;
CLASS_MemoIntf: TGUID = ‘{A1E420CA-F75F-11D2-B3B9-0040F67455FE}’;
type
// ********************************************************************
// Forward declaration of interfaces defined in Type Library
// ********************************************************************
IMemoIntf = interface;
IMemoIntfDisp = dispinterface ;
// ********************************************************************
// Declaration of CoClasses defined in Type Library
// (NOTE: Here we map each CoClass to its Default Interface)
//
//
//
//
//
//
//
//
//
//
//
// ******************************************************************** //
MemoIntf = IMemoIntf;
// ******************************************************************** //
// Interface: IMemoIntf
// Flags:
(4416) Dual OleAutomation Dispatchable
// GUID:
{A1E420C8-F75F-11D2-B3B9-0040F67455FE}
// ******************************************************************** //
IMemoIntf = interface (IDispatch)
[‘{A1E420C8-F75F-11D2-B3B9-0040F67455FE}’]
function Get_Color: OLE_COLOR; safecall;
procedure Set_Color(Value: OLE_COLOR); safecall;
function Get_Font: IFontDisp; safecall;
procedure Set_Font( const Value: IFontDisp); safecall;
function Get_Text: IStrings; safecall ;
procedure Set_Text( const Value: IStrings); safecall;
property Color: OLE_COLOR read Get_Color write Set_Color;
property Font: IFontDisp read Get_Font write Set_Font;
property Text: IStrings read Get_Text write Set_Text;
end;
// ******************************************************************** //
// DispIntf: IMemoIntfDisp
// Flags:
(4416) Dual OleAutomation Dispatchable
// GUID:
{A1E420C8-F75F-11D2-B3B9-0040F67455FE}
// ******************************************************************** //
IMemoIntfDisp = dispinterface
[‘{A1E420C8-F75F-11D2-B3B9-0040F67455FE}’]
property Color: OLE_COLOR dispid 2;
property Font: IFontDisp dispid 1;
property Text: IStrings dispid 3;
end;
CoMemoIntf = class
class function Create: IMemoIntf;
class function CreateRemote(const MachineName: string): IMemoIntf;
end;
164 Capítulo 4. Automatización
implementation
uses ComObj;
class function CoMemoIntf.Create: IMemoIntf;
begin
Result := CreateComObject( CLASS_MemoIntf) as IMemoIntf;
end;
class function CoMemoIntf.CreateRemote(const MachineName: string):
IMemoIntf;
begin
Result := CreateRemoteComObject(MachineName, CLASS_MemoIntf) as
IMemoIntf;
end;
end.
Añada MemoSrv_TLB a la cláusula uses del formulario principal, y compile el
proyecto para asegurar que Delphi encuentra la unidad importada. Si se produce
un error de compilación al intentar compilar MemoSrv_TLB , deberá comprobar sus
opciones del entorno (menú Tools | Environment Options) para asegurarse de que
$(Delphi)\Imports forma parte de las rutas de bibliotecas (ver Figura 4.10).
Figura 4.10: Verificando que
$(DELPHI)\Imports se encuentra
en la lista de rutas de bibliotecas.
Ahora podemos escribir el código para conectarse y desconectarse del servidor.
Como el código utiliza interfaces, no hay nada nuevo aquí:
procedure TForm1.btnConnectClick(Sender: TObject);
begin
FMemo := CoMemoIntf.Create;
end;
procedure Tform1.btnDisconnectClick(Sender: TObject);
begin
FMemo := nil;
end;
Servidores de automatización externos al proceso 165
Añadiremos tres botones al formulario para configurar el tipo de letra, el color y el
contenido del control de edición remoto. Como hemos señalado anteriormente,
Delphi proporciona el marshaling de tipos de letra, listas de cadenas e imágenes.
Esto hace que el código que tenemos que escribir resulte trivial.
procedure TForm1.btnSetFontClick(Sender: TObject);
var
FontDisp: IfontDisp;
begin
if Fontdialog1.Execute then begin
Memo1.Font.Assign(FontDialog1.Font);
GetOleFont(FontDialog1.Font, FontDisp);
FMemo.Set_Font(FontDisp);
end;
end;
GetOleFont en un procedimiento de Delphi que “convierte” un objeto TFont en una
interfaz IFontDisp apropiada para atravesar las fronteras de un proceso. De forma
similar, GetOleStrings produce una interfaz IStrings a partir de un objeto TStrings. El
Listado 4.6 muestra el código de la aplicación cliente.
Listado 4.6: Aplicación MemoCli - MainForm.pas
unit MainForm;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs,
StdCtrls, MemoSrv_TLB;
type
TForm1 = class(TForm)
btnSetColor: TButton;
btnConnect: TButton;
btnSetFont: TButton;
btnSetText: TButton;
btnDisconnect: TButton;
btnGetColor: TButton;
btnGetFont: TButton;
btnGetText: TButton;
Memo1: TMemo;
FontDialog1: TFontDialog;
ColorDialog1: TColorDialog;
procedure btnConnectClick(Sender: TObject);
procedure btnDisconnectClick(Sender: TObject);
procedure btnSetColorClick(Sender: TObject);
procedure btnSetFontClick(Sender: TObject);
procedure
procedure
procedure
procedure
private
btnSetTextClick(Sender: TObject);
btnGetColorClick(Sender: TObject);
btnGetFontClick(Sender: TObject);
btnGetTextClick(Sender: TObject);
166 Capítulo 4. Automatización
{ Private declarations }
FMemo: IMemoIntf;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
uses
ActiveX, AXCtrls, StdVCL;
{$R *.DFM}
procedure TForm1.btnConnectClick(Sender: TObject);
begin
FMemo := CoMemoIntf.Create;
end;
procedure TForm1.btnDisconnectClick(Sender: TObject);
begin
FMemo := nil;
end;
procedure TForm1.btnSetColorClick(Sender: TObject);
begin
if ColorDialog1.Execute then begin
Memo1.Color := ColorDialog1.Color;
FMemo.Set_Color(ColorToRGB(ColorDialog1.Color));
end;
end;
procedure TForm1.btnSetFontClick(Sender: TObject);
var
FontDisp: IFontDisp;
begin
if FontDialog1.Execute then begin
Memo1.Font.Assign(FontDialog1.Font);
GetOleFont(FontDialog1.Font, FontDisp);
FMemo.Set_Font(FontDisp);
end;
end;
procedure TForm1.btnSetTextClick(Sender: TObject);
var
Strings: IStrings;
begin
GetOleStrings(Memo1.Lines, Strings);
FMemo.Set_Text(Strings);
end;
procedure TForm1.btnGetColorClick(Sender: TObject);
begin
Memo1.Color := FMemo.Get_Color;
end;
procedure TForm1.btnGetFontClick(Sender: TObject);
var
FontDisp: IFontDisp;
begin
FontDisp := FMemo.Get_Font;
Servidores de automatización externos al proceso 167
SetOleFont(Memo1.Font, FontDisp);
end;
procedure TForm1.btnGetTextClick(Sender: TObject);
var
Strings: IStrings;
begin
Strings := FMemo.Get_Text;
SetOleStrings(Memo1.Lines, Strings);
end;
end.
La Figura 4.11 muestra la aplicación MemoCli conectada a MemoSrv.
Figura 4.11: MemoCli
controla al servidor de
automatización MemoSrv.
Recapitulando, hemos ejecutado los siguientes pasos para crear un cliente de
automatización:
•
•
•
•
Crear una nueva aplicación.
Importar la biblioteca de tipos del servidor.
Añadir en la cláusula uses de la aplicación cliente la unidad de importación.
Realizar llamadas a los métodos del servidor de automatización.
Hasta aquí, hemos cubierto las bases de la automatización. Ahora sabe cómo crear
un servidor de automatización, y cómo puede controlar el servidor utilizando
interfaces, disp-interfaces y variantes. En la próxima sección examinaremos las
características más avanzadas de la automatización COM: los eventos y funciones
de respuesta (callbacks).
Descargar