Professional Documents
Culture Documents
de shell no Windows
NESTE CAPTULO
l
Vnculos do shell
Extenses do shell
CAPTULO
16
Lanada no Windows 95, o shell do Windows tambm tem suporte em todas as verses seguintes do
Windows (NT 3.51 e superior, 98, 2000, Me e XP). Parente distante do Program Manager (gerenciador de programas), o shell do Windows inclui alguns grandes recursos para estender o shell de
modo a atender suas necessidades. O problema que muitos desses poderosos recursos extensveis
so temas de desenvolvimento do Win32 mal documentados. Este captulo tem como objetivo dar
informaes e exemplos de que voc precisa para ter acesso aos recursos do shell, como os cones da
bandeja de notificao, barras de ferramentas do desktop da aplicao, vnculos do shell e extenses
do shell.
A API
Acredite se quiser, mas somente uma chamada da API est envolvida na criao, modificao e remoo dos cones da bandeja de notificao. A funo se chama Shell_NotifyIcon( ). Esta e outras
funes relacionadas ao shell do Windows esto contidas na unidade ShellAPI. Shell_NotifyIcon( ) definida da seguinte maneira:
function Shell_NotifyIcon(dwMessage: DWORD; lpData:
PNotifyIconData): BOOL; stdcall;
O parmetro dwMessage descreve a ao a ser executada pelo cone, que pode ser qualquer um
dos valores mostrados na Tabela 16.1.
Tabela 16.1 Valores do parmetro dwMessage
Constante
Valor
Significado
NIM_ADD
NIM_MODIFY
NIM_DELETE
cbSize: DWORD;
Wnd: HWND;
uID: UINT;
uFlags: UINT;
uCallbackMessage: UINT;
hIcon: HICON;
szTip: array [0..63] of AnsiChar;
end;
O campo cbSize armazena o tamanho do registro e deve ser inicializado como SizeOf(TNotifyIconData).
Wnd a ala da janela qual as mensagens de callback da bandeja de notificao devem ser enviadas. (Callback est entre aspas, pois no se trata de uma callback no sentido estrito da palavra; no
entanto, a documentao do Win32 usa essa terminologia para mensagens enviadas para uma janela, no para um cone da bandeja de notificao.)
uID um nmero de ID exclusivo definido pelo programador. Se voc tiver uma aplicao com
diversos cones, precisar identificar cada um deles colocando um nmero diferente nesse campo.
uFlags descreve qual dos campos do registro TNotifyIconData deve ser considerado ativo pela
funo Shell_NotifyIcon( ) e, por essa razo, qual das propriedades do cone ser afetada pela ao
especificada pelo parmetro dwMessage. Esse parmetro pode ser qualquer combinao de flags
mostrada na Tabela 16.2.
Tabela 16.2 Possveis flags a serem includos em uFlags
Constante
Valor
Significado
NIF_MESSAGE
NIF_ICON
NIF_TIP
uCallbackMessage contm o valor da mensagem do Windows a ser enviada para a janela identificada pelo campo Wnd. Geralmente, o valor desse campo obtido pela chamada de RegisterWindowMessage( ) ou pelo uso de um deslocamento de WM_USER. O lParam dessa mensagem ter o mesmo valor que o campo uID, e wParam armazenar a mensagem de mouse gerada pelo cone de notificao.
hIcon identifica a ala para o cone que ser colocado na bandeja de notificao.
szTip armazena uma string terminada em nulo, que aparecer na janela de dica exibida quando
o ponteiro do mouse for mantido sobre o cone de notificao.
O componente TTrayNotifyIcon encapsula Shell_NotifyIcon( ) em um mtodo chamado
SendTrayMessage( ), que mostrado a seguir:
procedure TTrayNotifyIcon.SendTrayMessage(Msg: DWORD; Flags: UINT);
{ Este mtodo envolve a chamada para o Shell_NotifyIcon da API }
begin
{ Preenche o registro com os valores apropriados }
with Tnd do
begin
cbSize := SizeOf(Tnd);
StrPLCopy(szTip, PChar(FHint), SizeOf(szTip));
uFlags := Flags;
uID := UINT(Self);
Wnd := IconMgr.HWindow;
uCallbackMessage := Tray_Callback;
hIcon := ActiveIconHandle;
end;
Shell_NotifyIcon(Msg, @Tnd);
end;
Manipulando mensagens
J dissemos que todas as mensagens da bandeja de notificao so enviadas para uma janela mantida
pelo objeto IconMgr global. Este objeto construdo e liberado nas sees initialization e finalization da unidade do componente, conforme mostrado a seguir:
initialization
{ Obtm o ID de mensagem exclusivo para a callback da bandeja }
DDGM_TRAYICON := RegisterWindowMessage(TrayMsgStr);
IconMgr := TIconManager.Create;
finalization
IconMgr.Free;
A janela para a qual as mensagens da bandeja de notificao sero enviadas criada no construtor desse objeto usando a funo AllocateHWnd( ):
constructor TIconManager.Create;
begin
FHWindow := AllocateHWnd(TrayWndProc);
end;
4
O mtodo TrayWndProc( ) serve como procedimento de janela para a janela criada no construtor. Voltaremos a falar sobre esse mtodo mais adiante.
cones e dicas
O mtodo mais objetivo para expor cones e dicas para o usurio final do componente atravs das
propriedades. Alm disso, a criao de uma propriedade Icon do tipo TIcon significa que ela pode
automaticamente tirar proveito do editor de propriedades do Delphi para os cones, o que um recurso muito interessante. Como o cone da bandeja visvel inclusive durante o projeto, voc precisa se certificar de que o cone e a dica podem mudar dinamicamente. Fazer isso no implica, como se
pode pensar a princpio, muito trabalho extra; basta se certificar de que o mtodo SendTrayMessage( ) chamado (usando a mensagem NIM_MODIFY) no mtodo write das propriedades Hint e Icon.
Veja, a seguir, os mtodos write para essas propriedades:
procedure TTrayNotifyIcon.SetIcon(Value: TIcon);
{ Mtodo write para a propriedade Icon. }
begin
FIcon.Assign(Value); // define novo cone
if FIconVisible then
{ Muda o cone na bandeja de notificao }
SendTrayMessage(NIM_MODIFY, NIF_ICON);
end;
procedure TTrayNotifyIcon.SetHint(Value: String);
{ Define o mtodo para a propriedade Hint }
begin
if FHint < > Value then
begin
FHint := Value;
if FIconVisible then
{ Muda dica no cone da bandeja de notificao }
SendTrayMessage(NIM_MODIFY, NIF_TIP);
end;
end;
Cliques do mouse
Uma das partes mais desafiadoras desse componente garantir que os cliques do mouse sejam manipulados de modo apropriado. Voc pode ter percebido que muitos cones da bandeja de notificao
executam trs aes diferentes devido a cliques do mouse:
l
Abre uma janela diferente (geralmente uma folha de propriedades) com um clique duplo
O desafio est na criao de um evento que represente o clique duplo sem acionar tambm o
evento de clique nico.
Em termos de mensagem do Windows, quando o usurio d um clique duplo no boto esquerdo
do mouse, a janela selecionada recebe tanto a mensagem WM_LBUTTONDOWN como a mensagem
WM_LBUTTONDBLCLK. Para permitir que uma mensagem de clique duplo seja processada independentemente de um clique nico, preciso um mecanismo para retardar a manipulao da mensagem de clique nico o tempo necessrio para garantir que uma mensagem de clique duplo no esteja acessvel.
O intervalo de tempo a esperar antes que voc possa ter certeza de que uma mensagem
WM_LBUTTONDBLCLK no esteja vindo depois de uma mensagem M_LBUTTONDOWN bastante fcil de determinar. A funo GetDoubleClickTime( ) da API, que no utiliza parmetros, retorna o maior inter- 5
valo de tempo possvel (em milissegundos) que o Control Panel (painel de controle) permitir entre
os dois cliques de um clique duplo. A escolha bvia para um mecanismo permitir que voc aguarde
o nmero de milissegundos especificado por GetDoubleClickTime( ), garantindo que um clique duplo no venha depois de um clique, o componente TTimer. Por essa razo, um componente TTimer
criado e inicializado no construtor do componente TTrayNotifyIcon, com o seguinte cdigo:
FTimer := TTimer.Create(Self);
with FTimer do
begin
Enabled := False;
Interval := GetDoubleClickTime;
OnTimer := OnButtonTimer;
end;
OnButtonTimer( ) um mtodo que ser chamado quando o intervalo do timer expirar. Vamos
end
else
{ Se no for uma mensagem de callback da bandeja, chama DefWindowProc }
Result := DefWindowProc(FHWindow, Msg, wParam, lParam);
end;
end;
O que faz isso tudo funcionar que a mensagem de clique nico se limita a ativar o timer, enquanto a mensagem de clique duplo define um flag para indicar que o clique duplo ocorreu antes do
acionamento do seu evento OnDblClick. O clique com o boto direito, a propsito, chama o menu
instantneo dado pela propriedade PopupMenu do componente. Agora d uma olhada no mtodo
OnButtonTimer( ):
procedure TTrayNotifyIcon.OnButtonTimer(Sender: TObject);
begin
{ Desativa o timer, pois s queremos que ele seja acionado uma vez. }
FTimer.Enabled := False;
{ Se um clique duplo no tiver ocorrido, o clique nico acionado. }
if (not FNoShowClick) and Assigned(FOnClick) then
FOnClick(Self);
FNoShowClick := False;
// reinicia flag
end;
Esse mtodo primeiro desativa o timer, para garantir que o evento s ser acionado uma vez a
cada clique no mouse. Em seguida, o mtodo verifica o status do flag FNoShowClick. Lembre-se de
que esse flag ser definido pela mensagem de clique duplo no mtodo OwnerWndProc( ). Por essa razo, o evento OnClick s ser acionado quando OnDblClk no o for.
Ocultando a aplicao
Outro aspecto das aplicaes da bandeja de notificao que elas no aparecem como botes na
barra de tarefas do sistema. Para fornecer essa funcionalidade, o componente TTrayNotifyIcon expe uma propriedade HideTask, que permite que o usurio decida se a aplicao deve ser visvel na
barra de tarefas. O mtodo write para essa propriedade mostrado no cdigo a seguir. A linha de
cdigo que o trabalho executa a chamada para o procedimento ShowWindow( ) da API, que passa a
propriedade Handle de Application e uma constante, para indicar se a aplicao tem que ser mostrada normalmente ou ocultada. Veja o cdigo a seguir:
procedure TTrayNotifyIcon.SetHideTask(Value: Boolean);
{ Mtodo write da propriedade HideTask }
const
{ Flags para exibir a aplicao normalmente ou ocult-la }
ShowArray: array[Boolean] of integer = (sw_ShowNormal, sw_Hide);
begin
if FHideTask < > Value then begin
FHideTask := Value;
{ No faz nada no modo de projeto }
if not (csDesigning in ComponentState) then
ShowWindow(Application.Handle, ShowArray[FHideTask]);
end;
end;
end;
end
else
{ Se no for uma mensagem de callback da bandeja, chama DefWindowProc }
Result := DefWindowProc(FHWindow, Msg, wParam, lParam);
end;
end;
{ TTrayNotifyIcon }
constructor TTrayNotifyIcon.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FIcon := TIcon.Create;
FTimer := TTimer.Create(Self);
with FTimer do
begin
Enabled := False;
Interval := GetDoubleClickTime;
OnTimer := OnButtonTimer;
end;
{ Mantm o cone padro da janela... }
LoadDefaultIcon;
end;
destructor TTrayNotifyIcon.Destroy;
begin
if FIconVisible then SetIconVisible(False);
FIcon.Free;
FTimer.Free;
inherited Destroy;
end;
//cone de destruio
// dados livres
11
A Figura 16.2 mostra uma imagem do cone gerada por TTrayNotifyIcon na bandeja de notificao.
14
Alm de ser encaixada nas bordas da tela, AppBars podem empregar recursos do tipo barra de
tarefas, como a funcionalidade de auto-ocultar e arrastar-e-soltar. No entanto, voc pode se surpre-
ender com o pequeno tamanho da API (ela tem apenas uma funo). Como se pode deduzir pelo seu
pequeno tamanho, a API no tem muita coisa a oferecer. A funo da API mais consultiva do que
funcional. Ou seja, em vez de controlar a AppBar com comandos do tipo faa isto, faa aquilo,
voc interroga a AppBar com comandos do tipo posso fazer isso, posso fazer aquilo?.
A API
Como os cones da bandeja de notificao, AppBars s tem uma funo da API com a qual voc vai
trabalhar SHAppBarMessage( ), nesse caso. Veja a seguir como SHAppBarMessage( ) definida na
unidade ShellAPI:
function SHAppBarMessage(dwMessage: DWORD; var pData: TAppBarData): UINT
stdcall;
O primeiro parmetro dessa funo, dwMessage, pode conter um dos valores descritos na Tabela 16.3.
Tabela 16.3 Mensagens da AppBar
Constante
Valor
Significado
ABM_NEW
$0
ABM_REMOVE
$1
ABM_QUERYPOS
$2
ABM_SETPOS
$3
ABM_GETSTATE
$4
ABM_GETTASKBARPOS
$5
ABM_ACTIVATE
$6
ABM_GETAUTOHIDEBAR
$7
ABM_SETAUTOHIDEBAR
$8
ABM_WINDOWPOSCHANGED
$9
Nesse registro, o campo cbSize armazena o tamanho do registro, o campo hWnd armazena a
ala da janela da AppBar especificada, uCallbackMessage armazena o valor da mensagem que ser 15
enviada para a janela AppBar juntamente com as mensagens de notificao, rc armazena o retngulo que envolve a AppBar em questo e lParam armazena algumas informaes adicionais especficas
da mensagem.
DICA
Para obter maiores informaes sobre a funo SHAppBarMessage( ) da API e sobre o tipo
TAppBarData, consulte a ajuda on-line do Win32.
Se a AppBar for criada com sucesso, o mtodo SetAppBarEdge( ) ser chamado para definir a
AppBar como sua posio inicial. Este mtodo, por sua vez, chama o mtodo SetAppBarPos( ), passando o flag apropriado, definido pela API, que indica a borda de tela solicitada. Como era de se
imaginar, os flags ABE_TOP, ABE_BOTTOM, ABE_LEFT e ABE_RIGHT representam cada uma das bordas da
tela. Isso mostrado no trecho de cdigo a seguir:
procedure TAppBar.SetAppBarPos(Edge: UINT);
begin
if csDesigning in ComponentState then Exit;
FABD.uEdge := Edge;
// define borda
with FABD.rc do
begin
// define coordenada como tela inteira
Top := 0;
Left := 0;
Right := Screen.Width;
Bottom := Screen.Height;
// Envia ABM_QUERYPOS para obter retngulo apropriado na margem
SendAppBarMsg(ABM_QUERYPOS);
// reajusta retngulo com base naquele modificado por ABM_QUERYPOS
case Edge of
ABE_LEFT: Right := Left + FDockedWidth;
ABE_RIGHT: Left := Right FDockedWidth;
ABE_TOP: Bottom := Top + FDockedHeight;
ABE_BOTTOM: Top := Bottom FDockedHeight;
end;
16
Esse mtodo primeiro define o campo uEdge de FABD como o valor passado atravs do parmetro Edge. Em seguida, ele define o campo rc como as coordenadas da tela inteira e envia a mensagem
ABM_QUERYPOS. Essa mensagem redefine o campo rc de modo que ele contenha o retngulo apropriado para a borda indicada por uEdge. Uma vez obtido o retngulo apropriado, rc ajustado novamente de modo que tenha altura e largura razoveis. Nesse ponto, rc armazena o retngulo final
para a AppBar. A mensagem ABM_SETPOS em seguida enviada para informar ao shell quanto ao
novo retngulo, e o retngulo definido por meio da propriedade BoundsRect do controle.
J dissemos que as mensagens de notificao da AppBar sero enviadas para a janela indicada
por FABD.hWnd, usando o identificador de mensagem armazenado em FABD.uCallbackMessage. Essas
mensagens de notificao so manipuladas no mtodo WndProc( ) mostrado a seguir:
procedure TAppBar.WndProc(var M: TMessage);
var
State: UINT;
WndPos: HWnd;
begin
if M.Msg = AppBarMsg then
begin
case M.WParam of
// Enviada quando o estado Sempre visvel ou Auto-Ocultar alterado.
ABN_STATECHANGE:
begin
// Verifica se a barra de acesso ainda ABS_ALWAYSONTOP.
State := SendAppBarMsg(ABM_GETSTATE);
if ABS_ALWAYSONTOP and State = 0 then
SetTopMost(False)
else
SetTopMost(True);
end;
// Uma aplicao de tela cheia foi iniciada ou a ltima
// aplicao de tela inteira foi fechada.
ABN_FULLSCREENAPP:
begin
// Define a ordem z da barra de acesso de modo apropriado.
State := SendAppBarMsg(ABM_GETSTATE);
if M.lParam < > 0 then begin
if ABS_ALWAYSONTOP and State = 0 then
SetTopMost(False)
else
SetTopMost(True);
end
else
if State and ABS_ALWAYSONTOP < > 0 then
SetTopMost(True);
end;
// Enviado quando houver algo que possa afetar a posio da AppBar.
ABN_POSCHANGED:
begin
// A barra de tarefas ou outra barra de acesso
// mudou seu tamanho ou sua posio.
17
SetAppBarPos(FABD.uEdge);
end;
end;
end
else
inherited WndProc(M);
end;
Este mtodo manipula algumas mensagens de notificao que permitem que a AppBar responda s mudanas que podem ocorrer no shell enquanto a aplicao estiver sendo executada. O restante do cdigo do componente AppBar mostrado na Listagem 16.3.
Listagem 16.3 AppBars.pas a unidade que contm a classe bsica para dar suporte AppBar
unit AppBars;
interface
uses Windows, Messages, SysUtils, Forms, ShellAPI, Classes, Controls;
type
TAppBarEdge = (abeTop, abeBottom, abeLeft, abeRight);
EAppBarError = class(Exception);
18
TAppBar = class(TCustomForm)
private
FABD: TAppBarData;
FDockedHeight: Integer;
FDockedWidth: Integer;
FEdge: TAppBarEdge;
FOnEdgeChanged: TNotifyEvent;
FTopMost: Boolean;
procedure WMActivate(var M: TMessage); message WM_ACTIVATE;
procedure WMWindowPosChanged(var M: TMessage); message WM_WINDOWPOSCHANGED;
function SendAppBarMsg(Msg: DWORD): UINT;
procedure SetAppBarEdge(Value: TAppBarEdge);
procedure SetAppBarPos(Edge: UINT);
procedure SetTopMost(Value: Boolean);
procedure SetDockedHeight(const Value: Integer);
procedure SetDockedWidth(const Value: Integer);
protected
procedure CreateParams(var Params: TCreateParams); override;
procedure CreateWnd; override;
procedure DestroyWnd; override;
procedure WndProc(var M: TMessage); override;
public
constructor CreateNew(AOwner: TComponent; Dummy: Integer = 0); override;
property DockManager;
published
property Action;
property ActiveControl;
property AutoScroll;
property AutoSize;
property BiDiMode;
property BorderWidth;
property Color;
property Ctl3D;
19
OnShortCut;
OnShow;
OnStartDock;
OnUnDock;
implementation
var
AppBarMsg: UINT;
constructor TAppBar.CreateNew(AOwner: TComponent; Dummy: Integer);
begin
FDockedHeight := 35;
FDockedWidth := 40;
inherited CreateNew(AOwner, Dummy);
ClientHeight := 35;
Width := 100;
BorderStyle := bsNone;
BorderIcons := [ ];
// configura o registro TAppBarData
FABD.cbSize := SizeOf(FABD);
FABD.uCallbackMessage := AppBarMsg;
end;
procedure TAppBar.WMWindowPosChanged(var M: TMessage);
begin
inherited;
// Deve informar ao shell que a posio da AppBar foi alterada
SendAppBarMsg(ABM_WINDOWPOSCHANGED);
end;
procedure TAppBar.WMActivate(var M: TMessage);
begin
inherited;
// Deve informar ao shell que a janela AppBar foi ativada
SendAppBarMsg(ABM_ACTIVATE);
end;
procedure TAppBar.WndProc(var M: TMessage);
var
State: UINT;
begin
if M.Msg = AppBarMsg then
begin
case M.WParam of
// Enviada quando mudar o estado Sempre visvel ou Auto-Ocultar.
ABN_STATECHANGE:
begin
// Verifica se a barra de acesso ainda ABS_ALWAYSONTOP.
State := SendAppBarMsg(ABM_GETSTATE);
if ABS_ALWAYSONTOP and State = 0 then
SetTopMost(False)
else
SetTopMost(True);
end;
// Uma aplicao de tela inteira foi iniciada ou a ltima
20
21
Usando TAppBar
Se voc instalou o software encontrado no CD-ROM que acompanha este livro, o uso de TAppBar
ser muito fcil: basta selecionar a opo AppBar da pgina DDG da caixa de dilogo File, New.
Isso chama um assistente que gerar uma unidade contendo um componente TAppBar.
NOTA
O Captulo 17 demonstra como criar um assistente que gera automaticamente uma TAppBar. Neste
captulo, no entanto, voc pode ignorar a implementao do assistente. Basta entender que algum
trabalho est sendo feito nos bastidores para gerar a unidade e o formulrio da AppBar para voc.
Nesta pequena aplicao de exemplo, TAppBar usada para criar uma barra de ferramentas de
aplicao que contenha botes para diversos comandos de edio: Open (abrir), Save (salvar), Cut
(recortar), Copy (copiar) e Paste (colar). Os botes manipularo um componente TMemo encontrado
no formulrio principal. O cdigo-fonte dessa unidade aparece na Listagem 16.4, e a Figure 16.5
mostra a aplicao em ao com o controle da AppBar encaixado na parte inferior da tela.
Listagem 16.4 ApBarFrm.pas a unidade principal da aplicao de demonstrao AppBar
unit ApBarFrm;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
AppBars, Menus, Buttons;
type
TAppBarForm = class(TAppBar)
sbOpen: TSpeedButton;
23
25
Vnculos do shell
O shell do Windows expe uma srie de interfaces que podem ser utilizadas para manipular diferentes aspectos do shell. Essas interfaces so definidas na unidade ShlObj. Seria preciso um novo livro para discutir em profundidade todos os objetos dessa unidade e, portanto, vamos concentrar
nossos esforos em uma das interfaces mais teis (e mais usadas): IShellLink.
IShellLink uma interface que permite a criao e manipulao dos vnculos do shell nas suas
aplicaes. Caso esteja em dvida, a maioria dos cones do seu desktop provavelmente composta
de vnculos do shell. Alm disso, cada item no menu Send To (enviar para) local do shell ou no menu
Documents (documentos), ao qual voc tem acesso pelo menu Start (iniciar) um vnculo do shell.
A interface IShellLink definida da seguinte maneira:
const
type
IShellLink = interface(IUnknown)
[{000214EE-0000-0000-C000-000000000046}]
function GetPath(pszFile: PAnsiChar; cchMaxPath: Integer;
var pfd: TWin32FindData; fFlags: DWORD): HResult; stdcall;
function
GetIDList(var ppidl: PItemIDList): HResult; stdcall;
26
NOTA
Como IshellLink e seus mtodos so descritos com detalhes na ajuda on-line do Win32, no vamos discuti-los aqui.
NOTA
No se esquea de que, antes de poder usar qualquer uma das funes OLE, voc deve inicializar a
biblioteca COM usando a funo CoInitialize( ). Quando voc tiver acabado de usar a COM,
dever exclu-la chamando CoUninitialize( ). Essas funes sero chamadas pelo Delphi em
uma aplicao que use ComObj e contenha uma chamada para Application.Initialize( ).
Caso contrrio, voc mesmo ter que chamar essas funes.
27
Usando IShellLink
Aparentemente, os vnculos do shell podem ser considerados algo mgico: voc d um clique com o
boto direito do mouse no desktop, cria um novo atalho e alguma coisa acontece, que faz com que
um cone aparea no desktop. Na verdade, essa alguma coisa no tem mistrio algum quando voc
sabe o que est ocorrendo. Um vnculo de shell no passa de um arquivo com uma extenso LNK,
que reside em algum diretrio. Quando o Windows inicializado, ele procura arquivos LNK em
determinados diretrios, que representam vnculos que residem em diferentes pastas do shell. Essas
pastas do shell, ou pastas especiais, incluem itens como Network Neighborhood (ambiente de
rede), Send To (enviar para), Startup (iniciar) e Desktop (rea de trabalho), entre outras coisas. O
shell armazena as correspondncias vnculo/pasta no Registro do sistema encontradas, em sua
maioria, abaixo da seguinte chave, caso voc queira examinar:
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer
\Shell Folders
A criao de um vnculo do shell em uma pasta especial, portanto, apenas uma questo de colocar um arquivo de vnculo em um determinado diretrio. Em vez de mexer com o Registro do sistema, voc pode usar SHGetSpecialFolderPath( ) para obter o caminho de diretrio para as diversas
pastas especiais. Esse mtodo definido da seguinte maneira:
function SHGetSpecialFolderPath(hwndOwner: HWND; lpszPath: PChar;
nFolder: Integer; fCreate: BOOL): BOOL; stdcall;
hwndOwner contm a ala de uma janela que servir como o proprietria para qualquer caixa de
dilogo que a funo possa chamar.
lpszPath um ponteiro para um buffer que receber o caminho. Esse buffer deve ter, pelo menos, o nmero de caracteres registrado em MAX_PATH.
nFolder identifica a pasta especial cujo caminho voc deseja obter. A Tabela 16.4 mostra os
possveis valores para esse parmetro e uma descrio de cada um deles.
fCreate indica se uma pasta deve ser criada, caso no exista.
Tabela 16.4 Possveis valores para NFolder
28
Flag
Descrio
CSIDL_ALTSTARTUP
CSIDL_APPDATA
CSIDL_BITBUCKET
CSIDL_COMMON_ALTSTARTUP
CSIDL_COMMON_DESKTOPDIRECTORY
CSIDL_COMMON_FAVORITES
CSIDL_COMMON_PROGRAMS
CSIDL_COMMON_STARTMENU
Descrio
CSIDL_COMMON_STARTUP
CSIDL_CONTROLS
CSIDL_COOKIES
CSIDL_DESKTOP
CSIDL_DESKTOPDIRECTORY
CSIDL_DRIVES
CSIDL_FAVORITES
CSIDL_FONTS
CSIDL_HISTORY
CSIDL_INTERNET
CSIDL_INTERNET_CACHE
CSIDL_NETHOOD
CSIDL_NETWORK
CSIDL_PERSONAL
CSIDL_PRINTERS
CSIDL_PRINTHOOD
CSIDL_PROGRAMS
CSIDL_RECENT
CSIDL_SENDTO
CSIDL_STARTMENU
CSIDL_STARTUP
CSIDL_TEMPLATES
NOTA
Para obter uma descrio completa de IPersistFile e seus mtodos, consulte a ajuda on-line do
Win32.
Como a classe que implementa IShellLink tambm obrigatria para implementar IPeristFile, voc pode usar a QueryInterface para consultar a instncia de IShellLink de uma instncia de
IPersistFile usando o operador as, como mostramos a seguir:
var
SL: IShellLink;
PF: IPersistFile;
begin
OleCheck(CoCreateInstance(CLSID_ShellLink, nil, CLSCTX_INPROC_SERVER,
IShellLink, SL));
PF := SL as IPersistFile;
// use PF e SL
end;
Como j dissemos, o uso de objetos da interface COM corresponde a usar os objetos normais
da Object Pascal. O cdigo mostrado a seguir, por exemplo, cria um vnculo com o shell do desktop
com a aplicao Notepad (bloco de notas):
procedure MakeNotepad;
const
// NOTA: Posio presumida do Notepad:
AppName = c:\windows\notepad.exe;
var
SL: IShellLink;
PF: IPersistFile;
LnkName: WideString;
begin
OleCheck(CoCreateInstance(CLSID_ShellLink, nil, CLSCTX_INPROC_SERVER,
IShellLink, SL));
30
Nesse procedimento, o mtodo SetPath( ) de IShellLink usado para apontar o vnculo para
um documento ou arquivo executvel (nesse caso, o Notepad). Posteriormente, um caminho e
nome de arquivo para o vnculo criado usando o caminho retornado por GetFolderLocation(Desktop) (j descrita nesta seo) e pelo uso da funo ChangeFileExt( ) para alterar a extenso do Notepad de .EXE para .LNK. Esse novo nome de arquivo armazenado em LnkName. Depois
disso, o mtodo Save( ) salva o vnculo em um arquivo no disco. Como voc j aprendeu, quando o
procedimento terminar e as instncias de interface SL e PF sarem do escopo, suas respectivas referncias sero liberadas.
Dado esse registro, voc pode criar funes que recuperam as definies de um determinado
vnculo do shell com o registro ou que definem os valores do vnculo como aqueles indicados pelo
contedo do registro. Essas funes aparecem na Listagem 16.5; WinShell.pas uma unidade que
contm o cdigo-fonte completo dessas funes.
Listagem 16.5 WinShell.pas a unidade que contm as funes que operam nos vnculos do
shell
unit WinShell;
interface
uses SysUtils, Windows, Registry, ActiveX, ShlObj;
type
EShellOleError = class(Exception);
TShellLinkInfo = record
PathName: string;
31
33
Um mtodo de IShellLink que ainda tem que ser explicado o mtodo Resolve( ). Resolve( )
deve ser chamado depois que a interface IPersistFile de IShellLink for usada para carregar um arquivo de vnculo. Isso pesquisa o arquivo de vnculo especificado e preenche o objeto IShellLink
com os valores especificados no arquivo.
34
DICA
Na funo GetShellLinkInfo( ), mostrada na Listagem 16.5, observe o uso do array local AStr,
no qual os valores so recuperados. Essa tcnica usada no lugar de SetLength( ) para alocar espao para as strings o uso de SetLength( ) em tantas strings acarretaria na fragmentao do heap
da aplicao. O uso de Astr como um intermedirio impede que isso ocorra. Alm disso, como o
tamanho das strings s precisa ser definido uma vez, o uso de AStr acaba sendo ligeiramente mais
rpido.
Figura 16.6 O formulrio principal de Shell Link, mostrando um dos vnculos com o desktop.
35
37
Listagem 16.7 NewLinkU.pas a unidade com formulrio que ajuda a criar novo vnculo
unit NewLinkU;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
Buttons, StdCtrls;
type
TNewLinkForm = class(TForm)
Label1: TLabel;
Label2: TLabel;
edLinkTo: TEdit;
btnOk: TButton;
btnCancel: TButton;
cbLocation: TComboBox;
sbOpen: TSpeedButton;
OpenDialog: TOpenDialog;
procedure sbOpenClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
39
41
Extenses do shell
ltima palavra em termos de extensibilidade, o shell do Windows fornece um meio para voc desenvolver um cdigo capaz de ser executado dentro do prprio processo e namespace do shell. As
extenses do shell so implementadas como servidores COM in-process, que so criados e usados
pelo shell.
NOTA
Como as extenses do shell no fundo no passam de servidores COM, voc s conseguir entender
o que so se conhecer pelo menos um pouco do COM. Se voc no tem a menor idia do que seja
COM, o Captulo 15 explica os fundamentos.
Diversos tipos de extenses do shell esto disponveis para lidar com uma srie de aspectos do
shell. Tambm conhecida como tratador (ou handler), uma extenso do shell deve implementar
uma ou mais interfaces do COM. O shell tem suporte para os seguintes tipos de extenses do shell:
l
42
Tratadores copy hook implementam a interface ICopyHook. Essas extenses do shell permitem que voc receba notificaes sempre que uma pasta copiada, excluda, movida ou renomeada e para opcionalmente impedir que a operao ocorra.
Tratadores de menu de contexto implementam as interfaces IContextMenu e IShellExtInit.
Essas extenses do shell permitem que voc adicione itens ao menu de contexto de um determinado objeto de arquivo no shell.
Tratadores de arrastar e soltar tambm implementam as interface IContextMenu e IShellExtInit. A implementao dessas extenses do shell quase idntica dos tratadores de
menu de contexto, exceto pelo fato de serem chamadas quando um usurio arrasta um objeto e o solta em um novo local.
Tratadores de cones implementam as interfaces IExtractIcon e IPersistFile. Os tratadores
de cones permitem que voc fornea diferentes cones para instncias mltiplas do mesmo
tipo de objeto de arquivo.
Tratadores de folha de propriedades implementam as interfaces IShellPropSheetExt e
IShellExtInit e permitem que voc adicione pginas caixa de dilogo de propriedades associada a um tipo de arquivo.
Tratadores de soltar no destino implementam as interfaces IDropTarget e IPersistFile.
Essas extenses do shell permitem que voc controle o que acontece quando arrasta um objeto de shell sobre outro.
Tratadores de objeto de dados implementam as interfaces IDataObject e IPersistFile e fornecem o objeto de dados quando arquivos esto sendo arrastados e soltos ou copiados e colados.
O restante deste captulo dedicado a mostrar uma descrio mais detalhada das extenses do
shell que acabamos de descrever. Voc vai aprender sobre tratadores de copy hook, tratadores de
menu de contexto e tratadores de cones.
A primeira etapa na criao de um tratador de copy hook criar um objeto que descende de
TComObject e implementa a interface ICopyHook. Essa interface definida na unidade ShlObj da seguinte maneira:
type
ICopyHook = interface(IUnknown)
[{000214EF-0000-0000-C000-000000000046}]
function CopyCallback(Wnd: HWND; wFunc, wFlags: UINT;
pszSrcFile: PAnsiChar; dwSrcAttribs: DWORD; pszDestFile: PAnsiChar;
dwDestAttribs: DWORD): UINT; stdcall;
end;
O mtodo CopyCallback( )
Como voc pode ver, ICopyHook uma interface bastante simples e implementa apenas uma funo:
CopyCallback( ). Essa funo ser chamada sempre que uma pasta do shell for manipulada. Os prximos pargrafos descrevem os parmetros dessa funo.
Wnd a ala da janela que o tratador de copy hook deve usar como o pai de qualquer janela que
ele apresente. wFunc indica a operao que est sendo executada. Isso pode ser qualquer um dos valores mostrados na Tabela 16.5.
Tabela 16.5 Os valores de wFunc para CopyCallback( )
Constante
Valor
Significado
FO_COPY
$2
FO_DELETE
$3
FO_MOVE
$1
FO_RENAME
$4
PO_DELETE
$13
PO_PORTCHANGE
$20
PO_RENAME
$14
PO_REN_PORT
$34
wFlags mantm os flags que controlam a operao. Esse parmetro pode ser uma combinao
44
Constante
Valor
Significado
FOF_ALLOWUNDO
$40
FOF_MULTIDESTFILES
$1
Valor
Significado
FOF_NOCONFIRMATION
$10
Responde com Yes to All (sim para todos) para qualquer caixa
de dilogo exibida.
FOF_NOCONFIRMMKDIR
$200
FOF_RENAMEONCOLLISION
$8
FOF_SILENT
$4
FOF_SIMPLEPROGRESS
$100
tino.
Ao contrrio da maioria dos mtodos, essa interface no retorna um cdigo de resultado OLE.
Em vez disso, ela deve retornar um dos valores listados na Tabela 16.7, conforme definidos na unidade Windows.
Tabela 16.7 Os valores de wFlags de CopyCallback( )
Constante
Valor
Significado
IDYES
Permite a operao
IDNO
IDCANCEL
Implementao de TCopyHook
Sendo um objeto que implementa uma interface com um mtodo, no h muita coisa em TCopyHook:
type
TCopyHook = class(TComObject, ICopyHook)
protected
function CopyCallback(Wnd: HWND; wFunc, wFlags: UINT;
pszSrcFile: PAnsiChar;
dwSrcAttribs: DWORD; pszDestFile: PAnsiChar; dwDestAttribs: DWORD): UINT;
stdcall;
end;
45
DICA
Voc pode estar se perguntando por que a funo MessageBox( ) da API usada para exibir uma
mensagem em vez de se usar uma funo do Delphi, como MessageDlg( ) ou ShowMessage( ). A
razo simples: tamanho e eficincia. A chamada de qualquer funo fora da unidade Dialogs ou
Forms faria com que uma grande parte da VCL fosse vinculada DLL. Mantendo essas unidades
fora da clusula uses, a DLL da extenso do shell ocupa irrisrios 70KB.
Acredite se quiser, mas isso tudo que h para se falar sobre o objeto TCopyHook propriamente
dito. No entanto, ainda h um importante trabalho a ser feito antes de algum dia ele poder ser chamado: a extenso do shell deve ser registrada com o System Registry antes de ele poder funcionar.
Registro
Alm do registro normal exigido de qualquer servidor COM, um tratador de copy hook deve ter uma
entrada adicional no Registro, sob
HKEY_CLASSES_ROOT\directory\shellex\CopyHookHandlers
Alm disso, o Windows NT exige que todas as extenses do shell sejam registradas, conforme
as extenses do shell aprovadas, sob
HKEY_LOCAL_MACHINE\ SOFTWARE\Microsoft\Windows\CurrentVersion
\Shell Extensions\Approved
Voc pode utilizar vrias tcnicas para registrar as extenses do shell: elas podem ser registradas atravs de um arquivo REG ou atravs de um programa de instalao. A DLL da extenso do
shell propriamente dita pode ser auto-registrvel. Embora isso signifique um pouco mais de trabalho, a melhor soluo tornar cada DLL de extenso do shell auto-registrvel. Isso mais legvel,
pois cria sua extenso do shell em um pacote de um arquivo independente.
Conforme voc aprendeu no Captulo 15, os objetos COM so sempre criados a partir de fbricas de classes. Dentro da estrutura da VCL, os objetos de fbrica de classes tambm so responsveis pelo registro do objeto COM que criaro. Se um objeto COM requer entradas de Registro personalizadas (como o caso com uma extenso do shell), a definio dessas entradas s uma
questo de modificar o mtodo UpdateRegistry( ) da fbrica de classes. A Listagem 16.9 mostra a
unidade CopyMain completa, que inclui uma fbrica de classes especializada para executar um registro personalizado.
Listagem 16.9 CopyMain unidade principal da implementao de copy hook
unit CopyMain;
interface
46 uses Windows, ComObj, ShlObj;
47
O que faz a fbrica de classes TCopyHookFactory funcionar o fato de uma instncia dela, no o
TComObjectFactory normal, estar sendo criada na parte initialization da unidade. A Figura 16.7
mostra o que acontece quando voc tenta trocar o nome de uma pasta no shell depois que a DLL da
extenso do shell copy hook instalada.
48
IShellExtInit
A interface IShellExtInit usada para inicializar uma extenso do shell. Essa interface definida na
unidade ShlObj da seguinte maneira:
type
IShellExtInit = interface(IUnknown)
[{000214E8-0000-0000-C000-000000000046}]
function Initialize(pidlFolder: PItemIDList; lpdobj: IDataObject;
hKeyProgID: HKEY): HResult; stdcall;
end;
Initialize( ), sendo o nico mtodo dessa interface, chamado para inicializar o tratador do
menu de contexto. Os prximos pargrafos descrevem os parmetros desse mtodo.
pidlFolder um ponteiro para uma estrutura PItemIDList (lista de identificadores de item)
para a pasta que contm o item cujo menu de contexto est sendo exibido. lpdobj mantm o objeto
de interface IDataObject, usado para recuperar o objeto sobre o qual a ao est sendo executada.
hkeyProgID contm a chave do Registro para o objeto de arquivo ou tipo de pasta.
A implementao desse mtodo mostrada no cdigo a seguir. primeira vista, o cdigo pode
parecer complexo, mas na realidade ele se reduz a trs coisas: uma chamada para lpobj.GetData( )
para obter dados de IDataObject e duas chamadas para DragQueryFile( ) (uma chamada para obter
o nmero de arquivos e outra para obter o nome do arquivo). O nome do arquivo armazenado no
campo FFileName do objeto. Veja o cdigo a seguir:
function TContextMenu.Initialize(pidlFolder: PItemIDList; lpdobj: IDataObject;
hKeyProgID: HKEY): HResult;
var
Medium: TStgMedium;
FE: TFormatEtc;
begin
try
// Falha na chamada se lpdobj for nulo.
if lpdobj = nil then
begin
Result := E_FAIL;
Exit;
end;
with FE do
begin
cfFormat := CF_HDROP;
ptd := nil;
dwAspect := DVASPECT_CONTENT;
lindex := -1;
tymed := TYMED_HGLOBAL;
end;
// Apresenta os dados referenciados pelo ponteiro IDataObject em um meio de
// armazenamento HGLOBAL no formato CF_HDROP.
Result := lpdobj.GetData(FE, Medium);
if Failed(Result) then Exit;
try
49
IContextMenu
A interface IContextMenu usada para manipular o menu instantneo associado a um arquivo no
shell. Essa interface definida na unidade ShlObj da seguinte maneira:
type
IContextMenu = interface(IUnknown)
[{000214E4-0000-0000-C000-000000000046}]
function QueryContextMenu(Menu: HMENU;
indexMenu, idCmdFirst, idCmdLast, uFlags: UINT): HResult; stdcall;
function InvokeCommand(var lpici: TCMInvokeCommandInfo): HResult; stdcall;
function GetCommandString(idCmd, uType: UINT; pwReserved: PUINT;
pszName: LPSTR; cchMax: UINT): HResult; stdcall;
end;
Depois que o tratador for inicializado atravs da interface IShellExtInit, o prximo mtodo a
ser chamado IContextMenu.QueryContextMenu( ). Os parmetros passados para esse mtodo incluem uma ala de menu, o ndice no qual o primeiro item de menu ser inserido, os valores mnimo e
mximo dos IDs de item de menu e flags que indicam os atributos de menu. A implementao de
TContextMenu desse mtodo, mostrada a seguir, adiciona um item de menu com o texto Package
Info... ala de menu passada no parmetro Menu (observe que o valor de retorno de QueryContextMenu( ) o ndice do ltimo item de menu inserido mais um):
function TContextMenu.QueryContextMenu(Menu: HMENU; indexMenu, idCmdFirst,
idCmdLast, uFlags: UINT): HResult;
begin
FMenuIdx := indexMenu;
// Acrescenta um item ao menu de contexto
InsertMenu (Menu, FMenuIdx, MF_STRING or MF_BYPOSITION, idCmdFirst,
Package Info...);
// Retorna ndice do ltimo item inserirdo + 1
Result := FMenuIdx + 1;
end;
O prximo mtodo chamado pelo shell GetCommandString( ). O objetivo desse mtodo recuperar a string de comando ou ajuda independente de linguagem de um determinado item de
menu. Os parmetros desse mtodo incluem o offset do item de menu, flags indicando o tipo de informao a ser recebida, um parmetro reservado e um buffer de string com o tamanho do buffer. A
implementao de TContextMenu desse mtodo, mostrada a seguir, s precisa lidar com o fornecimento
da string de ajuda para o item de menu:
50
Quando voc d um clique no novo item no menu de contexto, o shell chamar o mtodo
InvokeCommand( ). O mtodo aceita um registro TCMInvokeCommandInfo como parmetro. Esse registro definido na unidade ShlObj da seguinte maneira:
type
PCMInvokeCommandInfo = ^TCMInvokeCommandInfo;
TCMInvokeCommandInfo = packed record
cbSize: DWORD;
{ deve ser SizeOf(TCMInvokeCommandInfo) }
fMask: DWORD;
{ qualquer combinao de CMIC_MASK_* }
hwnd: HWND;
{ pode ser NULL (ausncia de janela de proprietrio) }
lpVerb: LPCSTR;
{ uma string de MAKEINTRESOURCE(idOffset) }
lpParameters: LPCSTR; { pode ser NULL (ausncia de parmetro) }
lpDirectory: LPCSTR; { pode ser NULL (ausncia de diretrio especfico) }
nShow: Integer;
{ um dos valores SW_ da API ShowWindow( ) API }
dwHotKey: DWORD;
hIcon: THandle;
end;
A palavra de baixa ordem ou o campo lpVerb conter o ndice do item de menu selecionado.
Veja, a seguir, a implementao desse mtodo:
function TContextMenu.InvokeCommand(var lpici: TCMInvokeCommandInfo): HResult;
begin
Result := S_OK;
try
// Certifica-se de que no estamos sendo chamados por uma aplicao
if HiWord(Integer(lpici.lpVerb)) < > 0 then
begin
Result := E_FAIL;
Exit;
end;
// Executa o comando especificado por lpici.lpVerb.
// Retorna E_INVALIDARG se passarmos um nmero de argumentos invlido.
if LoWord(lpici.lpVerb) = FMenuIdx then
ExecutePackInfoApp(FFileName, lpici.hwnd)
else
Result := E_INVALIDARG;
except
MessageBox(lpici.hwnd, Error obtaining package information., Error,
MB_OK or MB_ICONERROR);
Result := E_FAIL;
end;
end;
51
Registro
Os tratadores de menu de contexto devem ser registrados sob
HKEY_CLASSES_ROOT\<tipo de arquivo>\shellex\ContextMenuHandlers
no Registro do sistema. Seguindo o modelo da extenso de copy hook, a capacidade de registro adicionada DLL criando-se um descendente de TComObject especializado. O objeto mostrado na Listagem 16.10, juntamente com todo o cdigo-fonte completo da unidade que contm TContextMenu.
A Figura 16.9 mostra o menu local do arquivo BPL com o novo item, e a Figura 16.10 mostra a janela PackInfo.exe do modo como chamada pelo tratador do menu de contexto.
53
{ TContextMenu }
{ TContextMenu.IContextMenu }
function TContextMenu.QueryContextMenu(Menu: HMENU; indexMenu, idCmdFirst,
idCmdLast, uFlags: UINT): HResult;
begin
FMenuIdx := indexMenu;
// Acrescenta um item de menu ao menu de contexto
InsertMenu (Menu, FMenuIdx, MF_STRING or MF_BYPOSITION, idCmdFirst,
'Package Info...');
// Retorna ndice do ltimo item inserido + 1
Result := FMenuIdx + 1;
end;
function TContextMenu.InvokeCommand(var lpici: TCMInvokeCommandInfo): HResult;
begin
Result := S_OK;
try
// Certifica-se de que no estamos sendo chamados por uma aplicao
if HiWord(Integer(lpici.lpVerb)) <<|>> 0 then
begin
Result := E_FAIL;
Exit;
end;
// Executa o comando especificado por lpici.lpVerb.
// Retorna E_INVALIDARG se recebeu nmero de argumentos invlido.
if LoWord(lpici.lpVerb) = FMenuIdx then
ExecutePackInfoApp(FFileName, lpici.hwnd)
else
Result := E_INVALIDARG;
except
MessageBox(lpici.hwnd, 'Error obtaining package information.', 'Error',
MB_OK or MB_ICONERROR);
Result := E_FAIL;
end;
end;
function TContextMenu.GetCommandString(idCmd, uType: UINT; pwReserved: PUINT;
pszName: LPSTR; cchMax: UINT): HRESULT;
const
SCmdStrA: String = 'Get information for the selected package.';
SCmdStrW: WideString = 'Get information for the selected package.';
begin
Result := S_OK;
try
// verifica se ndice de menu est correto e se o shell
// est pedindo string de ajuda
if (idCmd = FMenuIdx) and ((uType and GCS_HELPTEXT) < > 0) then
begin
// retorna string de ajuda para item do menu
if Win32MajorVersion >= 5 then // trata como Unicode para Win2k em diante
Move(SCmdStrW[1], pszName^,
Min(cchMax, Length(SCmdStrW) + 1) * SizeOf(WideChar))
54
Tratadores de cones
Os tratadores de cones permitem que diferentes cones sejam usados em vrias instncias do mesmo tipo de arquivo. Nesse exemplo, o objeto tratador de cones TIconHandler fornece diferentes
cones para diferentes tipos de arquivos Borland Package (BPL). Dependendo de um pacote ser de
runtime, projeto, ambos ou nenhum deles, um cone diferente ser exibido em uma pasta do shell.
Flags de pacote
Antes de obter implementaes das interfaces necessrias para essa extenso do shell, reserve um
momento para examinar o mtodo que determina o tipo de um determinado arquivo de pacote. O
mtodo retorna TPackType, que definida da seguinte maneira:
TPackType = (ptDesign, ptDesignRun, ptNone, ptRun);
Esse mtodo funciona chamando o mtodo GetPackageInfo( ) da unidade SysUtils para obter
os flags de pacote. Um ponto interessante a ser observado a respeito da otimizao do desempenho
que a funo LoadLibraryEx( ) da API chamada, no o procedimento LoadPackage( ) do Delphi,
para carregar a biblioteca de pacotes. Internamente, o procedimento LoadPackage( ) chama a API
LoadLibrary( ) para carregar a BPL e em seguida chama InitializePackage( ) para executar o cdigo de inicializao de cada uma das unidades no pacote. Como tudo o que queremos obter os flags
de pacote e, como os flags residem em um recurso vinculado BPL, podemos seguramente carregar
o pacote com LoadLibraryEx( ), usando o flag LOAD_LIBRARY_AS_DATAFILE.
57
O outro mtodo que deve ser implementado IExtractIcon.GetIconLocation( ). Os parmetros desse mtodo so discutidos nos prximos pargrafos.
uFlags indica o tipo de cone a ser exibido. Esse parmetro pode ser 0, GIL_FORSHELL ou
GIL_OPENICON. GIL_FORSHELL significa que o cone deve ser exibido em uma pasta do shell. GIL_
OPENICON significa que o cone deve estar no estado aberto caso as imagens para os estados aberto e
fechado estejam disponveis. Se esse flag no for especificado, o cone deve estar no estado normal,
ou fechado. Esse flag geralmente usado para objetos de pasta.
szIconFile o buffer que recebe o local do cone e cchMax o tamanho do buffer. piIndex um
inteiro que recebe o ndice de cone, que d mais detalhes quanto ao local do cone. pwFlags recebe
zero ou mais dos valores mostrados na Tabela 16.8.
A implementao de GetIconLocation( ) em TIconHandler mostrada a seguir:
function TIconHandler.GetIconLocation(uFlags: UINT; szIconFile: PAnsiChar;
cchMax: UINT; out piIndex: Integer; out pwFlags: UINT): HResult;
begin
Result := S_OK;
try
// retorna a DLL do nome do mdulo para localizar cone
58
Significado
GIL_DONTCACHE
Os bits da imagem fsica desse cone no devem ser colocados em cache pelo
responsvel pela chamada. Essa diferena deve ser levada em considerao,
pois um flag GIL_DONTCACHELOCATION pode ser introduzido em futuras
verses do shell.
GIL_NOTFILENAME
GIL_PERCLASS
GIL_PERINSTANCE
Cada objeto dessa classe tem seu prprio cone. Esse flag usado internamente
pelo shell para cuidar de casos como setup.exe, onde mais de um objeto com
nomes idnticos podem ser conhecidos ao shell e usam diferentes cones.
Implementaes tpicas de IExtractIcon no exigem esse flag.
GIL_SIMULATEDOC
Registro
Os tratadores de cones devem ser registrados na chave do Registro
HKEY_CLASSES_ROOT\<tipo de arquivo>\shellex\IconHandler.
Mais uma vez, um descendente de TComObjectFactory criado para lidar com o registro dessa
extenso do shell. Isso mostrado na Listagem 16.11, juntamente com o resto do cdigo-fonte do
tratador de cones.
A Figura 16.11 mostra uma pasta do shell contendo pacotes de diferentes tipos. Observe os diferentes cones de pacotes.
59
61
63
O mtodo GetInfoTip( ) chamado pelo shell para apanhar a dica de tela para um determinado arquivo. O parmetro dwFlags atualmente no utilizado. A string da dica de tela retornada no
parmetro ppwszTip.
NOTA
O parmetro ppwszTip aponta para uma string de caracteres larga. A memria para essa string precisa ser alocada dentro do tratador de dica de tela, usando o alocador de memria do shell. O shell
responsvel por liberar essa memria.
Implementao
Assim como outras extenses do shell, o tratador de dica de tela implementado como uma
DLL simples de servidor COM. O objeto COM nele contido implementa os mtodos IQueryInfo e IPersistFile. A Listagem 16.12 mostra o contedo de InfoMain.pas, a unidade principal para o projeto DDGInfoTip, que contm a implementao do Delphi para um tratador de dica
de tela.
Listagem 16.12 InfoMain.pas a unidade principal para o tratador de dica de tela
unit InfoMain;
64 {$WARN SYMBOL_PLATFORM OFF}
Existem alguns pontos interessantes nessa implementao. Observe que o alocador de memria do shell apanhado e armazenado no mtodo Initialize( ). O alocador mais tarde usado
para alocar memria para a string de dica de tela no mtodo GetInfoTip( ). O nome do arquivo em
questo passado para o tratador no mtodo Load( ). O trabalho realizado no mtodo GetInfoTip( ), que apanha informaes do pacote usando a funo GetPackageInfo( ), a respeito da qual
voc aprendeu anteriormente neste captulo. medida que a funo de callback PackageInfoCallback( ) chamada repetidamente de dentro de GetPackageInfo( ), a string de dica de tela concatenada arquivo por arquivo.
Registro
A tcnica usada para o registro da DLL do servidor COM quase idntica de outras extenses do
shell neste captulo, como voc pode ver na Listagem 16.12. A principal diferena a chave sob a
qual os tratadores de dica de tela so registrados; estes sempre so registrados sob
HKEY_CLASSES_ROOT\<extenso de arquivo>\shellex\{00021500-0000-0000-C000-000000000046}, onde a <extenso de arquivo> composta do ponto separador e da extenso propriamente dita.
A Figura 16.12 mostra esse tratador de dica de tela em ao.
Resumo
Este captulo inclui todos os diferentes aspectos de extenso do shell do Windows: cones da bandeja de notificao, AppBars, vnculos do shell e uma srie de extenses do shell. Ele uma continuao do conhecimento que voc obteve no captulo anterior, trabalhando com COM e ActiveX. No
Captulo 17, voc aprender mais sobre o desenvolvimento baseado em componentes por meio de
interfaces.
69
Usando a API
Open Tools
NESTE CAPTULO
l
Assistentes de formulrio
CAPTULO
17
Voc j se colocou diante da seguinte questo: O Delphi realmente bom, mas por que o IDE no
executa esta pequena tarefa que eu gostaria que ele fizesse? Se esse o seu caso, a API Open Tools
est a para satisfazer seus desejos. A API Open Tools do Delphi oferece a capacidade de integrar suas
prprias ferramentas, que trabalham em conjunto com o IDE do Delphi. Neste captulo, voc aprender as diferentes interfaces que compem a API Open Tools, como usar as interfaces e tambm como
aproveitar sua habilidade recm-adquirida para escrever um assistente repleto de recursos.
A Tabela 17.1 mostra as unidades que compem a API Open Tools e as interfaces que elas fornecem. A Tabela 17.2 lista as unidades obsoletas da API Open Tools, que permanecem apenas por
compatibilidade com os experts escritos em Delphi 4 ou anterior. Como as unidades obsoletas so
anteriores ao tipo interface nativo, elas empregam classes regulares do Delphi com mtodos abstratos virtuais, como substituto para interfaces verdadeiras. O uso de interfaces verdadeiras foi substitudo pela API Open Tools nas ltimas verses do Delphi e a edio atual da API Open Tools baseada principalmente em interface.
Tabela 17.1 Unidades na API Open Tools
Nome da unidade
Finalidade
ToolsAPI
VCSIntf
Finalidade
DesignConst
DesignEditors
DesignIntf
DesignMenus
DesignWindows
PropertyCategories
TreeIntf
VCLSprigs
VCLEditors
ClxDesignWindows
ClxEditors
ClxSprigs
72
Nome da unidade
Finalidade
FileIntf
Finalidade
EditIntf
ExptIntf
VirtIntf
IStreams
ToolIntf
NOTA
Voc pode estar se perguntando onde todo esse material referente a assistentes est documentado
no Delphi. Garantimos que est documentado, mas no nada fcil de se achar. Cada uma dessas
unidades contm documentao completa para a interface, classes, mtodos e procedimentos declarados em seu interior. Como no vamos repetir as mesmas informaes, convidamos para que
voc d uma olhada nas unidades, para ter acesso documentao completa.
Um assistente burro
Para comeo de conversa, voc criar um assistente muito simples que, por isso, chamado de assistente burro. O requisito mnimo para a criao de um assistente criar uma classe que implemente a
interface IOTAWizard. Apenas como referncia, IOTAWizard definido na unidade ToolsAPI da seguinte maneira:
type
IOTAWizard = interface(IOTANotifier)
[{B75C0CE0-EEA6-11D1-9504-00608CCBF153}]
{ Strings de IU do expert }
function GetIDString: string;
function GetName: string;
function GetState: TWizardState;
{ Inicia o AddIn }
procedure Execute;
end;
Essa interface consiste principalmente em algumas funes GetXXX( ) que so projetadas para
serem modificadas pelas classes descendentes, de modo a fornecer informaes especficas para cada
assistente. O mtodo Execute( ) a finalidade comercial de IOTAWizard. Execute( ) chamado pelo
IDE quando o usurio seleciona o seu assistente no menu principal ou no menu New Items (itens
novos), e nesse mtodo que o assistente deve ser criado e chamado.
Se voc tiver um olho astuto, deve ter percebido que IOTAWizard descende de outra interface,
chamada IOTANotifier. IOTANotifier uma interface definida na unidade ToolsAPI, que contm mtodos que podem ser chamados pelo IDE a fim de notificar um assistente quanto a vrias ocorrncias. Essa interface definida da seguinte maneira:
type
IOTANotifier = interface(IUnknown)
[{F17A7BCF-E07D-11D1-AB0B-00C04FB16FB3}]
{ Este procedimento chamado imediatamente depois que o item salvo
com sucesso. Ele no chamado para IOTAWizards }
procedure AfterSave;
{ Esta funo chamada imediatamente antes de o item ser salvo. Ela
no chamada para IOTAWizard }
procedure BeforeSave;
{ O item associado est sendo destrudo de modo que todas as referncias
devem ser liberadas. As excees so ignoradas. }
procedure Destroyed;
{ Este item associado foi modificado de alguma forma. Ele no chamado
para IOTAWizards }
procedure Modified;
end;
[{B75C0CE2-EEA6-11D1-9504-00608CCBF153}]
function GetMenuText: string;
end;
Como voc pode ver, essa interface descende de IOTAWizard e s inclui um mtodo adicional
para retornar a string de texto do menu.
Para juntar tudo isso e mostrar para que servem as informaes que voc aprendeu at agora, a
Listagem 17.1 mostra a unidade DumbWiz.pas, que contm o cdigo-fonte de TDumbWizard.
Listagem 17.1 DumbWiz.pas uma implementao de assistente simples
unit DumbWiz;
interface
uses
ShareMem, SysUtils, Windows, ToolsAPI;
type
TDumbWizard = class(TNotifierObject, IOTAWizard, IOTAMenuWizard)
// Mtodos de IOTAWizard
function GetIDString: string;
function GetName: string;
function GetState: TWizardState;
procedure Execute;
// Mtodo de IOTAMenuWizard
function GetMenuText: string;
end;
procedure Register;
implementation
uses Dialogs;
function TDumbWizard.GetName: string;
begin
Result := 'Dumb Wizard';
end;
function TDumbWizard.GetState: TWizardState;
begin
Result := [wsEnabled];
end;
function TDumbWizard.GetIDString: String;
begin
Result := 'DDG.DumbWizard';
end;
procedure TDumbWizard.Execute;
begin
MessageDlg('This is a dumb wizard.', mtInformation, [mbOk], 0);
end;
function TDumbWizard.GetMenuText: string;
begin
75
76
O assistente Wizard
preciso um pouco mais de trabalho para criar um assistente baseado em DLL (que o oposto de
um assistente baseado em uma biblioteca de componentes). Alm de demonstrar a criao de um assistente baseado em DLL, o assistente Wizard usado aqui como exemplo por duas razes: ilustrar
como os assistentes de DLL se relacionam com o Registro e manter a base de um cdigo-fonte que se
destina tanto a um assistente EXE como a um assistente DLL.
NOTA
Se voc no est acostumado com os detalhes das DLLs no Windows, d uma olhada no Captulo 9,
na verso eletrnica de Delphi 5 Guia do Desenvolvedor, no CD que acompanha este livro.
DICA
No h uma regra infalvel que determine se um assistente deve residir em um pacote na biblioteca
de componentes ou em uma DLL. Do ponto de vista de um usurio, a principal diferena entre as
duas que os assistentes de biblioteca de componentes s precisam da instalao de um pacote
para serem reconstrudos, enquanto os assistentes de DLL exigem uma entrada no Registro e o
Delphi deve ser fechado e reiniciado para que as mudanas faam efeito. No entanto, como um desenvolvedor, voc ver que os assistentes de pacotes so um pouco mais fceis de se lidar por uma
srie de razes. Especificamente, as excees se propagam automaticamente entre o seu assistente
e o IDE, voc no precisa usar sharemem.dll para gerenciamento de memria, no tem que fazer
nada especial para inicializar a varivel de aplicao da DLL e as mensagens de entrada/sada do
mouse e dicas de tela funcionaro a contento.
Com isso em mente, voc deve considerar o uso de um assistente de DLL quando quiser que o assistente seja instalado com um mnimo de trabalho por parte do usurio final.
Para que o Delphi reconhea um assistente de DLL, ele deve ter uma entrada no Registro do
sistema, sob a seguinte chave:
HKEY_CURRENT_USER\Software\Borland\Delphi\6.0\Experts
Interface do assistente
O objetivo do assistente Wizard fornecer uma interface para adicionar, modificar e excluir entradas de assistente de DLL do Registro sem ter que usar a complicada aplicao RegEdit. Primeiro, vamos examinar InitWiz.pas, a unidade que contm a classe do assistente (veja a Listagem 17.2).
77
Voc deve perceber algumas poucas diferenas entre essa unidade e a que usada para criar o
assistente burro. Mais importante, uma funo de inicializao do tipo TWizardInitProc exigida
como um ponto de entrada para o IDE na DLL do assistente. Nesse caso, essa funo chamada
InitWizard( ). Essa funo executa uma srie de tarefas de inicializao de assistente, incluindo as
seguintes:
l
Definio da ala da varivel Application da DLL como o valor retornado por IOTAServices.GetParentHandle( ) GetParentHandle( ) retorna a ala da janela que deve servir como o
pai para todas as janelas de nvel superior criadas pelo assistente.
Passagem da instncia recm-criada do assistente para o procedimento RegisterProc( )
para registrar o assistente com o IDE. RegisterProc( ) ser chamada uma vez para cada instncia de assistente que a DLL registra com o IDE.
Opcionalmente, InitWizard( ) tambm pode atribuir um procedimento do tipo TWizardTerminateProc para o parmetro Terminate servir como um procedimento de sada para o
assistente. Esse procedimento ser chamado imediatamente antes de o assistente ser descarregado pelo IDE e nele voc pode executar qualquer limpeza necessria. Inicialmente, esse
parmetro nil e, portanto, se voc no precisar de nenhuma limpeza especial, deixe seu
valor como nil.
79
ATENO
O mtodo de inicializao do assistente deve usar a conveno de chamada stdcall.
ATENO
Os assistentes de DLL que chamam funes da API Open Tools que possuem parmetros de string
devem ter a mesma unidade ShareMem em sua clusula uses; caso contrrio, o Delphi emitir uma
violao de acesso quando a instncia do assistente for liberada.
ModifyBtnClick(Sender: TObject);
FormCreate(Sender: TObject);
DoAddMod(Action: TAddModAction);
RefreshReg;
var
MainForm: TMainForm;
{ Chave do registro onde os assistentes do Delphi 6 so mantidos.
Verso EXE usa default, enquanto verso DLL apanha chave de
ToolServices.GetBaseRegistryKey. }
SDelphiKey: string = '\Software\Borland\Delphi\6.0\Experts';
implementation
uses InitWiz;
{$R *.DFM}
var
DelReg: TRegistry;
procedure TMainForm.RemoveBtnClick(Sender: TObject);
{ Tratador para clique no boto Remove. Remove item selecionado
do Registro. }
var
Item: TListItem;
begin
Item := WizList.Selected;
if Item < > nil then
begin
if MessageDlg(Format('Remove item "%s"', [Item.Caption]), mtConfirmation,
[mbYes, mbNo], 0) = mrYes then
DelReg.DeleteValue(Item.Caption);
RefreshReg;
end;
end;
procedure TMainForm.CloseBtnClick(Sender: TObject);
{ Tratador para clique no boto Close. Fecha aplicao. }
begin
Close;
end;
procedure TMainForm.DoAddMod(Action: TAddModAction);
{ Inclui um novo item de expert no Registro ou modifica existente. }
var
OrigName, ExpName, ExpPath: String;
Item: TListItem;
begin
if Action = amaModify then
// se modifica...
begin
Item := WizList.Selected;
if Item = nil then Exit;
// verifica se item est selecionado
ExpName := Item.Caption;
// inicializa variveis
if Item.SubItems.Count > 0 then
81
Essa a unidade responsvel pelo fornecimento da interface com o usurio para adicionar, remover e modificar entradas do assistente de DLL no Registro. Na seo initialization desta unidade, criado um objeto TRegistry, chamado DelReg. A propriedade RootKey de DelReg definida
como HKEY_CURRENT_USER e abre a chave \Software\Borland\Delphi\6.0\Experts a chave usada para
monitorar assistentes de DLL usando seu mtodo OpenKey( ).
Quando o assistente acionado, um componente TListView chamado ExptList preenchido
com os itens e os valores da chave de Registro mencionada anteriormente. Isso feito pela chamada
de DelReg.GetValueNames( ) para recuperar os nomes dos itens em TStringList. Um componente
TListItem adicionado a ExptList para cada elemento na lista de strings e o mtodo DelReg.ReadString( ) usado para ler o valor de cada item, que colocado na lista SubItems de TListItem.
O trabalho do Registro feito nos mtodos RemoveBtnClick( ) e DoAddMod( ). RemoveBtnClick( ) responsvel pela remoo do item de assistente atualmente selecionado do Registro. Ele primeiro verifica para se certificar de que um item est destacado; em seguida, ele abre uma
caixa de dilogo de confirmao. Finalmente, ele faz o trabalho chamando o mtodo DelReg.DeleteValue( ) e passando CurrentItem como parmetro.
DoAddMod( ) aceita um parmetro do tipo TAddModAction. Esse tipo definido da seguinte maneira:
type
TAddModAction = (amaAdd, amaModify);
Como se pode deduzir pelos valores do tipo, essa varivel indica se um novo item ser adicionado ou um item existente ser modificado. Essa funo primeiro verifica se h um item atualmente
selecionado ou, se no houver, se o parmetro Action armazena o valor amaAdd. Depois disso, se
Action for amaModify, o item e o valor existentes para o assistente so copiados nas variveis locais
ExpName e ExpPath . Posteriormente, esses valores so passados para uma funo chamada AddModExpert( ), que definida na unidade AddModU, mostrada na Listagem 17.4. Essa funo chama uma
caixa de dilogo na qual o usurio pode inserir informaes novas ou modificadas sobre nome ou
caminho para um assistente (veja a Figura 17.5). Ela retorna True quando o usurio fecha a caixa de
dilogo com o boto OK. Nesse ponto, um item existente modificado usando DelReg.RenameValue( ) e um valor novo ou modificado escrito com DelReg.WriteString( ).
83
uses
{$ifndef BUILD_EXE}
ShareMem,
// ShareMem exigida pela DLL
InitWiz in InitWiz.pas, // Material do assistente
{$endif}
ToolsAPI,
Forms,
Main in Main.pas {MainForm},
AddModU in AddModU.pas {AddModForm};
{$ifdef BUILD_EXE}
{$R *.RES}
{$else}
exports
InitWizard name WizardEntryPoint;
{$endif}
begin
{$ifdef BUILD_EXE}
// obrigatrio para EXE...
Application.Initialize;
Application.CreateForm(TMainForm, MainForm);
Application.Run;
{$endif}
end.
BUILD_EXE foi definida. Caso contrrio, ele construir um assistente baseado em DLL. Voc pode de-
finir uma condicional em Conditional Defines (definies condicionais) na pgina Directories/Conditionals (diretrios/condicionais) da caixa de dilogo Project, Options, que mostrada na
Figura 17.6.
85
Uma ltima observao sobre este projeto: observe que a funo InitWizard( ) da unidade
InitWiz est sendo exportada na clusula exports do arquivo de projeto. Voc deve exportar essa
funo com o nome WizardEntryPoint, que definido na unidade ToolsAPI.
ATENO
A Borland no fornece um arquivo ToolsAPI.dcu, o que significa que EXEs ou DLLs contendo uma
referncia a ToolsAPI em uma clusula uses s podem ser construdos com pacotes. Atualmente,
no possvel construir assistentes sem pacotes.
DDG Search
Voc se lembra do pequeno porm interessante programa que desenvolveu no Captulo 5? Nesta
seo, voc aprender como pode tornar essa aplicao til em um assistente do Delphi ainda mais
til, adicionando apenas um pouco de cdigo. Esse assistente chamado de DDG Search.
Primeiro, a unidade que faz a interface de DDG Search com o IDE, InitWiz.pas, mostrada na
Listagem 17.6. Voc vai perceber que essa unidade muito semelhante unidade homnima no
exemplo anterior. Isso foi proposital. Essa unidade apenas uma cpia da anterior, com algumas
mudanas necessrias envolvendo o nome do assistente e o mtodo Execute( ). Copiar e colar o
que chamamos de herana moda antiga. Afinal de contas, por que digitar mais do que o estritamente necessrio?
Listagem 17.6 InitWiz.pas a unidade que contm a lgica de assistente para o assistente DDG
Search
unit InitWiz;
interface
uses
Windows, ToolsAPI;
type
TSearchWizard = class(TNotifierObject, IOTAWizard, IOTAMenuWizard)
// Mtodos de IOTAWizard
function GetIDString: string;
function GetName: string;
function GetState: TWizardState;
procedure Execute;
// Mtodo de IOTAMenuWizard
86
87
A funo Execute( ) deste assistente mostra uma coisa um pouco diferente do que voc viu at
agora: o formulrio principal do assistente, MainForm, est sendo exibido de forma no-modal, ao
invs de modal. claro que isso requer um pouco mais de trabalho, pois voc tem que saber quando
um formulrio criado e quando a varivel do formulrio invlida. Isso pode ser feito certificando-se de que a varivel MainForm esteja definida como nil quando o assistente est inativo. Falaremos mais sobre isso posteriormente.
Outro aspecto desse projeto que foi significativamente alterado desde o Captulo 5 que o arquivo de projeto agora chamado de DDGSrch.dpr. Esse arquivo mostrado na Listagem 17.7.
Listagem 17.7 DDGSrch.dpr arquivo de projeto para DDGSrch
{$IFDEF BUILD_EXE}
program DDGSrch;
{$ELSE}
library DDGSrch;
{$ENDIF}
uses
{$IFDEF BUILD_EXE}
Forms,
{$ELSE}
ShareMem,
ToolsAPI,
InitWiz in 'InitWiz.pas',
{$ENDIF}
Main in 'MAIN.PAS' {MainForm},
SrchIni in 'SrchIni.pas',
SrchU in 'SrchU.pas',
PriU in 'PriU.pas' {ThreadPriWin},
MemMap in '..\..\Utils\MemMap.pas',
DDGStrUtils in '..\..\Utils\DDGStrUtils.pas';
{$R *.RES}
88
Mais uma vez, voc pode ver que esse projeto foi criado para ser compilado como um assistente baseado em EXE ou DLL. Quando compilado como um assistente, ele usa o cabealho library
para indicar que uma DLL e exporta a funo InitWiz( ) para ser inicializado pelo IDE.
Apenas algumas mudanas foram feitas na unidade Main nesse projeto. Como j dissemos, a varivel MainForm precisa ser definida como nil quando o assistente no est ativo. Como voc aprendeu no Captulo 2, a varivel de instncia MainForm automaticamente ter o valor nil na partida da
aplicao. Alm disso, no tratador de evento OnClose do formulrio, a instncia do formulrio liberada e a global MainForm redefinida como nil. Veja o mtodo a seguir:
procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree;
Application.OnShowHint := FOldShowHint;
MainForm := nil;
end;
O retoque final desse assistente trazer os arquivos para o Code Editor do IDE quando o usurio der um clique duplo na caixa de listagem do formulrio principal. Essa lgica manipulada por
um novo mtodo FileLBDblClick( ), mostrado a seguir::
procedure TMainForm.FileLBDblClick(Sender: TObject);
{ Chamado quando o usurio d um clique duplo na caixa de listagem.
Carrega o arquivo no IDE }
var
FileName: string;
Len: Integer;
begin
{ certifica-se de que o usurio deu um clique em um arquivo... }
if Integer(FileLB.Items.Objects[FileLB.ItemIndex]) > 0) then
begin
{ Elimina File e : da string }
FileName := Copy(FileName, 6, Length(FileName));
Len := Length(FileName);
if FileName[Len] = : then SetLength(FileName, Len 1);
{ Abre o projeto ou o arquivo }
{ $IFNDEF BUILD_EXE}
if CompareText(ExtractFileExt(FileName), .DPR) = 0 then
ActionSvc.OpenProject(FileName, True)
else
ActionSvc.OpenFile(FileName);
{ $ELSE }
89
91
93
95
DICA
Observe a seguinte linha da Listagem 17.8:
{ $WARN UNIT_PLATFORM OFF }
Essa diretiva do compilador usada para silenciar o aviso, durante a compilao, que gerado porque Main.pas usa a unidade FileCtrl, que uma unidade especfica da plataforma Windows. FileCtrl marcada como tal por meio da diretiva platform.
Assistentes de formulrio
Um outro tipo de assistente suportado pela API Open Tools o assistente de formulrio. Uma vez
instalados, os assistentes de formulrio so acessados a partir da caixa de dilogo New Items; eles
geram novos formulrios e unidades para o usurio. O Captulo 16 empregou esse tipo de assistente
para gerar novos formulrios AppBar; no entanto, voc no conseguiu ver o cdigo que fez o assistente funcionar.
extremamente simples criar um assistente de formulrio, embora voc deva implementar
uma boa quantidade de mtodos de interface. A criao de um assistente de formulrio pode ser dividida em cinco etapas bsicas:
1. Crie uma classe descendente de TCustomForm, TDataModule ou qualquer TWinControl, que
ser usada como a classe bsica do formulrio. Geralmente, essa classe residir em uma
unidade separada do assistente. Nesse caso, TAppBar servir como a classe bsica.
96
2. Crie um descendente de TNotifierObject que implemente as seguintes interfaces: IOTAWizard, IOTARepositoryWizard, IOTAFormWizard, IOTACreator e IOTAModuleCreator.
3. No seu mtodo IOTAWizard.Execute( ), voc normalmente chamar IOTAModuleServices.GetNewModuleAndClassName( ) para obter uma nova unidade e nome de classe para seu
assistente e IOTAModuleServices.CreateModule( ) para instruir o IDE a comear a criao
do novo mdulo.
4. Muitas das implementaes de mtodo das interfaces mencionadas acima possuem apenas
uma linha. Os no-triviais so os mtodos NewFormFile( ) e NewImplFile( ) de IOTAModuleCreator, que retornam o cdigo para o formulrio e a unidade, respectivamente. O mtodo IOTACreator.GetOwner( ) pode ser um pouco complicado, mas o exemplo a seguir
oferece uma boa tcnica para adicionar a unidade ao projeto atual (se houver).
5. Complete o procedimento Register( ) do assistente registrando um tratador para a nova
classe de formulrio, usando o procedimento RegisterCustomModule( ) na unidade
DsgnIntf e criando o assistente com a chamada ao procedimento RegisterPackageWizard( )
da unidade ToolsAPI.
A Listagem 17.9 mostra o cdigo-fonte de ABWizard.pas, que o assistente AppBar.
Listagem 17.9 ABWizard.pas, a unidade que contm a implementao do assistente AppBar
unit ABWizard;
interface
uses Windows, Classes, ToolsAPI;
type
TAppBarWizard = class(TNotifierObject, IOTAWizard, IOTARepositoryWizard,
IOTAFormWizard, IOTACreator, IOTAModuleCreator)
private
FUnitIdent: string;
FClassName: string;
FFileName: string;
protected
// Mtodos de IOTAWizard
function GetIDString: string;
function GetName: string;
function GetState: TWizardState;
procedure Execute;
// Mtodos de IOTARepositoryWizard / IOTAFormWizard
function GetAuthor: string;
function GetComment: string;
function GetPage: string;
function GetGlyph: HICON;
// Mtodos de IOTACreator
function GetCreatorType: string;
function GetExisting: Boolean;
function GetFileSystem: string;
function GetOwner: IOTAModule;
function GetUnnamed: Boolean;
// Mtodos de IOTAModuleCreator
function GetAncestorName: string;
function GetImplFileName: string;
function GetIntfFileName: string;
function GetFormName: string;
function GetMainForm: Boolean;
function GetShowForm: Boolean;
function GetShowSource: Boolean;
97
99
101
ter( ) no pacote de projeto que contm o assistente, usando as duas linhas a seguir:
102
RegisterCustomModule(TAppBar, TCustomModule);
RegisterPackageWizard(TAppBarWizard.Create);
Resumo
Depois da leitura deste captulo, voc deve ter uma compreenso maior das diversas unidades e interfaces envolvidas na API Open Tools do Delphi. Em particular, voc deve saber e entender as
questes envolvidas na criao de assistentes plugados no IDE. Este captulo completa essa parte do
livro. Na prxima seo, voc aprender tcnicas para criar aplicaes de porte empresarial, comeando com aplicaes baseadas em COM+ e MTS.
103
Desenvolvimento
BizSnap: escrevendo
Web Services
baseados em SOAP
NESTE CAPTULO
l
O que SOAP?
CAPTULO
20
O desenvolvimento de solues de eBusiness rapidamente a chave para o sucesso de muitas organizaes. Felizmente, a Borland possibilitou esse desenvolvimento rpido por meio de um novo recurso no Delphi 6, chamado BizSnap. BizSnap uma tecnologia que integra XML e Web Services
(servios da Web) usando um protocolo SOAP no Delphi 6.
O que SOAP?
SOAP um acrnimo que significa Simple Object Access Protocol (protocolo simples de acesso a objeto). SOAP um protocolo simplificado, usado para a troca de dados em um ambiente distribudo,
semelhante a partes da arquitetura CORBA e DCOM, mas com menos funcionalidade e overhead
resultante. SOAP troca dados usando documentos XML, por meio de HTTP (ou HTTPS) para suas
comunicaes. Uma especificao sobre SOAP est disponvel para referncia na Internet, no endereo http://www.w3.org/TR/SOAP/.
Web Services tambm usam uma forma de XML para instruir os usurios a respeito deles, chamada WSDL. WSDL uma abreviao de Web Services Description Language (linguagem de descrio de Web Services). WSDL usada por aplicaes cliente para identificar o que um Web Service
pode fazer, onde ele pode ser encontrado e como cham-lo.
A coisa mais maravilhosa sobre o BizSnap que voc no precisa aprender todos os detalhes de
SOAP, XML ou WSDL para criar aplicaes de Web Service.
Neste captulo, vamos mostrar como simples criar um Web Service, e depois vamos mostrar
como acessar esse servio a partir de uma aplicao cliente.
para essa classe. A interface ir expor somente as partes da classe que voc deseja oferecer ao restante do mundo atravs do seu Web Service.
O Delphi oferece um Web Service Wizard na guia WebServices do Object Repository. Voc
ver trs itens nessa guia. Neste ponto, vamos nos concentrar apenas no Soap Server Application
Wizard. Quando voc der um clique nisso, ver a caixa de dilogo New Soap Server Application (veja
a Figura 20.1). Essa caixa de dilogo dever ser conhecida, se voc j realizou algum desenvolvimento com servidor Web. De fato, Web Services so, na realidade, servidores Web que tratam da
resposta SOAP especfica.
Em nosso exemplo, escolhemos um CGI Stand-alone Executable. D um clique em OK e o assistente gerar um TWebModule, como mostra a Figura 20.2.
TWSDLHTMLPublish usado para publicar a lista de documentos WSDL que contm as infor-
maes sobre as interfaces invocveis disponveis. Isso permite que clientes fora do Delphi
identifiquem e usem os mtodos que se tornaram disponveis por meio de um determinado
Web Service.
No h nada que voc tenha que fazer com o Web Module neste ponto. No entanto, preciso
definir
e implementar uma interface invocvel.
106
Essa pequena unidade contm somente uma interface, que define os mtodos que pretendemos publicar como parte do nosso Web Service. Voc notar que nossa interface descende de
IInvokable. IInvokable uma interface simples, compilada com a opo {M+} do compilador, para
garantir que a RTTI seja compilada em todos os seus descendentes. Isso necessrio para permitir que os servios e clientes Web traduzam o cdigo e informaes simblicas passadas um para o
outro.
Em nosso exemplo, definimos dois mtodos para converter temperaturas e um mtodo Purpose( ), que retorna uma string. Alm disso, observe que fornecemos um GUID para essa interface,
para que tenha uma identificao exclusiva (para criar um GUID no seu prprio cdigo, basta pressionar Ctrl+Shift+G no editor).
ATENO
Observe que cada mtodo definido na interface invocvel definido usando a conveno de chamada stdcall. Essa conveno precisa ser usada ou ento a interface invocvel no funcionar.
Primeiro, observe que nossa implementao da interface descendente do objeto TInvokableClass. Existem dois motivos principais para fazermos isso. Os motivos a seguir foram apanhados
da ajuda on-line do Delphi 6:
l
108
em sua interface cai para zero. Os componentes invocadores no sabem quando liberar as
classes de implementao das interfaces que chamam. Como TInvokableClass sabe quando
ser liberada, voc no precisa fornecer seu prprio gerenciamento de tempo de vida para
esse objeto.
Alm do mais, voc ver que nossa classe TTempConverter implementa a interface ITempConverter. Os mtodos de implementao para realizar converses de temperatura so auto-explicativos.
Na seo de inicializao, a chamada para RegisterInvokableClass( ) registra a classe TTempConverter com o registro de invocao. Isso s necessrio no servidor para que o Web Service possa invocar a implementao correta da interface.
Isso realmente tudo o que preciso para a criao de um Web Service simples. Nesse ponto,
voc pode compilar o Web Service e coloc-lo em um diretrio executvel de um servidor Web,
como IIS ou Apache. Normalmente, esse seria um diretrio \Scripts ou \cgi-bin.
109
110
111
Para importar um Web Service em uma aplicao cliente, voc digita o caminho WSDL (o
URL especificado anteriormente) no campo WSDL or XML Schema Location e depois pressiona
o boto Generate para criar a unidade de importao. A unidade de importao para nosso Web
Service aparece na Listagem 20.5 e se parece bastante com nossa unidade de definio de interface original.
Listagem 20.5 Unidade de importao de Web Service
Unit TempConvImport;
interface
uses Types, XSBuiltIns;
type
ITempConverter = interface(IInvokable)
['{684379FC-7D4B-4037-8784-B58C63A0280D}']
function FahrenheitToCelsius(const AFValue: Double): Double;
function CelsiusToFahrenheit(const ACValue: Double): Double;
function Purpose: WideString; stdcall;
end;
implementation
112
stdcall;
stdcall;
Depois que a unidade tiver sido gerada, retorne ao formulrio principal da aplicao cliente e
use (uses) a unidade de importao recm-gerada. Isso far com que o formulrio principal tome
conhecimento da nova interface.
Enquanto voc insere esse cdigo, observe que o CodeInsight do Delphi est disponvel para o
prprio Web Service. Isso porque o Delphi adaptou o Web Service na sua aplicao como um objeto nativo. As implicaes aqui so bastante amplas: qualquer Web Service trazido para uma aplicao Delphi, no importando se esse servio instalado em um Solaris, Windows, Linux ou mainframe, e independente da linguagem em que o servidor est escrito, ser beneficiado com isso. Alm do
CodeInsight, a aplicao escrita para usar um Web Service tambm ganhar uma verificao de tipo
do compilador e outros recursos de depurao, devido forte integrao que existe entre estes. 113
Resumo
Web Services so uma nova e poderosa ferramenta para a computao distribuda, usando padres
abertos e a infra-estrutura existente para permitir a interoperao dentro e entre diferentes plataformas.
Neste captulo, mostramos como criar um Web Service simples e o cliente para usar esse servio. Demonstramos as etapas necessrias para se distribuir esse servidor e configurar a propriedade
do componente THTTPRIO do cliente. Neste ponto, voc j dever estar familiarizado o suficiente
com o desenvolvimento de Web Services com maior complexidade. Voc pode examinar muito
mais exemplos de Web Services no site da comunidade Borland. Um artigo que recomendamos
Managing Sessions with Delphi 6 Web Services (gerenciando sesses com Web Services do Delphi 6). Ele foi escrito por Daniel Polistchuck (Article ID: 27575) e pode ser lido em http://community.borland.com/article/0,1410,27575,00.html.
114
Desenvolvimento
DataSnap
Por Dan Miser
NESTE CAPTULO
l
Erros clssicos
CAPTULO
21
As aplicaes multicamadas esto sendo to comentadas quanto qualquer outro assunto em programao de computador hoje em dia. Isso est acontecendo por um bom motivo. As aplicaes multicamadas guardam muitas vantagens em relao s aplicaes cliente/servidor mais tradicionais. O
DataSnap da Borland um meio de ajud-lo a criar e oferecer uma aplicao multicamadas usando
Delphi, baseando-se em tcnicas e habilidades que voc acumulou quando usou o Delphi. Este captulo o acompanhar por algumas informaes gerais sobre o projeto de aplicao multicamadas,
mostrando-lhe como aplicar esses princpios para criar aplicaes de DataSnap slidas.
Dados A camada de dados responsvel por armazenar seus dados. Normalmente, isso
ser um RDBMS (ou SGBDR, Sistema de Gerenciamento de Banco de Dados Relacional),
como o Microsoft SQL Server, Oracle ou InterBase.
Comercial A camada comercial responsvel por apanhar dados da camada de dados em
um formato apropriado para a aplicao e realizar a validao final dos dados (tambm conhecida como imposio de regras comerciais). Essa tambm a camada do servidor de
aplicao.
Apresentao Tambm conhecida como camada GUI, essa camada responsvel por exibir os dados em um formato apropriado na aplicao cliente. A camada de apresentao
sempre fala com a camada comercial. Ela nunca fala diretamente com a camada de dados.
Nas aplicaes cliente/servidor tradicionais, voc tem uma arquitetura semelhante que mostramos na Figura 21.1. Observe que as bibliotecas do cliente para o acesso aos dados precisam estar
localizadas em cada mquina cliente isolada. Historicamente, esse tem sido um ponto de muito trabalho na distribuio de aplicaes cliente/servidor, devido a verses incompatveis de DLLs e um
dispendioso gerenciamento de configurao. Alm disso, como a maior parte da camada comercial
est localizada em cada cliente, voc precisa atualizar todos os clientes toda vez que precisar atualizar uma regra comercial.
Cliente
DBMS
BDE, ADO e outros.
116
IAppServer
Servidor
IAppServer
MIDAS.DLL
Cliente
DBMS
MIDAS.DLL
rio a determinar o que fazer com os registros que foram alterados desde que o usurio baixou o registro pela ltima vez. No verdadeiro esprito do Delphi, se essa caixa de dilogo no atender s suas
necessidades, voc poder remov-la e criar uma que atenda.
Modelo de briefcase
O modelo de briefcase baseado na metfora de uma maleta fsica (ou briefcase). Voc coloca seus
papis importantes na sua maleta e os transporta de um lugar para outro, desempacotando-os quando for necessrio. O Delphi oferece um meio de empacotar todos os seus dados e lev-los consigo
para a rua sem exigir uma conexo real com o servidor de aplicao ou servidor de banco de dados.
Tolerncia a falhas
Se a mquina servidora estiver indisponvel devido a circunstncias imprevistas, seria bom mudar
dinamicamente para um servidor de backup sem recompilar suas aplicaes cliente ou servidor. O
Delphi j oferece funcionalidade embutida para isso.
Balanceamento de carga
Ao distribuir sua aplicao cliente para mais pessoas, voc inevitavelmente comear a saturar a largura de banda do seu servidor. Existem duas maneiras de tentar balancear o trfego da rede: balanceamento de carga esttico e dinmico. Para o balanceamento de carga esttico, voc incluiria outra
mquina servidora e teria metade dos clientes usando o servidor A e a outra metade acessando o servidor B. No entanto, e se os clientes que usam o servidor A impuserem uma carga muito mais pesada
do que aqueles usam o servidor B? Usando o balanceamento de carga dinmico, voc poderia enfrentar essa situao dizendo a cada aplicao cliente qual servidor ela deve acessar. Existem muitos
algoritmos diferentes para balanceamento de carga dinmico, como aleatrio, seqencial, round
robin e menor trfego de rede. Do Delphi 4 em diante, isso feito por meio de um componente
para implementar o balanceamento de carga seqencial.
TClientDataset
TDatasetProvider
TDataset
TDispatchConnection
Cliente
118
Servidor
Vejamos algumas das opes que esto disponveis quando um RDM criado. A Figura 21.4
mostra a caixa de dilogo que o Delphi apresenta quando voc seleciona File, New, Remote Data
Module.
Servidor
Agora que voc viu como uma aplicao DataSnap tpica montada, vamos mostrar como fazer
com que isso acontea no Delphi. Vamos comear vendo algumas das opes disponveis quando se
configura o servidor.
Opes de instanciao
A especificao de uma opo de instanciao afeta a quantidade de cpias do processo servidor
que sero iniciadas. A Figura 21.5 mostra como as opes feitas aqui controlam como o seu servidor
se comporta.
Cliente1
Servidor1
Cliente1
Cliente2
Servidor2
Cliente2
Cliente3
Servidor3
Cliente3
Instncia nica
Instncia mltipla
Cliente1
Cliente2
Cliente3
Servidor
Thread1
Servidor
Thread2
Thread3
Threading em apartamento
ciMultiInstance Cada cliente que acessa o servidor COM usar a mesma instncia do ser-
vidor. Por default, isso significa que um cliente precisa esperar por outro antes de ter permisso para operar sobre o servidor COM. Veja na prxima seo, Opes de threading,
informaes mais detalhadas sobre como o valor especificado para o modelo de threading
tambm afeta esse comportamento. Isso equivalente ao acesso serial para os clientes. Todos os clientes precisam compartilhar uma conexo de banco de dados; portanto, a propriedade TDatabase.HandleShared precisa ser True.
119
ciSingleInstance Cada cliente que acessa o servidor COM usar uma instncia separada.
Isso significa que cada cliente consumir recursos do servidor para cada instncia do servidor a ser carregada. Isso equivalente ao acesso paralelo para os clientes. Se voc decidir seguir com essa opo, saiba que h limites para o BDE que poderiam tornar essa escolha menos atraente. Especificamente, o BDE 5.01 possui um limite de 48 processos por mquina.
Como cada cliente gera um novo processo servidor, voc s pode ter 48 clientes conectados de cada vez.
ciInternal O servidor COM no pode ser criado a partir de aplicaes externas. Isso
til quando voc deseja controlar o acesso a um objeto COM por meio de uma camada de
proxy. Um exemplo de uso dessa opo de instanciao pode ser encontrado no exemplo
<DELPHI>\DEMOS\MIDAS\POOLER.
Observe tambm que a configurao do objeto DCOM possui efeito direto sobre o modo de
instanciao do objeto. Veja mais informaes sobre esse tpico na seo Distribuindo aplicaes
DataSnap.
Opes de threading
O suporte para threading no Delphi 5 viu uma mudana drstica para melhor. No Delphi 4, a seleo do modelo de threading para um servidor EXE era insignificativa. O flag simplesmente marcava
o Registro para dizer ao COM que uma DLL era capaz de ser executada sob o modelo de threading
selecionado. Com o Delphi 5 e 6, a opo de modelo de threading agora se aplica a servidores EXE,
permitindo que o COM coloque as conexes em thread sem usar qualquer cdigo externo. A seguir, veja um resumo das opes de threading disponveis para um RDM:
l
120
Single A seleo de Single (nico) significa que o servidor s capaz de cuidar de um pedido de cada vez. Ao usar Single, voc no precisa se preocupar com as opes de threading,
pois o servidor roda em um thread e o COM trata dos detalhes de sincronismo das mensagens para voc. No entanto, essa a pior seleo que voc pode fazer se pretende ter um sistema multiusurio, pois o cliente B teria que esperar at que o cliente A terminasse o processamento antes que o cliente B pudesse sequer comear a trabalhar. Isso obviamente no
uma situao boa, pois o cliente A poderia estar fazendo um relatrio de resumo do fim
do dia ou alguma outra operao semelhante, que exija muito tempo.
Apartment A seleo do modelo de threading Apartment (apartamento) oferece o melhor
de todos os mundos quando combinada com a instanciao ciMultiInstance. Nesse cenrio, todos os clientes compartilham um processo servidor, por causa de ciMultiInstance,
mas o trabalho feito no servidor a partir de um cliente no impede o trabalho do outro cliente, devido opo de threading Apartment. Ao usar o threading em apartamento, voc
garante que os dados da instncia do seu RDM esto seguros, mas precisa proteger o acesso
a variveis globais usando alguma tcnica de sincronismo de thread, como PostMessage( ),
sees crticas, mutexes, semforos ou a classe wrapper TMultiReadExclusiveWriteSynchronizer do Delphi. Esse o modelo de threading preferido para os datasets BDE. Observe
que, se voc usar esse modelo de threading com os datasets BDE, ter que colocar um componente TSession no seu RDM e definir a propriedade AutoSessionName como True, para
ajudar o BDE a estar de acordo com os requisitos internos para threading.
Free Esse modelo oferece ainda mais flexibilidade no processamento do servidor, permitindo que vrias chamadas sejam feitas simultaneamente a partir do cliente para o servidor.
No entanto, junto com esse poder vem a responsabilidade. Voc precisa ter cuidado para
proteger todos os dados contra conflitos de thread dados de instncia e variveis globais.
Esse o modelo de threading preferido quando se usa ADO.
Both Essa opo efetivamente a mesma que a opo Free, com uma exceo callbacks
so colocados automaticamente em srie.
Servios de anncio
O RDM responsvel por comunicar quais servios estaro disponveis aos clientes. Se o RDM tiver que tornar um TQuery disponvel para uso no cliente, voc precisa colocar o TQuery no RDM junto com um TDatasetProvider. O componente TDatasetProvider ento ligado ao TQuery por meio da
propriedade TDatasetProvider.Dataset. Mais tarde, quando um cliente aparecer e quiser usar os dados do TQuery, ele poder fazer isso vinculando-se ao TDatasetProvider que voc acabou de criar.
Voc pode controlar quais provedores esto visveis ao cliente definindo a propriedade TDatasetProvider.Exported como True ou False.
Se, por outro lado, voc no precisar que um dataset inteiro fique exposto a partir do servidor
e s precisar que o cliente faa uma chamada de mtodo para o servidor, tambm poder fazer isso.
Enquanto o RDM tem o foco, selecione a opo de menu Edit, Add To Interface (adicionar interface) e preencha a caixa de dilogo com o prottipo de mtodo padro (que simplesmente uma declarao combinando com um mtodo que voc criar na sua implementao). Em seguida, voc
pode especificar a implementao desse mtodo no cdigo, como sempre fez, lembrando-se das implicaes do seu modelo de threading.
Cliente
Depois de montar o servidor, voc precisa criar um cliente para usar os servios fornecidos pelo servidor. Vamos dar uma olhada em algumas das opes disponveis quando estiver montando seu cliente DataSnap.
Opes de conexo
A arquitetura do Delphi para a conexo do cliente ao servidor comea com TDispatchConnection. Esse objeto bsico o pai de todos os tipos de conexo listados mais adiante. Quando o tipo
de conexo irrelevante para determinada seo, TDispatchConnection ser usado para indicar
esse fato.
TSocketConnection a conexo mais fcil de se configurar. Alm disso, ela s usa uma porta
para o trfego do DataSnap, de modo que seus administradores de firewall ficaro mais satisfeitos
se tiverem que fazer com que o DCOM trabalhe atravs do firewall. Voc precisa estar rodando o
ScktSrvr (encontrado no diretrio <DELPHI>\BIN) para que isso funcione, de modo que h um arquivo extra para distribuir e executar no servidor. O Delphi 4 exigia que voc instalasse o WinSock2
para usar esse tipo de conexo, o que significava outra instalao para clientes Windows 9x. No en- 121
tanto, se voc no estiver usando callbacks, pode considerar a definio de TSocketConnection.SupportCallbacks como False. Isso permite que voc fique com o WinSock 1 nas mquinas cliente.
Voc tambm pode usar TCORBAConnection se quiser usar CORBA como seu protocolo de transporte. CORBA pode ser considerado como o equivalente de padro aberto do DCOM, e inclui muitos recursos para audodescoberta, recuperao de falha e balanceamento de carga, realizados automaticamente por sua aplicao. Voc desejar examinar a arquitetura CORBA ao migrar suas
aplicaes DataSnap, para permitir conexes entre plataforma e entre linguagem.
O componente TWebConnection tambm est disponvel para voc. Esse componente da conexo permite que o trfego seja transportado por HTTP ou HTTPS. Ao usar esse tipo de conexo, algumas limitaes so as seguintes:
l
A mquina servidora precisa estar executando o MS Internet Information Server (IIS) 4.0
ou o Netscape 3.6 em diante.
No entanto, essas limitaes parecem compensar quando voc precisa oferecer uma aplicao
pela Internet ou por um firewall que no esteja sob o seu controle.
O Delphi 6 introduziu um novo tipo de conexo: a TSOAPConnection. Essa conexo se comporta de modo semelhante a WebConnection, mas conecta-se a um Web Service DataSnap. Ao contrrio
de quando se usa outros componentes de conexo DataSnap, voc no pode usar a propriedade
AppServer de TSoapConnection para chamar mtodos da interface do servidor de aplicao que no
sejam mtodos IAppServer. Em vez disso, para se comunicar com um mdulo de dados SOAP na interface da aplicao, use um objeto THTTPRIO separado.
Observe que todos esses transportes assumem uma instalao vlida do TCP/IP. A nica exceo a isso se voc estiver usando duas mquinas Windows NT para se comunicarem via DCOM.
Nesse caso, voc pode especificar qual protocolo o DCOM usar, executando o DCOMCNFG e
movendo o protocolo desejado para o topo da lista na guia Default Protocols (protocolos default).
O DCOM para Windows 9x tambm tem suporte para TCP/IP.
Conectando os componentes
A partir do diagrama na Figura 21.3, voc pode ver como a aplicao DataSnap se comunica entre as
camadas. Esta seo indica as principais propriedades e componentes que do ao cliente a capacidade de se comunicar com o servidor.
Para se comunicar do cliente para o servidor, voc precisa usar um dos componentes TDispatchConnection listados anteriormente. Cada componente possui propriedades especficas apenas a
esse tipo de conexo, mas todas elas permitem que voc especifique onde encontrar o servidor de
aplicao. TDispatchConnection semelhante ao componente TDatabase quando usado em aplicaes cliente/servidor, pois define o link com o sistema externo e serve como canal para outros componentes quando se comunica com elementos a partir desse sistema.
Quando voc tem uma conexo com o servidor, precisa de uma maneira de usar os servios que
exps no servidor. Isso pode ser feito incluindo-se um TClientDataset no seu cliente e conectando-o
ao TDispatchConnection por meio da propriedade RemoteServer. Quando essa conexo estiver feita,
voc poder ver uma lista dos provedores exportados no servidor percorrendo a lista na propriedade
ProviderName. Voc ver uma lista de provedores exportados que existem no servidor. Desse modo, o
componente TClientDataset semelhante a um TTable nas aplicaes cliente/servidor.
Voc tambm tem a capacidade de chamar mtodos personalizados que existem no servidor
usando a propriedade TDispatchConnection.AppServer. Por exemplo, a linha de cdigo a seguir
chamar a funo Login no servidor, passando dois parmetros de string e retornando um valor
booleano:
122 LoginSucceeded := DCOMConnection1.AppServer.Login(UserName, Password);
Configurando o servidor
Primeiro, vamos focalizar a mecnica da criao do servidor de aplicao. Depois que voc tiver
criado o servidor, vamos explorar como o cliente criado.
Esse mtodo chamado sempre que o servidor registrado ou perde seu registro. Alm das entradas do Registro especficas do COM, que so criadas na chamada UpdateRegistry herdada, voc
pode chamar os mtodos EnableXXXTransport( ) e DisableXXXTransport( ) para marcar esse objeto
como protegido.
123
NOTA
TSocketConnection s mostrar objetos registrados e protegidos na propriedade ServerName. Se
voc no quiser impor segurana alguma, desmarque a opo de menu Connections, Registered
Objects Only (somente objetos registrados) no SCKTSRVR.
Provedores
O servidor de aplicao ser responsvel por fornecer dados para o cliente, de modo que voc precisa encontrar um meio de servir dados a partir do servidor em um formato que seja utilizvel no cliente. Felizmente, o DataSnap oferece um componente TDatasetProvider para facilitar essa etapa.
Comece incluindo um TQuery no RDM. Se voc estiver usando um RDBMS, inevitavelmente
tambm precisar da configurao de um componente TDatabase. Por enquanto, voc vincular
TQuery a TDatabase e especificar uma consulta simples na propriedade SQL, como select *from
customer. Por ltimo, inclua um componente TDatasetProvider no RDM e vincule-o a TQuery por
meio da propriedade Dataset. A propriedade Exported no DatasetProvider determina se esse provedor ser visvel aos clientes. Essa propriedade tambm oferece a capacidade de controlar com facilidade quais provedores so visveis em runtime.
NOTA
Embora a discusso nesta seo focalize o uso do TDBDataset baseado no BDE, os mesmos princpios se aplicam se voc quiser usar qualquer outro descendente de TDataset para o acesso aos seus
dados. Existem vrias possibilidades j prontas, como DBExpress, ADO e InterBase Express, e vrios
componentes de terceiros esto disponveis para acessar banco de dados especficos.
Registrando o servidor
Quando o servidor de aplicao estiver montado, ele precisa ser registrado com o COM para que
esteja disponvel para as aplicaes cliente que se conectaro a ele. As entradas do Registro discutidas no Captulo 15 tambm so usadas para servidores DataSnap. Voc s precisa executar a aplicao servidora e a configurao do Registro ser acrescentada. No entanto, antes de registrar o servidor, no se esquea de salvar o projeto em primeiro lugar. Isso garante que o ProgID estar correto
desse ponto em diante.
Se voc preferir no executar a aplicao, poder passar o parmetro /regserver na linha de
comandos ao executar a aplicao. Isso s realizar o processo de registro e terminar a aplicao
imediatamente. Para remover as entradas do Registro associadas a essa aplicao, voc pode usar o
parmetro /unregserver.
Criando o cliente
Agora que voc tem um servidor de aplicao funcionando, vejamos como realizar algumas tarefas
bsicas com o cliente. Vamos discutir como apanhar os dados, como editar os dados, como atualizar
o banco de dados com as mudanas feitas no cliente e como lidar com erros durante o processo de
atualizao do banco de dados.
Apanhando dados
No decorrer de uma aplicao de banco de dados, preciso trazer dados do servidor para o cliente,
a fim de que sejam editados. Trazendo os dados para um cache local, voc pode reduzir o trfego na
rede e diminuir os tempos de transao. Em verses anteriores do Delphi, voc usaria atualizaes
em cache para realizar essa tarefa. No entanto, as mesmas etapas gerais ainda servem para aplica124 es DataSnap.
Um aspecto interessante da propriedade Delta que ela compatvel com a propriedade Data.
Em outras palavras, ela pode ser atribuda diretamente propriedade Data do componente ClientDataset. Isso permitir investigar o contedo atual da propriedade Delta em determinado momento.
Vrios mtodos esto disponveis para lidar com a edio dos dados no TClientDataset. Vamos nos referir a esses mtodos como mtodos de controle de alterao. Os mtodos de controle de
alterao permitem que voc modifique as alteraes feitas no TClientDataset de vrias maneiras.
NOTA
TClientDataset provou ser til de mais maneiras do que a inteno original. Ele tambm serve
como um excelente mtodo para armazenar tabelas na memria, o que no tem nada a ver com
DataSnap especificamente. Alm disso, devido ao modo como ele expe dados por meio das propriedades Data e Delta, ele provou ser til em diversas implementaes de padro de OOP. A discusso a respeito dessas tcnicas est fora do escopo deste captulo. No entanto, voc encontrar
documentos sobre esses tpicos em http://www.xapware.com ou http://www.xapware.com/ddg.
Desfazendo mudanas
A maioria dos usurios tem usado uma aplicao de processamento de textos que permita a operao Undo (desfazer). Essa operao apanha sua ao anterior e a retorna ao estado imediatamente
antes de ser iniciada. Usando TClientDataset, voc pode chamar cdsCustomer.UndoLastChange( )
para simular esse comportamento. A pilha de undo ilimitada, permitindo que o usurio continue a
recuar at o incio da sesso de edio, se assim desejar. O parmetro que voc passa para esse mtodo especifica se o cursor est posicionado no registro sendo afetado.
Se o usurio quisesse se livrar de todas as suas atualizaes ao mesmo tempo, haveria um modo
mais fcil do que chamar UndoLastChange( ) repetidamente. Basta chamar cdsCustomer.CancelUpdates( ) para cancelar todas as alteraes que foram feitas em uma nica sesso de edio.
Reconciliando dados
Depois que voc tiver acabado de fazer as mudanas na cpia local dos dados em TClientDataset,
precisar sinalizar sua inteno de aplicar essas mudanas de volta ao banco de dados. Isso feito
chamando cdsCustomer.ApplyUpdates( ). Nesse ponto, DataSnap tirar o Delta de cdsCustomer e o
passar ao servidor de aplicao, onde o DataSnap aplicar essas mudanas ao servidor de banco de
dados usando o mecanismo de reconciliao que voc escolheu para esse dataset. Todas as atualizaes so realizadas dentro do contexto de uma transao. Logo, explicaremos como os erros so
tratados durante esse processo.
O parmetro que voc passa a ApplyUpdates( ) especifica o nmero de erros que o processo de
atualizao permitir antes de considerar a atualizao como ruim e, subseqentemente, rolar todas as mudanas que foram feitas. A palavra erros aqui se refere a erros bsicos de violao, erros de
integridade referencial ou qualquer outra lgica comercial ou erros de banco de dados. Se voc especificar zero para esse parmetro, estar dizendo ao DataSnap que no tolerar quaisquer erros.
Portanto, se ocorrer um erro, todas as mudanas que voc fez no sero submetidas ao banco de dados. Essa a configurao que voc mais usar, pois combina melhor com as orientaes e princpios slidos de banco de dados.
No entanto, se voc quiser, poder especificar que um certo nmero de erros poder ocorrer,
enquanto ainda submete todos os registros que tiverem sucesso. A principal extenso desse conceito
passar -1 como parmetro para ApplyUpdates( ). Isso diz ao DataSnap que ele deve submeter cada
registro que puder, independente do nmero de erros encontrados in-process. Em outras palavras,
a transao sempre ser submetida quando esse parmetro for usado.
Se voc quiser ter o mximo de controle sobre o processo de atualizao incluindo a mudana da SQL que executar para uma insero, atualizao ou excluso , poder fazer isso no evento
TDatasetProvider.BeforeUpdateRecord( ). Por exemplo, quando um usurio quer excluir um registro, voc pode no querer realizar realmente uma operao de excluso no banco de dados. Em vez
disso, um flag definido para dizer s aplicaes que esse registro no est disponvel. Mais tarde,
um administrador pode rever essas excluses e submeter a operao fsica da excluso. O exemplo a
seguir mostra como fazer isso:
procedure TDataModule1.Provider1BeforeUpdateRecord(Sender: TObject;
SourceDS: TDataset; DeltaDS: TClientDataset; UpdateKind: TUpdateKind;
var Applied: Boolean);
begin
if UpdateKind=ukDelete then
begin
Query1.SQL.Text:=update CUSTOMER set STATUS=DEL where ID=:ID;
Query1.Params[0].Value:=DeltaDS.FieldByName(ID).OldValue;
Query1.ExecSQL;
Applied:= true;
end;
end;
Voc pode criar tantas consultas quantas quiser, controlando o fluxo e o contedo do processo de atualizao, com base em diferentes fatores, como UpdateKind e valores no Dataset. Ao inspecionar ou modificar registros do DeltaDS, no se esquea de usar as propriedades OldValue e NewValue do TField apropriado. O uso das propriedades TField.AsXXX gerar resultados imprevisveis.
Alm disso, voc pode impor regras comerciais aqui ou evitar totalmente a postagem de um registro para o banco de dados. Qualquer exceo que voc levanta aqui passar pelo mecanismo de
tratamento de erro do DataSnap, que explicaremos em seguida.
Depois que a transao terminar, voc ter oportunidade para lidar com erros. O erro pra em
eventos no servidor e no cliente, dando-lhe uma chance para tomar uma medida corretiva, registrar
o erro ou fazer qualquer outra coisa que queira com ele.
127
A primeira parada para o erro o evento DatasetProvider.OnUpdateError. Esse um timo lugar para se lidar com erros que voc esteja esperando ou que possa resolver sem maiores intervenes do cliente.
O destino final para o erro de volta ao cliente, onde voc pode lidar com o erro permitindo
que o usurio ajude a determinar o que fazer com o registro. Voc faz isso atribuindo um tratador ao
evento TClientDataset.OnReconcileError.
Isso til especialmente porque o DataSnap baseado em uma estratgica otimista de bloqueio de registro. Essa estratgia permite que vrios usurios trabalhem no mesmo registro ao mesmo tempo. Em geral, isso causa conflitos quando o DataSnap tenta reconciliar os dados de volta ao
banco de dados, pois o registro foi modificado desde o momento em que foi apanhado.
O valor do parmetro Action determina o que o DataSnap far com esse registro. Vamos focalizar, um pouco mais adiante, alguns outros fatores que afetam as aes que so vlidas nesse ponto.
A lista a seguir mostra as aes vlidas:
l
raSkip No atualiza esse registro de banco de dados especfico. Deixe o registro inaltera-
do na cache do cliente.
128
raMerge Mescla os campos desse registro no registro do banco de dados. Esse registro no
raCorrect Atualiza o registro do banco de dados com os valores que voc especifica. Ao
selecionar essa ao na caixa de dilogo Reconcile Error, voc pode editar os valores na
grade. Voc no pode usar esse mtodo se outro usurio alterou o registro do banco de
dados.
raRefresh Atualiza o registro no cache do cliente com o registro atual no banco de dados.
Nem todas essas opes fazem sentido (e, portanto, no sero exibidas) em todos os casos. Um
requisito para que as aes raMerge e raRefresh estejam disponveis que o DataSnap possa identificar o registro por meio da chave primria do banco de dados. Para tanto, defina a propriedade TField.ProviderFlags.pfInKey como True no componente TDataset do RDM para todos os campos na
sua chave primria.
DataSet.Open;
if VarIsEmpty(OwnerData) then
DataSet.First
else
begin
while not DataSet.Eof do
begin
if DataSet.FieldByName(au_id).Value = OwnerData then
break;
end;
end;
end;
end;
procedure TStateless.DataSetProvider1AfterGetRecords(Sender: TObject;
var OwnerData: OleVariant);
begin
with Sender as TDataSetProvider do
begin
OwnerData := Dataset.FieldValues[au_id];
DataSet.Close;
end;
end;
Client:
procedure TForm1.ClientDataSet1BeforeGetRecords(Sender: TObject;
var OwnerData: OleVariant);
begin
// KeyValue uma varivel OleVariant privada
if not (Sender as TClientDataSet).Active then
KeyValue := Unassigned;
OwnerData := KeyValue;
end;
procedure TForm1.ClientDataSet1AfterGetRecords(Sender: TObject;
var OwnerData: OleVariant);
begin
KeyValue := OwnerData;
end;
130
upWhereAll Essa configurao a mais restritiva, mas oferece a maior garantia de que o re-
gistro o mesmo que o usurio apanhou inicialmente. Se dois usurios editam o mesmo
registro, o primeiro usurio poder atualizar o registro, enquanto o segundo receber a infame mensagem de erro Another user changed the record (outro usurio alterou o registro).
Se voc quiser escolher ainda mais quais os campos so usados para realizar essa verificao, poder remover o elemento pfInWhere da propriedade TField.ProviderFlags correspondente.
131
upWhereChanged Essa configurao permite que dois usurios realmente editem o mesmo
registro ao mesmo tempo; desde que os dois usurios editem campos diferentes no mesmo registro, no haver deteco de coliso. Por exemplo, se o usurio A modifica o campo Endereo e atualiza o registro, o usurio B ainda pode modificar o campo DataNascimento e atualizar o registro com sucesso.
l
upWhereKeyOnly Essa configurao a mais liberal de todas. Desde que o registro exista no
banco de dados, a mudana de cada usurio ser aceita. Isso sempre modificar o registro
existente no banco de dados, de modo que pode ser visto como um meio de oferecer a funcionalidade do tipo o ltimo ganha.
A discusso inteira sobre o processo de reconciliao at aqui tem girado em torno da reconcialiao default, baseada em SQL. Isso significa que todos os eventos no TDataset bsico no sero
usados durante o processo de reconciliao. A propriedade TDatasetProvider.ResolveToDataset foi
criada para usar esses eventos durante a reconciliao. Por exemplo, se TDatasetProvider.ResolveToDataset for verdadeiro, a maioria dos eventos no TDataset ser disparada. Saiba que os eventos
usados so chamados apenas quando da aplicao de atualizaes de volta ao servidor. Em outras
palavras, se voc tiver um evento TQuery.BeforeInsert definido no servidor, ele s ser disparado
no servidor quando voc chamar TClientDataSet.ApplyUpdates. Os eventos no se integram aos
eventos correspondentes do TClientDataSet.
Datasets aninhados
A opo para relacionamentos mestre/detalhe so datasets aninhados. Os datasets aninhados permitem que uma tabela mestre realmente contenha datasets de detalhe. Alm da atualizao de registros mestre e detalhe em uma transao, eles permitem que o armazenamento de todos os registros
mestre e detalhe seja feito em um nico arquivo de briefcase, e voc pode usar as melhorias em
DBGrid para fazer exibir datasets de detalhe em suas prprias janelas. Um aviso se voc decidir usar
datasets aninhados: todos os registros de detalhe sero apanhados e trazidos para o cliente ao selecionar um registro mestre. Isso se tornar um engarrafamento possvel ao desempenho, se voc aninhar vrios nveis de datasets de detalhe. Por exemplo, se voc apanhar apenas um registro mestre
que tenha 10 registros de detalhe, e cada registro de detalhe tiver trs registros de detalhes ligados
ao detalhe de primeiro nvel, voc apanharia 41 registros inicialmente. Ao usar o vnculo no cliente,
voc s apanharia 14 registros inicialmente e obteria os outros registros netos ao rolar pelo TClientDataSet do detalhe.
Para configurar um relacionamento de dataset aninhado, voc precisa definir o relacionamento mestre/detalhe no servidor de aplicao. Isso feito usando a mesma tcnica que voc usou nas
aplicaes cliente/servidor, ou seja, definindo a instruo SQL para a TQuery do detalhe, incluindo o
parmetro de vnculo. Veja um exemplo:
select * orders where custno=:custno
Depois voc atribui o TQuery.Datasource para o TQuery do detalhe apontar para um componente TDatasource que est ligado ao TDataset mestre. Quando esse relacionamento for estabelecido,
voc s precisa exportar o TDatasetProvider que est ligado ao dataset mestre. O DataSnap inteligente o bastante para entender que o dataset mestre possui datasets de detalhe vinculados a ele e,
portanto, enviar os datasets de detalhe para o cliente como um TDatasetField.
No cliente, voc atribui a propriedade TClientDataset.ProviderName do mestre para o provedor do mestre. Depois, voc inclui campos persistentes ao TClientDataset. Observe o ltimo campo
no Fields Editor. Ele contm um campo com o mesmo nome do dataset de detalhe no servidor,
sendo declarado como um tipo TDatasetField. Nesse ponto, voc tem informaes suficientes para
usar o dataset aninhado no cdigo. No entanto, para tornar as coisas realmente fceis, voc pode incluir um TClientDataset de detalhe e atribuir sua propriedade DatasetField ao TDatasetField apropriado a partir do mestre. importante observar aqui que voc no configurou quaisquer outras
propriedades no TClientDataset de detalhe, como RemoteServer, ProviderName, MasterSource, MasterFields ou PacketRecords. A nica propriedade que voc definiu foi a propriedade DatasetField.
Nesse ponto, voc tambm pode vincular controles cientes de dados ao TClientDataset de detalhe.
Depois que voc acabar de trabalhar com os dados no dataset aninhado, ter que aplicar as
atualizaes de volta ao banco de dados. Isso feito chamando o mtodo ApplyUpdates( ) do TClientDataset mestre. O DataSnap aplicar todas as mudanas no TClientDataset mestre, incluindo os datasets de detalhe, de volta ao servidor dentro do contexto da transao.
Voc ver um exemplo no CD-ROM que acompanha este livro, no diretrio para este captulo, sob \NestCDS.
Vnculo no cliente
Lembre-se de que fizemos alguns avisos anteriormente com relao ao uso de datasets aninhados. A
alternativa ao uso de datasets aninhados a criao de um relacionamento mestre/detalhe no cliente. Para criar um vnculo mestre/detalhe usando esse mtodo, voc simplesmente cria um TDataset e
TDatasetProvider para o mestre e o detalhe no servidor.
No cliente, voc vincula dois componentes TClientDataset aos datasets que exportou no servidor. Depois, crie o relacionamento mestre/detalhe atribuindo a propriedade TClientDataset.MasterSource do detalhe ao componente TDatasource que aponta para o TClientDataset mestre.
A definio de MasterSource em um TClientDataset define a propriedade PacketRecords como
zero. Quando PacketRecords igual a zero, isso significa que o DataSnap deve apenas retornar as informaes de metadados para esse TClientDataset. No entanto, quando PacketRecords igual a 133
zero no contexto de um relacionamento mestre/detalhe, o significado muda. O DataSnap agora apanhar os registros para o dataset de detalhe para cada registro mestre. Resumindo, deixe a propriedade PacketRecords definida com o valor default.
Para reconciliar os dados mestre/detalhe no banco de dados em uma transao, voc precisa
escrever sua prpria lgica de ApplyUpdates. Isso no to simples quanto a maioria das tarefas em
Delphi, mas oferece controle flexvel sobre o processo de atualizao.
A aplicao de atualizaes a uma nica tabela normalmente disparada por uma chamada a
TClientDataset.ApplyUpdates. Esse mtodo envia os registros alterados do ClientDataset para seu
provedor na camada intermediria, onde o provedor gravar ento as alteraes no banco de dados. Tudo isso feito dentro do escopo de uma transao e feito sem qualquer interveno do programador. Para fazer a mesma coisa para as tabelas mestre/detalhe, voc precisa entender o que o
Delphi est fazendo por voc quando feita a chamada a TClientDataset.ApplyUpdates.
Quaisquer alteraes que voc faa a um TClientDataset so armazenadas na propriedade Delta. A propriedade Delta contm todas as informaes que por fim sero gravadas no banco de dados. O cdigo a seguir ilustra o processo de atualizao para a aplicao de propriedades Delta de
volta ao banco de dados. As Listagens 21.2 e 21.3 mostram as sees relevantes do cliente e do servidor para a aplicao de atualizaes em uma configurao mestre/detalhe.
Listagem 21.2 Atualizaes do cliente no relacionamento mestre/detalhe
procedure TClientDM.ApplyUpdates;
var
MasterVar, DetailVar: OleVariant;
begin
Master.CheckBrowseMode;
Detail_Proj.CheckBrowseMode;
if Master.ChangeCount > 0 then
MasterVar := Master.Delta else
MasterVar := NULL;
if Detail.ChangeCount > 0 then
DetailVar := Detail.Delta else
DetailVar := NULL;
RemoteServer.AppServer.ApplyUpdates(DetailVar, MasterVar);
{ Reconcia os pacotes de dados com erro. Como permitimos 0 erros, somente um
pacote de erro pode conter erros. Se nenhum pacote tiver erros, ento
atualizamos os dados. }
if not VarIsNull(DetailVar) then
Detail.Reconcile(DetailVar) else
if not VarIsNull(MasterVar) then
Master.Reconcile(MasterVar) else
begin
Detail.Reconcile(DetailVar);
Master.Reconcile(MasterVar);
Detail.Refresh;
Master.Refresh;
end;
end;
Listagem 21.3 Atualizaes do servidor no relacionamento mestre/detalhe
procedure TServerRDM.ApplyUpdates(var DetailVar, MasterVar: OleVariant);
var
ErrCount: Integer;
begin
134 Database.StartTransaction;
Embora esse mtodo funcione muito bem, ele realmente no oferece oportunidade para reutilizao de cdigo. Essa seria uma boa hora para estender o Delphi e oferecer uma reutilizao fcil.
Aqui esto as principais etapas exigidas para resumir o processo de atualizao:
1. Coloque os deltas para cada CDS em um array de variantes.
2. Coloque os provedores para cada CDS em um array de variantes.
3. Aplique todos os deltas em uma transao.
4. Reconcilie os pacotes de dados de erro retornados na etapa anterior e atualize os dados.
O resultado dessa abstrao fornecido na unidade utilitria mostrada na Listagem 21.4.
Listagem 21.4 Uma unidade oferecendo rotinas utilitrias e abstrao
unit CDSUtil;
interface
uses
DbClient, DbTables;
function RetrieveDeltas(const cdsArray : array of TClientDataset): Variant;
function RetrieveProviders(const cdsArray : array of TClientDataset): Variant;
procedure ReconcileDeltas(const cdsArray : array of TClientDataset;
vDeltaArray: OleVariant);
procedure CDSApplyUpdates(ADatabase : TDatabase; var vDeltaArray: OleVariant;
const vProviderArray: OleVariant);
implementation
uses
SysUtils, Provider, Midas, Variants;
type
PArrayData = ^TArrayData;
TArrayData = array[0..1000] of Olevariant;
135
//
//
//
//
Etapa
Etapa
Etapa
Etapa
1
2
3
4
Voc pode usar essa unidade em aplicaes de duas camadas ou trs camadas. Para passar de
um modelo de duas camadas para trs, voc exportaria uma funo no servidor que chame
CDSApplyUpdates em vez de chamar CDSApplyUpdates no cliente. Tudo o mais no cliente permanece
inalterado.
Voc encontrar um exemplo neste CD-ROM no diretrio para este captulo, sob \MDCDS.
Associaes
A escrita de uma aplicao de banco de dados relacional depende bastante de percorrer os relacionamentos entre as tabelas. Normalmente, voc achar conveniente representar seus dados altamente normalizados em uma viso mais achatada do que a estrutura de dados bsica. No entanto, a atualizao dos dados a partir dessas associaes exige mais cuidado da sua parte.
Fazendo isso, voc est dizendo a ClientDataset para registrar o nome da tabela para voc.
Agora, quando voc chamar ClientDataset1.ApplyUpdates( ), o DataSnap saber experimentar e
traduzir o nome de tabela que voc especificou, ao contrrio de permitir que o DataSnap tente descobrir qual seria o nome da tabela.
Uma tcnica alternativa seria usar um componente TUpdateSQL que s atualiza a tabela em
que voc est interessado. Isso permite que TQuery.UpdateObject seja usado durante o processo de
reconciliao e combina mais de perto com o processo usado nas aplicaes cliente/servidor tradicionais.
NOTA
Nem todos os TDatasets possuem uma propriedade UpdateObject. No entanto, voc ainda pode
usar a mesma tcnica, devido modificao feita em TUpdateSQL. Basta definir sua SQL para cada
ao (excluso, insero, modificao) e usar um cdigo semelhante a este:
procedure TForm1.DataSetProvider1BeforeUpdateRecord(Sender: TObject;
SourceDS: TDataSet; DeltaDS: TCustomClientDataSet;
UpdateKind: TUpdateKind; var Applied: Boolean);
begin
UpdateSQL1.DataSet := DeltaDS;
UpdateSQL1.SetParams(UpdateKind);
ADOCommand1.CommandText := UpdateSQL1.SQL[UpdateKind].Text;
ADOCommand1.Parameters.Assign(UpdateSQL1.Query[UpdateKind].Params);
ADOCommand1.Execute;
Applied := true;
end;
Voc encontrar um exemplo neste CD-ROM, no diretrio para este captulo, sob \Join1.
O mtodo mais antigo, que usar DatasetProvider.BeforeUpdateRecord( ) para desmembrar o pacote de dados e aplicar as atualizao s tabelas bsicas
O mtodo mais recente, que aplicar atualizaes usando a propriedade UpdateObject
139
Ao usar atualizaes em cache com uma associao de mltiplas tabelas, voc precisa configurar um componente TUpdateSQL para cada tabela que ser atualizada. Como a propriedade UpdateObject s pode ser atribuda a um componente TUpdateSQL, voc precisa vincular programaticamente todas as propriedades TUpdateSQL.Dataset ao dataset associado em TQuery.OnUpdateRecord e
chamar TUpdateSQL.Apply para associar os parmetros e executar a instruo SQL bsica. Nesse
caso, o dataset em que voc est interessado o dataset Delta. Esse dataset passado como parmetro para o evento TQuery.OnUpdateRecord.
Tudo o que voc precisa fazer atribuir as propriedades SessionName e DatabaseName para permitir que a atualizao ocorra no mesmo contexto das outras transaes e ligar a propriedade Dataset ao Delta que passado ao evento. O cdigo resultante para o evento TQuery.OnUpdateRecord aparece na Listagem 21.6.
Listagem 21.6 Associao usando TUpdateSQL
procedure TJoin2Server.JoinQueryUpdateRecord(DataSet: TDataSet;
UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction);
begin
usqlEmp.SessionName := JoinQuery.SessionName;
usqlEmp.DatabaseName := JoinQuery.DatabaseName;
usqlEmp.Dataset := Dataset;
usqlEmp.Apply(UpdateKind);
usqlFTEmp.SessionName := JoinQuery.SessionName;
usqlFTEmp.DatabaseName := JoinQuery.DatabaseName;
usqlFTEmp.Dataset := Dataset;
usqlFTEmp.Apply(UpdateKind);
UpdateAction := uaApplied;
end;
Como voc cumpriu as regras de atualizao de dados dentro da arquitetura DataSnap, o processo de atualizao inteiro disparado de forma transparente, como sempre acontece no DataSnap,
com uma chamada a ClientDataset1.ApplyUpdates(0).
Voc encontrar um exemplo neste CD-ROM, no diretrio para este captulo, sob \Join2.
DataSnap na Web
At mesmo com a introduo do Kylix, o Delphi est ligado plataforma Windows (ou Linux); portanto, quaisquer clientes que voc escreva precisam ser executados nesse tipo de mquina. Isso nem
sempre desejvel. Por exemplo, voc poderia oferecer acesso fcil aos dados que existem no seu
banco de dados a qualquer um que tenha uma conexo com a Internet. Como voc j escreveu um
servidor de aplicao que atua como agente para seus dados alm de abrir regras comerciais para
esses dados , seria desejvel reutilizar o servidor de aplicao em vez de reescrever, em outro ambiente, toda a camada de acesso aos dados e regra comercial.
HTML pura
Esta seo explica como aproveitar seu servidor de aplicao e, ao mesmo tempo, oferecer uma
nova camada de apresentao que usar HTML pura. Esta seo considera que voc est acostumado com o material abordado no Captulo 31 de Delphi 5 Guia do Desenvolvedor, que est no
CD-ROM deste livro. Usando esse mtodo, voc est introduzindo outra camada na sua arquitetura. O WebBroker atua como cliente para o servidor de aplicao e empacota esses dados na HTML
que ser exibida no browser. Voc tambm perde alguns dos benefcios do trabalho com o IDE do
Delphi, como o uso de controles cientes dos dados. No entanto, essa uma opo bastante vivel
140 para permitir o acesso aos seus dados em um formato HTML simples.
Depois de criar uma aplicao WebBroker e um WebModule, voc simplesmente inclui TDispatchConnection e TClientDataset no WebModule. Quando as propriedades estiverem preenchidas, voc
poder usar diversos mtodos diferentes para traduzir esses dados em HTML que, por fim, ser vista pelo cliente.
Uma tcnica vlida seria incluir um TDatasetTableProducer vinculado ao TClientDataset de seu
interesse. A partir da, o usurio poder dar um clique em um vnculo e passar para uma pgina de
edio, onde poder editar os dados e aplicar as atualizaes. Veja as Listagens 21.7 e 21.8 para uma
implementao de exemplo dessa tcnica.
Listagem 21.7 HTML para editar e aplicar atualizaes
<form action="<#SCRIPTNAME>/updaterecord" method="post">
<b>EmpNo: <#EMPNO></b>
<input type="hidden" name="EmpNo" value=<#EMPNO>>
<table cellspacing="2" cellpadding="2" border="0">
<tr>
<td>Last Name:</td>
<td><input type="text" name="LastName" value=<#LASTNAME>></td>
</tr>
<tr>
<td>First Name:</td>
<td><input type="text" name="FirstName" value=<#FIRSTNAME>></td>
</tr>
<tr>
<td>Hire Date:</td>
<td><input type="text" name="HireDate" size="8" value=<#HIREDATE>></td>
</tr>
<tr>
<td>Salary:</td>
<td><input type="text" name="Salary" size="8" value=<#SALARY>></td>
</tr>
<tr>
<td>Vacation:</td>
<td><input type="text" name="Vacation" size="4" value=<#VACATION>></td>
</tr>
</table>
<input type="submit" name="Submit" value="Apply Updates">
<input type="Reset">
</form>
Listagem 21.8 Cdigo para editar e aplicar atualizaes
unit WebMain;
interface
uses
Windows, Messages, SysUtils, Classes, HTTPApp, DBWeb, Db, DBClient,
MConnect, DSProd, HTTPProd;
type
TWebModule1 = class(TWebModule)
dcJoin: TDCOMConnection;
cdsJoin: TClientDataSet;
dstpJoin: TDataSetTableProducer;
dsppJoin: TDataSetPageProducer;
141
143
Observe que esse mtodo exige que muito cdigo personalizado seja escrito, e o conjunto de
recursos completo do DataSnap no implementado nesse exemplo especificamente, a reconciliao de erro. Voc pode continuar a melhorar esse exemplo para ser mais robusto se usar essa tcnica extensivamente.
ATENO
imperativo que voc considere o conceito de estado ao escrever seu WebModule e servidor de
aplicao. Como o HTTP um protocolo sem estado, voc no pode contar com os valores das propriedades como sendo iguais aos que voc deixou quando a chamada terminou.
DICA
O WebBroker um meio de apanhar seus dados para browsers Web. Usando o WebSnap, voc
pode estender as capacidades da sua aplicao ainda mais, usando os novos recursos que o WebSnap oferece, como scripting e suporte para sesso.
Para executar este exemplo, no se esquea de compilar e registrar a aplicao de exemplo Join2. Em seguida, compile a aplicao da Web (uma verso CGI ou ISAPI) e coloque o executvel em
um diretrio capaz de trabalhar com script para o seu servidor da Web. O cdigo tambm espera encontrar o arquivo join.htm no diretrio de scripts; portanto, copie-o tambm. Depois, basta apontar
seu browser para http://localhost/scripts/WebJoin.exe e ver os resultados desse exemplo.
Voc encontrar um exemplo neste CD-ROM, no diretrio para este captulo, sob \WebBrok.
InternetExpress
Com o InternetExpress, voc pode melhorar a funcionalidade de uma tcnica WebModule pura para
permitir uma experincia mais rica no cliente. Isso possvel devido ao uso de padres abertos,
como XML e JavaScript, no InternetExpress. Usando o InternetExpress, voc pode criar um
front-end apenas com browser para o seu servidor de aplicao DataSnap: nada de controles ActiveX para baixar; nada de instalao no cliente e requisitos de configurao; nada alm de um browser Web alcanando um servidor Web.
Para usar o InternetExpress, voc dever ter algum cdigo rodando em um servidor Web.
Para este exemplo, usaremos uma aplicao ISAPI, mas voc poderia usar CGI ou ASP. A finalidade do agente Web apanhar pedidos do browser e pass-los para o servidor de aplicao. A
colocao de componentes InternetExpress na aplicao agente da Web torna essa tarefa muito
fcil.
Este exemplo usar um servidor de aplicao DataSnap padro, que possui clientes, pedidos e
funcionrios. Os clientes e os pedidos esto associados em um relacionamento de dataset aninhado
(para obter mais informaes sobre datasets aninhados, leia a prxima seo), enquanto o dataset
de funcionrios servir como tabela de pesquisa. Depois que o servidor de aplicao tiver sido criado e registrado, voc poder focalizar a criao da aplicao agente Web, que se comunicar com o
servidor de aplicao.
Crie uma nova aplicao ISAPI selecionando File, New, Web Server Application no Object
Repository. Acrescente um componente TDCOMConnection no WebModule. Isso atuar como vnculo com o servidor de aplicao; portanto, preencha a propriedade ServerName com o ProgID do servidor de aplicao.
Em seguida, voc acrescentar no WebModule um componente TXMLBroker a partir da pgina
InternetExpress da Component Palette e definir as propriedades RemoteServer e ProviderName
como o CustomerProvider. O componente TXMLBroker atua de modo semelhante ao TClientDataset. Ele responsvel por apanhar pacotes de dados do servidor de aplicao e passar esses pacotes
de dados para o browser. A principal diferena entre o pacote de dados em um TXMLBroker e um
144 TClientDataset que o TXMLBroker traduz os pacotes de dados do DataSnap para XML. Voc tambm
Com o Web Page Editor ativo, d um clique no boto Insert (inserir) para exibir a caixa de dilogo Add Web Component (adicionar componente Web) (veja a Figura 21.7). Essa caixa de dilogo
contm uma lista de componentes Web que podem ser acrescentados na pgina HTML. Essa lista
baseada no componente pai (a seo no canto superior esquerdo) que est atualmente selecionado.
Por exemplo, inclua um componente Web DataForm ao n raiz para permitir que os usurios finais
apresentem e editem informaes de banco de dados em um layout tipo formulrio.
Figura 21.7 A caixa de dilogo Add Web Component trazida a partir do Web Page Editor.
Se, depois disso, voc selecionar o n DataForm no Web Page Editor, poder dar um clique no
boto Insert (inserir) novamente. Observe que a lista de componentes disponveis nesse ponto diferente da lista exibida na etapa anterior. Depois de selecionar o componente FieldGroup, voc
ver um aviso no painel de visualizao, informando que a propriedade TXMLBroker para o FieldGroup no est atribuda. Atribuindo o XMLBroker no Object Inspector, voc notar imediata145
mente o layout da HTML no painel de visualizao do Web Page Editor. Ao continuar a modificar
propriedades ou incluir componentes, o estado da pgina HTML ser constantemente atualizado
(veja a Figura 21.8).
Figura 21.8 O Web Page Editor aps a criao de uma pgina HTML.
O nvel de personalizao disponvel com os componentes Web padro praticamente ilimitado. As propriedades facilitam a mudana das legendas de campo, do alinhamento e das cores; a incluso de cdigo HTML diretamente personalizado; e at mesmo o uso de folhas de estilo. Alm do
mais, se o componente no se ajustar exatamente s suas necessidades, voc sempre poder criar um
componente descendente e usar isso em seu lugar. A estrutura realmente to extensvel quanto a
sua imaginao permita.
Para poder chamar a DLL ISAPI, voc ter que coloc-la em um diretrio virtual capaz de executar o script. Voc tambm precisa mover os arquivos JavaScript encontrados em <DELPHI>\SOURCE\WEBMIDAS para um local vlido no seu servidor Web e modificar a propriedade TInetXPageProducer.IncludePathURL para que aponte para o URI dos arquivos JavaScript. Depois disso, a pgina est
pronta para ser vista.
Para acessar a pgina, tudo o que voc precisa de um browser capaz de executar JavaScript.
Basta apontar o browser para http://localhost/inetx/inetxisapi.dll e os dados aparecero no
browser. A Figura 21.9 mostra uma tela capturada durante o uso da aplicao.
Voc pode detectar erros de reconciliao durante o processo ApplyUpdates, como j est
acostumado a fazer em uma aplicao DataSnap independente. Essa capacidade torna-se possvel quando voc atribui a propriedade TXMLBroker.ReconcileProducer a um TPageProducer. Sempre que ocorre um erro, o Content do TPageProducer atribudo a essa propriedade ser retornado
ao usurio final.
Um TPageProducer especializado, TReconcilePageProducer, est disponvel por meio da instalao do pacote InetXCustom.dpk, encontrado em <DELPHI>\DEMOS\MIDAS\INTERNETEXPRESS\INETXCUSTOM.
Esse PageProducer gera HTML que atua de modo semelhante caixa de dilogo DataSnap Reconciliation Error padro (veja a Figura 21.10).
Voc encontrar um exemplo neste CD-ROM, no diretrio para este captulo, sob
\InetX.
146
147
Esse mtodo exige mais cdigo e esforo do que nas verses anteriores do Delphi, onde voc
simplesmente atribuiria a propriedade Table1.Provider.Data propriedade ClientDataset1.Data.
No entanto, essa funo ajudar a tornar o cdigo adicional menos observvel.
Voc tambm pode usar o componente TClientDataset para apanhar os dados de um TDataset
durante o projeto, selecionando o comando Assign Local Data (atribuir dados locais) no menu de
contexto do componente TClientDataset. Depois, voc especifica o cmp TDataset que contm os
dados que voc deseja, e os dados so trazidos para o TClientDataset e armazenados na propriedade
Data.
ATENO
Se voc tivesse que salvar o arquivo nesse estado e comparar o tamanho do arquivo DFM para o tamanho antes de executar esse comando, notaria um aumento no tamanho do DFM. Isso porque o
Delphi armazenou todos os metadados e registros associados ao TDataset no DFM. O Delphi s
colocar esses dados em stream no DFM se o TClientDataset estiver Active. Voc tambm pode
remover esse espao executando o comando Clear Data (apagar dados) no menu de contexto de
TClientDataset.
Se voc quiser ter a flexibilidade total que uma atribuio de provedor oferece, ter que atribuir a propriedade AppServer. Em runtime, voc pode atribuir a propriedade AppServer no cdigo. Isso
pode ser to simples quanto a seguinte instruo, encontrada em FormCreate:
ClientDataset1.AppServer:=TLocalAppServer.Create(Table1);
148 ClientDataset1.Open;
Voc pode atribuir a propriedade AppServer durante o projeto. Se deixar a propriedade RemoteServer em branco em um TClientDataset, voc poder atribuir um componente TDatasetProvider
propriedade TClientDataset.ProviderName.
Uma desvantagem importante no uso da propriedade TClientDataset.ProviderName que ela
no pode ser atribuda a provedores que residem em outro formulrio ou DataModule durante o projeto. Isso porque o Delphi 6 introduziu o componente TLocalConnection. TLocalConnection descobrir e
expor automaticamente quaisquer TDatasetProviders que encontrar com o mesmo proprietrio.
Para usar esse mtodo de atribuio de provedores, atribua a propriedade ClientDataset.RemoteServer para ser o componente LocalConnection no formulrio ou DataModule externo. Depois disso, voc
ter a lista de provedores para essa LocalConnection na propriedade ClientDataset.ProviderName.
A diferena principal entre o uso de componentes TDataset e ClientDataset que, quando
voc est usando ClientDataset, est usando a interface IAppServer para agenciar seus pedidos de
dados ao componente TDataset bsico. Isso significa que voc estar manipulando as propriedades,
os mtodos, os eventos e os campos do componente TClientDataset, e no do componente TDataset. Pense no componente TDataset como se estivesse em uma aplicao separada e, portanto, no
pudesse ser manipulado diretamente por voc no cdigo. Coloque todos os componentes do seu
servidor em um DataModule separado. A colocao dos componentes TDatabase, TDataset e TLocalConnection em um DataModule separado efetivamente prepara sua aplicao para uma transio mais
fcil, mais adiante, para uma distribuio multicamadas. Outro benefcio de se fazer isso que pode
ajud-lo a pensar em DataModule como algo em que o cliente no pode tocar com facilidade. Novamente, essa uma boa preparao para a sua aplicao (e para o seu prprio raciocnio) quando for
a hora de transportar essa aplicao para uma distribuio em multicamadas.
Erros clssicos
O erro mais comum na criao de uma aplicao multicamadas a introduo de conhecimento
desnecessrio da camada de dados na camada de apresentao. Alguma validao mais adequada
na camada de apresentao, mas o modo como essa validao realizada que determina sua adequao em uma aplicao multicamadas.
Por exemplo, se voc estiver passando instrues da SQL dinmica do cliente para o servidor,
isso gera uma dependncia, em que a aplicao cliente sempre deve estar sincronizada com a camada de dados. Fazer as coisas dessa maneira introduz mais partes em movimento que precisam ser coordenadas na aplicao multicamadas geral. Se voc mudar uma das estruturas das tabelas na camada de dados, ter que atualizar todas as aplicaes cliente que enviam SQL dinmica, de modo que
agora possam enviar a instruo SQL correta. Isso certamente limita o benefcio mantido por uma
aplicao de cliente magro desenvolvida de forma adequada.
Outro exemplo de um erro clssico quando a aplicao cliente tenta controlar o tempo de
vida da transao, em vez de permitir que a camada comercial cuide disso em favor do cliente. Quase sempre, isso implementado pela exposio de trs mtodos da instncia de TDataBase no servidor BeginTransaction( ), Commit( ) e Rollback( ) e pela chamada desses mtodos a partir do
cliente. Quando as coisas so feitas dessa forma, o cdigo do cliente se torna muito mais complicado
para se manter e infringe o princpio de que a camada de apresentao deve ser a nica camada responsvel pela comunicao com a camada de dados. A camada de apresentao nunca dever se basear nessa ttica. Em vez disso, voc deve enviar suas atualizaes para a camada comercial e permitir que essa camada trate da atualizao dos dados em uma transao.
Questes de licenciamento
O licenciamento tem sido um assunto difcil para muitos pessoas desde que o DataSnap foi introduzido inicialmente no Delphi 3. As inmeras opes para distribuio dessa tecnologia contriburam
para essa confuso. Esta seo explica os requisitos gerais de quando voc precisa adquirir uma licena do DataSnap. No entanto, o nico documento legalmente relacionado ao licenciamento est
em DEPLOY.TXT, localizado no diretrio do Delphi 6. Finalmente, para obter a resposta definitiva
para uma situao especfica, voc precisa entrar em contato com o representante de vendas local
da Borland. Outras orientaes e exemplos esto disponveis em
http://www.borland.com/midas/papers/licensing/
As informaes desse documento foram preparadas para responder a alguns dos cenrios mais
comuns em que o DataSnap utilizado. As informaes sobre preos e opes tambm esto includas no documento.
O critrio principal para se determinar a necessidade de uma licena do DataSnap para a sua
aplicao se o pacote de dados DataSnap atravessa um limite de mquina. Se isso acontecer e voc
utilizar os componentes DataSnap em ambas as mquinas, ento ter que adquirir uma licena. Se
no acontecer (como nos exemplos de uma e duas camadas, apresentados anteriormente), voc estar usando a tecnologia DataSnap, mas no ser preciso adquirir uma licena para usar o DataSnap
dessa maneira.
Configurao do DCOM
A configurao do DCOM se parece mais com uma arte do que uma cincia. Existem muitos aspectos envolvidos em uma configurao DCOM completa e segura, mas esta seo o ajudar a entender alguns dos fundamentos dessa arte oculta.
Depois de registrar seu servidor de aplicao, seu objeto servidor estar disponvel para personalizao no utilitrio DCOMCNFG da Microsoft. Esse utilitrio est includo nos sistemas NT automaticamente, mas um download separado para mquinas Win9x. Como um lembrete, existem
muitos bugs no DCOMCNFG; o principal que o DCOMCNFG s pode ser executado em mquinas Wi9x que estejam com o compartilhamento em nvel de usurio ativado. Isso, naturalmente,
exige um domnio. Isso nem sempre possvel ou desejvel em uma rede ponto a ponto, como em
duas mquinas Windows 9x. Isso tem feito com que muitas pessoas pensem incorretamente que
necessrio o uso de uma mquina NT para se trabalhar com DCOM.
Se voc puder executar o DCOMCNFG, poder selecionar o servidor de aplicao registrado
e dar um clique no boto Properties para revelar informaes sobre o seu servidor. A pgina Identity (identidade) um bom lugar para iniciarmos nosso passeio pelo DCOMCNFG. O valor default
para um objeto servidor registrado Launching User (usurio que inicia). A Microsoft no poderia
ter tomado uma deciso pior para o default, se tentasse.
Quando o DCOM cria o servidor, ele usa o contexto de segurana do usurio especificado na
pgina Identity. O usurio que inicia gerar um novo processo do objeto servidor para todo e qualquer login de usurio distinto. Muitas pessoas vem o fato de que selecionaram o modo de instanciao ciMultiple e se perguntam por que vrias cpias de seu servidor esto sendo criadas. Por exemplo, se o usurio A se conecta ao servidor e depois o usurio B se conecta, o DCOM gerar um
processo inteiramente novo para o usurio B. Alm disso, voc no ver a parte GUI do servidor
para usurios que se conectam sob uma conta diferente da que est atualmente em uso na mquina
servidora. Isso acontece por causa de um conceito do NT conhecido como estaes Windows. A
nica estao Windows capaz de escrever na tela a Interactive User (usurio interativo), que o
150 usurio atualmente conectado na mquina servidora. Alm do mais, estaes Windows so um re-
curso escasso, e voc pode no conseguir executar muitos processos do servidor se usar essa configurao. Resumindo, nunca use a opo Launching User como sua identidade para o seu servidor.
A prxima opo interessante nessa pgina Interactive User, que significa que cada cliente
que cria um servidor far isso sob o contexto do usurio que est conectado ao servidor naquele momento. Isso tambm permitir que voc tenha interao visual com o seu servidor de aplicao.
Infelizmente, a maior parte dos administradores de sistemas no permite que um login aberto fique
ocioso em uma mquina NT. Alm disso, se o usurio conectado decidir sair, o servidor de aplicao no funcionar mais conforme desejado.
Para esta discusso, isso nos deixa com a ltima opo na pgina Identity: This User (este usurio). Usando essa opo, todos os clientes criaro um servidor de aplicao e usaro as credenciais
de login e o contexto do usurio especificado na pgina Identity. Isso tambm significa que a mquina NT no exige que um usurio esteja conectado para usar o servidor de aplicao. A nica desvantagem dessa tcnica que no haver uma tela GUI do servidor quando a opo utilizada. No entanto, de longe essa a melhor de todas as opes disponveis para colocar seu servidor de aplicao
em produo.
Depois que o objeto servidor for configurado corretamente com a identidade certa, voc precisar voltar sua ateno para a guia Security (segurana). Certifique-se de que o usurio que estar
usando esse objeto possui privilgios apropriados. No se esquea tambm de conceder o acesso de
usurio SYSTEM ao servidor; caso contrrio, voc encontrar erros durante o processo.
Muitas nuances sutis esto espalhadas pelo processo de configurao do DCOM. Para ver o
que h de mais recente sobre problemas de configurao do DCOM, especialmente com relao ao
Windows 9x, Delphi e DataSnap, visite a pgina DCOM em nosso site Web, em
http://www.DistribuCon.com/dcom95.htm
Arquivos a distribuir
Os requisitos para a distribuio de uma aplicao DataSnap mudaram a cada nova verso do Delphi. O Delphi 6 facilita a distribuio, mais do que qualquer outra verso.
Com o Delphi 6, os arquivos mnimos necessrios para distribuio da sua aplicao DataSnap
aparecem nas listas a seguir.
Aqui esto as etapas para o servidor (essas etapas consideram um servidor COM; elas diferiro
ligeiramente para outras variedades):
1. Copie o servidor de aplicao para um diretrio com privilgios suficientes no NTFS ou
privilgios em nvel de compartilhamento definidos corretamente, se estiver em uma mquina Win9x.
2. Instale sua camada de acesso a dados para permitir que o servidor de aplicao atue como
cliente para o RDBMS (por exemplo, BDE, MDAC, bibliotecas de banco de dados especficas no cliente e assim por diante).
3. Copie MIDAS.DLL para o diretrio %SYSTEM%. Por default, esse seria C:\Winnt\System32 para
mquinas NT e C:\Windows\System para mquinas 9x.
4. Execute o servidor de aplicao uma vez para registr-lo no COM.
Aqui esto as etapas para o cliente:
1. Copie o cliente para um diretrio, junto com quaisquer outros arquivos de dependncia
usados pelo seu cliente (por exemplo, pacotes de runtime, DLLs, controles ActiveX e assim
por diante).
2. Copie MIDAS.DLL para o diretrio %SYSTEM%. Observe que o Delphi 6 pode vincular estaticamente o MIDAS.DLL sua aplicao, tornando essa etapa desnecessria. Para fazer isso, basta
incluir a unidade MidasLib em sua clusula uses e montar sua aplicao novamente. Voc
ver um aumento no tamanho do arquivo EXE, devido ao vnculo esttico.
151
3. Opcional: se voc especificar a propriedade ServerName na sua TDispatchConnection ou se empregar a vinculao inicial no seu cliente, ter que registrar o arquivo da biblioteca de tipos do
servidor (TLB). Isso pode ser feito usando um utilitrio como <DELPHI>\BIN\TREGSVR.EXE (ou
programaticamente, se voc optar por isso).
Essa chamada diz ao DataSnap que 16 objetos estaro disponveis no pool, e que o DataSnap pode
liberar quaisquer instncias de objetos que tiverem sido criadas se no houver atividade por 30 minutos.
Se voc nunca quiser liberar os objetos, ento poder passar 0 como parmetro de tempo limite.
O cliente no muito diferente disso. Basta usar uma TWebConnection como TDispatchConnection para o cliente e preencher as propriedades de forma adequada, e o cliente estar se comunicando com o servidor de aplicao atravs do HTTP. A nica grande diferena quando se usa TWebConnection a necessidade de especificar o URL completo para o arquivo httpsrvr.dll, em vez de
simplesmente identificar o componente servidor por nome ou por endereo. A Figura 21.11 mostra
152 uma captura de tela de uma configurao tpica, usando TWebConnection.
Outro benefcio do uso do HTTP para o seu transporte que um sistema operacional como o
NT Enterprise permite o uso de clusters de servidores. Isso oferece balanceamento de carga e tolerncia a falhas automatizados para o seu servidor de aplicao. Para obter mais informaes sobre o
clustering, consulte http://www.microsoft.com/ntserver/ntserverenterprise/exec/overview/clustering.
As limitaes do uso de TWebConnection so bastante triviais, e compensam qualquer concesso
para que haja mais clientes capazes de atingir seu servidor de aplicao. As limitaes so que voc
precisa instalar wininet.dll no cliente e nenhuma callback est disponvel quando se usa TWebConnection.
Resumo
Este captulo fornece muitas informaes sobre DataSnap. Mesmo assim, ele apenas uma introduo ao que pode ser feito com essa tecnologia algo muito alm do escopo de um nico captulo.
Mesmo depois que voc explorar todos os detalhes internos do DataSnap, ainda poder aumentar
seu conhecimento e suas capacidades usando DataSnap com C++Builder e JBuilder. Usando JBuilder, voc pode atingir o mximo em acesso de mltiplas plataformas a um servidor de aplicao, enquanto usa a mesma tecnologia e conceitos aprendidos aqui.
O DataSnap uma tecnologia em rpida evoluo, que traz a promessa de aplicaes multicamadas a cada programador. Quando voc experimentar o verdadeiro poder da criao de uma aplicao com DataSnap, pode nunca retornar ao desenvolvimento de aplicaes de banco de dados
conforme o conhece atualmente.
153
Criando aplicaes
WebSnap
Por Nick Hodges
NESTE CAPTULO
l
Caractersticas do WebSnap
Tpicos avanados
CAPTULO
23
O Delphi 6 introduz uma nova estrutura de aplicao Windows, chamada WebSnap, que traz os
pontos fortes do Rapid Application Development (RAD) para o desenvolvimento na Web. Baseado
no WebBroker e no InternetExpress, WebSnap um grande salto adiante para os desenvolvedores
Delphi que desejam usar sua ferramenta favorita para criar aplicaes Web. Ele oferece todos os
apetrechos padro para as aplicaes Web, incluindo gerenciamento de sesso, login de usurio,
acompanhamento de preferncias do usurio e scripting. Naturalmente, o Delphi 6 leva o RAD
para o desenvolvimento de servidor Web, tornando fcil e rpida a criao de aplicaes Web robustas, dinmicas e baseadas em banco de dados.
Caractersticas do WebSnap
WebSnap no uma tecnologia totalmente nova, e no despreza suas aplicaes WebBroker e
InternetExpress. WebSnap compatvel com essas duas tecnologias mais antigas, e a integrao do
seu cdigo existente a uma nova aplicao WebSnap um processo relativamente simples. O WebSnap oferece vrias caractersticas, listadas nas prximas sees.
Scripting no servidor
O WebSnap integra de forma transparente o script no servidor s suas aplicaes, permitindo-lhe
criar facilmente objetos poderosos que aceitam script, os quais voc pode usar para criar e personalizar suas aplicaes e a HTML. O componente TAdapter e todos os seus descendentes so componentes que aceitam script, o que significa que podem ser chamados por seu script no servidor e produzir HTML e JavaScript no cliente para as suas aplicaes.
Componentes TAdapter
Os componentes TAdapter definem uma interface entre uma aplicao e o scripting no servidor. O
script no servidor s tem acesso sua aplicao por meio de adaptadores, garantindo que o script
no mude inadvertidamente os dados em uma aplicao ou exponha funes que no sejam voltadas para consumo pblico. Voc pode criar descendentes personalizados de TAdapter, que gerenciam o contedo para as suas necessidades especficas, e esse contedo pode at mesmo ser visvel e
configurvel durante o projeto. TAdapters podem manter dados e executar aes. Por exemplo, o
TDataSetAdapter pode exibir registros de um dataset, bem como tomar as aes normais em um dataset, como rolar, adicionar, atualizar e excluir.
Gerenciamento de sesso
As aplicaes WebSnap contm gerenciamento de sesso interno e automtico; agora voc pode
acompanhar as aes do usurio entre os diversos pedidos HTTP. Como o HTTP um protocolo
sem estado, suas aplicaes Web precisam registrar os usurios deixando algo no cliente que identifique cada usurio. Normalmente, isso feito com cookies, referncias de URL ou controles de
campo oculto. O WebSnap oferece suporte transparente para sesso, facilitando bastante o acompanhamento de usurios. O WebSnap faz isso por meio do seu componente SessionsService. O
componente SessionsService mantm, de forma transparente, um valor de identificao de sesso
para cada usurio, simplificando bastante a tarefa de registrar cada usurio enquanto realiza pedidos individuais. Isso normalmente um servio difcil de se controlar, mas o WebSnap cuida de todos os detalhes e disponibiliza a informao de sesso tanto para o script no servidor quanto para o
prprio cdigo da aplicao Web.
Servios de login
Suas aplicaes Web provavelmente tero que implementar alguma segurana, exigindo que os
usurios se registrem em determinada aplicao. O WebSnap automatiza esse processo, oferecendo
um componente adaptador de login especializado. Esse componente contm as funes necessrias
para consultar e autenticar os usurios corretamente, de acordo com o modelo de segurana escolhido pela aplicao. Ele rene informaes de login e, em conjunto com o gerenciamento de sesso
do WebSnap, oferece as credenciais atuais do login para cada pedido. Os componentes de login
tambm automatizam a validao do login e a expirao do login. Por toda a sua aplicao, os usurios que tentarem acessar pginas no-autorizadas podem ser levados automaticamente para a pgina de login.
Acompanhamento do usurio
A funo mais comum que o rastreamento de sesso oferece a capacidade de acompanhar seus
usurios e suas preferncias para a sua aplicao. O WebSnap oferece componentes que permitem
acompanhar informaes do usurio com facilidade e exibi-las no seu site. Voc pode armazenar as
informaes de login do usurio e depois apanhar as informaes do usurio com base nisso. Voc
pode manter direitos de acesso do usurio e preferncias do site, alm de outras coisas, como informaes do carrinho de compras.
Gerenciamento de HTML
Normalmente, em uma aplicao Web dinmica, o acompanhamento e o gerenciamento da HTML
pode ser difcil. O contedo HTML pode residir em diversos lugares, como em arquivos e recursos,
ou ento pode ser gerado dinamicamente. O WebSnap oferece um meio para que voc gerencie esse
processo com seus servios de localizao de arquivo.
156
Projetando a aplicao
Primeiro, voc desejar incluir uma barra de ferramentas WebSnap no IDE; portanto, d um clique
com o boto direito na rea de botes da barra de ttulo do IDE e selecione a barra de ferramentas
Internet (veja a Figura 23.1). Isso acrescenta uma barra de ferramentas janela principal do IDE, facilitando a criao de aplicaes WebSnap e a incluso de formulrios e mdulos Web.
Em seguida, d um clique no boto com a mo segurando o globo, e voc ver a caixa de dilogo que aparece na Figura 23.2.
A caixa de dilogo da Figura 23.2 oferece uma srie de opes para configurar sua aplicao
WebSnap. A primeira o tipo de servidor em que sua aplicao ser executada. Voc tem cinco
opes:
l
ISAPI/NSAPI Dynamic Link Library Essa opo produz um projeto que roda sob IIS (ou
sob servidores Netscape, se o adaptador ISAPI correto for instalado). O projeto produz
uma DLL quando compilado e roda no mesmo espao de memria do servidor Web. O servidor Web mais comum para rodar aplicaes ISAPI o Internet Information Server da Microsoft, embora outros servidores Web possam rodar DLLs ISAPI.
CGI Standalone executable Essa opo cria um projeto que produz um executvel pelo
console, que l e escreve nas portas de entrada e sada padro. Ela se ajusta especificao
CGI. Quase todos os servidores Web aceitam CGI.
157
Win-CGI Standalone executable Essa opo produz um projeto Win-CGI que se comunica com um servidor Web por meio de arquivos INI baseados em texto. Win-CGI muito
rara e no recomendada.
Apache Shared Module (DLL) Essa opo produz um projeto que rodar no servidor
Web Apache. Para obter mais informaes sobre o Apache, consulte http://www.apache.org.
Web App Debugger Executable Se voc selecionar essa opo, obter uma aplicao que
ser executada no Web App Debugger do Delphi (veja a Figura 23.3). Sua aplicao Web ser
um servidor COM out-of-process, e o Web App Debugger controlar a execuo da aplicao. Esse tipo de aplicao Web permitir usar o poder completo do depurador do Delphi
para depur-lo. Isso significa que no h mais disputa com os servidores Web, ligando-os e
desligando-os para carregar e descarregar suas aplicaes. Em vez disso, a depurao de sua
aplicao ser rpida e fcil.
NOTA
O Web App Debugger pode ser acessado por meio do menu Tools no IDE. Para que funcione corretamente, voc precisa registrar a aplicao encontrada no diretrio <Delphi Dir>\bin, chamada
serverinfo.exe. Tudo o que voc precisa fazer para registr-la execut-la uma vez, e ela mesmo
se registrar. O Web App Debugger uma aplicao baseada em COM, que atua como um servidor Web para testar suas aplicaes. Quando voc cria uma aplicao para o Web App Debugger,
seu novo projeto ter um formulrio e um mdulo Web. O formulrio atua como um marcador de
lugar para o servidor COM, e a execuo da aplicao uma vez a registrar. Depois disso, o Web
App Debugger a controlar por meio do browser Web, e servir sua aplicao no browser. Como
a aplicao um executvel do Delphi, e no uma extenso do servidor Web, voc pode definir um
ponto de interrupo nela e execut-la no IDE do Delphi. Depois, quando voc a acessar por meio
do browser, o depurador do Delphi assumir o comando quando seus pontos de interrupo forem
alcanados, e voc poder depurar a aplicao normalmente. Para acessar sua aplicao por meio
do browser, execute o Web App Debugger e d um clique no hyperlink intitulado Default URL. Isso
trar uma aplicao Web que lista todas as aplicaes registradas com o servidor. Voc pode, ento, selecionar sua aplicao e execut-la. A opo View Details (exibir detalhes) permitir ver mais
informaes sobre as diferentes aplicaes, removendo-as do Registro quando no forem mais necessrias. No entanto, tenha cuidado para no excluir a aplicao ServerInfo; caso contrrio,
voc ter que retornar e registr-la novamente.
158
Para a aplicao de exemplo que voc criar aqui, selecione a opo Web App Debugger. Isso
permitir depurar a aplicao enquanto voc a cria.
A prxima opo no assistente permite selecionar o tipo de mdulo que voc deseja e os diferentes componentes que sero includos. Se voc escolher a opo Page Module (mdulo de pgina), receber um mdulo Web que representa uma pgina na sua aplicao. Se voc escolher a opo Data Module, receber um mdulo de dados que pode ser usado em uma aplicao WebSnap.
Ele pode realizar a mesma funo dos mdulos de dados nas aplicaes tradicionais cliente/servidor. Para essa aplicao, selecione a opo Page Module.
Em seguida, d um clique no boto Components, e voc ver a caixa de dilogo mostrada na
Figura 23.4.
Figura 23.4 A caixa de dilogo Web App Components permite que voc selecione os
componentes que sero includos em seu novo mdulo.
Sessions Service Esse componente gerencia sesses para os usurios, permitindo-lhe manter o estado para usurios individuais entre os pedidos HTTP. Sessions Service pode armazenar informaes sobre usurios e expirar automaticamente suas sesses depois de um
certo perodo de inatividade. Voc pode acrescentar qualquer informao especfica da
sesso que desejar na propriedade Session.Values, um array de variantes indexado por
string. Por default, as sesses so gerenciadas com cookies na mquina do usurio, embora
voc pudesse criar uma classe para lidar com elas de alguma outra maneira, como com
URLs gordos ou campos ocultos.
User List Service Esse componente mantm uma lista de usurios que esto autorizados a
se registrar na aplicao e informaes sobre eles.
NOTA
Cada uma dessas opes possui caixas suspensas que permitem escolher o componente que atender a cada um dos papis indicados. Voc pode criar seus prprios componentes, que atendero a
esses papis e os registraro com o WebSnap. Com isso, eles aparecero como opes nessa caixa
de dilogo. Por exemplo, voc poderia criar um componente de sesso que mantenha informaes
de sesso em um URL gordo, em vez de usar cookies.
Para este exemplo, marque todas as caixas de seleo. Depois, para o componente End User
Adapter, desa a caixa de combinao e selecione TEndUserSessionAdapter. Esse componente associar automaticamente um ID de sesso a um usurio final. Depois, d um clique em OK.
A prxima opo no assistente o nome da pgina. Chame essa pgina principal de Home e depois d um clique em Page Options (opes de pgina). Voc ver a caixa de dilogo que aparece na
Figura 23.5.
Figura 23.5 A caixa de dilogo Application Module Page Options permite selecionar
as opes para a pgina no seu mdulo Web.
160
Essa caixa de dilogo permite personalizar o componente PageProducer do seu mdulo Web e
a HTML associada a ele. Ela apresenta diversas opes. A primeira o tipo de produtor de pgina.
O WebSnap inclui uma srie de PageProducers padro, que podem produzir e gerenciar HTML de
vrias maneiras. Para comear, selecione a opo default, um PageProducer simples. Voc tambm
pode selecionar o tipo de scripting no servidor que deseja utilizar. O Delphi j vem com suporte
para JScript e VBScript (alm de XML/XSL, que sero discutidos mais adiante.) Aqui, deixe o valor
default JScript.
Cada mdulo possui uma pgina HTML associada a ele. A prxima opo permite selecionar
que tipo de HTML voc deseja. Por default, o Delphi oferece uma pgina Standard com um menu
de navegao simples, ativado por script. Voc pode criar seus prprios modelos HTML, registr-los com o WebSnap e depois selecion-los aqui. Veremos como fazer isso mais adiante, neste captulo. Por enquanto, mantenha o valor default, Standard.
Chame a pgina de Home (o Title preenchido automaticamente com o mesmo nome). Certifique-se de que Published (publicado) esteja marcado e mantenha Login Required (exige login) desmarcado. Uma pgina publicada aparecer na lisa de pginas da aplicao e poder ser referenciada
pelo objeto de scripting Pages. Isso necessrio para a criao de menus baseados em pgina no
script, usando o objeto de scripting Pages.
Depois que voc tiver feito isso, d um clique em OK e depois novamente em OK no assistente
principal. O assistente, em seguida, criar sua aplicao para voc, e o novo mdulo Web ficar semelhante ao que aparece na Figura 23.6.
Ainda no discutimos sobre o controle TWebAppComponents. Esse controle a carteira de compensao central para todos os outros componentes. Como muitos dos componentes em uma aplicao
WebSnap trabalham em conjunto, o componente WebAppComponents aquele que os une e permite que
se comuniquem e faam referncia um ao outro. Suas propriedades consistem simplesmente em outros componentes que atendem aos papis especficos discutidos anteriormente.
Nesse ponto, voc dever salvar o projeto. Para manter coerncia com o restante do captulo,
chame o mdulo Web (unit2) de wmHome, chame o formulrio (Unit1) de ServerForm e chame o
projeto em si de DDG6Demo.
Examinando o Code Editor, voc dever ver alguns recursos novos e desconhecidos. Primeiro, observe as guias na parte inferior. Cada mdulo Web por representar uma pgina em uma aplicao Web possui um arquivo HTML associado, que pode conter script no servidor. A segunda
guia na parte inferior mostra essa pgina (veja a Figura 23.7). Como voc selecionou o modelo de
pgina HTML Standard no assistente, a HTML possui script no servidor, que saudar o usurio se
ele for conectado, e oferecer um menu de navegao bsico montado automaticamente com base
em todas as pginas publicadas da aplicao. medida que as pginas so acrescentadas nessa aplicao de demonstrao, esse menu crescer e permitir que os usurios naveguem para cada uma
das pginas. O cdigo HTML default aparece na Listagem 23.1.
161
c++
}
if (c>1) Response.Write(s)
%>
</td>
</table>
</body>
</html>
Isso faz com o que o objeto de scripting Application apresente o valor para ApplicationTitle
na HTML.
Em seguida, v para o Code Editor e selecione a pgina HTML para o Home Module. Depois
mova o cursor para baixo da tag </table>, prximo do final, e inclua uma descrio resumida da pgina, saudando o usurio. O cdigo neste CD-ROM possui tal entrada, acrescentando o seguinte:
<P>
<FONT SIZE="+1" COLOR="Red">Welcome to the Delphi 6 Developers Guide WebSnap
Demonstration Application!</FONT>
<P>
This application will demonstrate many of the new features in Delphi 6 and
WebSnap. Feel free to browse around and look at the code involved. There is
a lot of power, and thus a lot to learn, in WebSnap, so take your time and
don't try to absorb it all at once.
<P>
Esse novo cdigo, naturalmente, tambm aparece imediatamente no painel HTML Preview.
Em seguida, s para provar que voc est realmente criando uma aplicao para o browser,
execute o projeto. A primeira coisa que voc ver um formulrio em branco. Esse o servidor
COM. Voc pode fech-lo e depois iniciar o Web App Debugger no menu Tools. Depois de fazer 163
isso, d um clique no hyperlink Default URL (ele ser chamado DDG6DemoApp.DDG6TestApp), localize a
aplicao na caixa de listagem no seu browser e d um clique no boto Go. Seu browser dever mostrar sua pgina conforme ilustrada na Figura 23.8.
Em seguida, inclua alguma mensagem simples na pgina HTML, no mesmo ponto abaixo da
tabela, na pgina padro. Depois, compile e execute a aplicao por meio do Web App Debugger,
como fizemos anteriormente. Se a pgina estava l desde a ltima vez em que foi examinada, tudo o
que voc precisa fazer dar um clique no boto Refresh (atualizar) no seu browser.
Dessa vez, quando voc executar a aplicao, dever notar que o menu de navegao agora
aparece. Esse menu resultado do seguinte script no servidor:
<%
%>
e = new Enumerator(Pages)
s = ''
c = 0
for (; !e.atEnd( ); e.moveNext( ))
{
if (e.item( ).Published)
{
if (c>0) s += ' | '
if (Page.Name != e.item( ).Name)
s += '<a href="' + e.item( ).HREF + '">' + e.item( ).Title + '</a>'
else
s += e.item( ).Title
c++
}
}
if (c>1) Response.Write(s)
Esse cdigo simplesmente percorre o objeto de scripting Pages, montando um menu de nomes
de pgina. O cdigo cria um link se a pgina encontrada no for a pgina atual. Assim, a pgina atual
no um link, e todos os outros nomes de pgina so, no importa qual seja a pgina atual. Esse
um menu bastante simples e, naturalmente, voc poderia escrever seus prprios menus mais sofisticados para a sua aplicao personalizada.
NOTA
Se voc alternar entre as duas pginas, poder observar que o formulrio da aplicao pisca em segundo plano toda vez que um pedido for feito. Isso porque o Web App Debugger est chamando a
aplicao como um objeto COM para cada pedido, executando a aplicao, apanhando a resposta HTTP e fechando a aplicao.
Em seguida, voc pode tornar parte da aplicao restrita apenas a usurios que efetuaram o login. Primeiro, inclua uma pgina que exija o login de um usurio para que ele possa v-la. D um clique no boto New WebSnap Page (nova pgina WebSnap) na barra de ferramentas Internet e chame a
pgina de LoggedIn. Depois, selecione a caixa de seleo Login Required. D um clique em OK
para criar uma pgina Web que s possa ser vista por um usurio que efetuou login. Salve a pgina
como wmLoggedIn.pas. Depois, inclua algum cdigo HTML na pgina, permitindo que o usurio saiba
que somente os usurios com login podero ver a pgina. A aplicao neste CD-ROM inclui o seguinte:
<P>
<FONT COLOR= Green ><B>Congratulations!</B></FONT>
<BR>
You are successfully logged in!Only logged in users are granted access
.to this page.All others are sent back to the Login page.
<P>
<P>
<FONT COLOR="Green"><B>Congratulations! </B></FONT>
165
<BR>
You are successfully logged in! Only logged in users are granted access
to this page. All others are sent back to the Login page.
<P>
A nica diferena entre a pgina LoggedIn e a pgina Simple um parmetro no cdigo que registra a pgina com o gerenciador de aplicao. Toda pgina WebSnap possui uma seo de inicializao que se parece com isto:
initialization
if WebRequestHandler < > nil then
WebRequestHandler.AddWebModuleFactory(TWebPageModuleFactory.Create(TLoggedIn,
TWebPageInfo.Create([wpPublished, wpLoginRequired], '.html'),
crOnDemand, caCache));
Esse cdigo registra a pgina com o objeto WebRequestHandler, que gerencia todas as pginas e
oferece seu contedo HTML quando for preciso. WebRequestHandler sabe como criar, colocar em
cache e destruir instncias de mdulos Web quando for preciso. O cdigo anterior o cdigo para a
pgina LoggedIn, e possui o parmetro wpLoginRequired, dizendo pgina que somente os usurios
que efetuaram login podem ter acesso a ela. Por default, o New Page Wizard acrescenta esse valor,
mas o transforma em comentrio. Se voc quiser que a pgina fique protegida por senha mais tarde,
poder simplesmente retirar o smbolo de comentrio do parmetro e recompilar a aplicao.
Login
Voc precisa criar uma pgina que permita o login do usurio. Primeiro, no entanto, existe uma tarefa de manuteno a ser feita na pgina Home.
Primeiro, crie uma nova pgina e d-lhe o nome Login. Depois, selecione TAdapterPageProducer para o tipo de produtor de pgina. Dessa vez, no entanto, no a publique, removendo a seleo da caixa Publish, e, obviamente, no exija que um usurio efetue login para ver a pgina de login! Desmarcando a opo Publish, a pgina estar disponvel para uso, mas no far parte do
objeto de scripting Pages, e por isso no aparecer no menu de navegao. Salve-a como wmLogin. Dessa vez, v para a pgina WebSnap da Component Palette e inclua um componente TLoginAdapter no mdulo.
O TAdapterPageProducer um PageProducer especializado, que sabe como exibir e tratar dos
campos e controles HTML apropriados para um TAdapter. No caso da aplicao de demonstrao,
esse TAdapterPageProducer ir exibir as caixas de edio Username (nome de usurio) e Password
(senha), que o usurio ter que usar para efetuar o login. Quando voc comear a entender melhor o
WebSnap, rapidamente desejar usar TAdapterPageProducers em todas as suas pginas, pois eles facilitam bastante a exibio de informaes de TAdapter, a execuo de aes de TAdapter e a criao de
formulrios HTML com base em campos TAdapter.
Como o TLoginFormAdapter possui todos os campos necessrios para isso, a criao da pgina
de login ser muito fcil, e feita sem qualquer cdigo isso mesmo, sem cdigo! Voc poder
acrescentar usurios, criar uma pgina de login e impor o login sobre pginas especificadas, tudo
sem uma nica linha de cdigo.
Primeiro, para gerenciar os logins, voc ter que criar alguns usurios. V para o mdulo Web
Home e d um clique duplo no componente WebUserList. Esse componente gerencia usurios e senhas. Voc pode facilmente acrescentar usurios e suas senhas. D um clique no boto New e inclua
dois usurios diferentes. Acrescente quaisquer senhas que voc quiser para cada usurio. Os dois
usurios na aplicao de demonstrao no CD-ROM so ddg6 e user. Suas senhas so iguais aos
seus nomes de usurio, como mostra a Figura 23.9.
166
E isso tudo o que voc precisa fazer. Execute a aplicao e deixe-a em execuo. Agora, como
voc est se baseando nas informaes de sesso sobre o usurio, precisa deixar a aplicao na memria para que ela se lembre que voc efetuou o login. Execute a aplicao no browser e depois tente navegar at a pgina que exige o login. Ela dever lev-lo para a pgina Login. Digite um nome de
usurio e senha vlidos, d um clique no boto Login e voc ser levado pgina que pedir seu login. A partir da, qualquer pgina que voc especifique como exigindo um login vlido s aparecer
se voc tiver efetuado o login corretamente. Caso contrrio, ela o levar para a pgina de login.
Tudo isso acontece sem a escrita de uma nica linha de cdigo em Pascal.
Experimente isto tambm: efetue o logout, selecionando o link Logout, e depois tente efetuar
o login com um nome de usurio ou senha invlida. Observe que aparecer uma mensagem de erro.
Isso o componente AdapterErrorList em ao. Ele coletar automaticamente os erros de login e os
mostrar para voc.
167
Quando voc estiver autenticado na aplicao e navegando por suas pginas, notar que ela se
lembra de quem voc e apresenta seu nome de login no cabealho de cada pgina. Isso resultado
do seguinte script no servidor, no arquivo HTML para os mdulos Web:
<% if (EndUser.Logout != null) { %>
<% if (EndUser.DisplayName != '') { %>
<h1>Welcome <%=EndUser.DisplayName %></h1>
<% } %>
168
Esse cdigo apanha os valores dos campos de entrada na sua HTML quando o usurio aciona o
boto Submit e coloca os valores na varivel de sesso, para serem recuperados pelos AdapterFields.
Naturalmente, voc precisa ser capaz de apanhar esses valores depois que estiverem definidos,
de modo que cada campo do adaptador apanhar seu valor de volta do objeto SessionsService. Para
cada campo no adaptador, torne os tratadores de evento OnGetValue semelhantes ao cdigo da Listagem 23.3.
Listagem 23.3 Tratadores de evento OnGetValue
...
const
sFavoriteMovie = 'FavoriteMovie';
sPasswordHint = 'PasswordHint';
sLikesChocolate = 'LikesChocolate';
sIniFileName = 'DDG6Demo.ini';
...
procedure TPreferenceInput.LikesChocolateFieldGetValue(Sender: TObject;
var Value: Boolean);
var
S: string;
begin
S := Session.Values[sLikesChocolate];
Value := S = 'true';
169
Em seguida, voc precisa ser capaz de exibir controles que realmente apanham os dados do
usurio. Voc far isso por meio de TAdapterPageProducer, assim como fez com a pgina Login. Primeiro, d um clique duplo em TAdapterPageProducer; voc voltar ao Web Surface Designer mais
uma vez. Crie um novo AdapterForm e inclua um AdapterFieldGroup, alm de um AdapterCommandGroup. Defina a propriedade Adapter do AdapterFieldGroup como PrefAdaper e defina o DisplayComponent do AdapterCommandGroup como AdapterFieldGroup. Depois, d um clique com o boto direito
no AdapterFieldGroup e selecione Add All Fields (adicionar todos os campos) no menu. Para cada
um dos campos resultantes, use o Object Inspector para definir a propriedade FieldName com os valores apropriados. Voc tambm pode mudar as propriedades Caption para valores mais amigveis
do que o default. Depois selecione o AdapterCommandGroup, d um clique nele com o boto direito e
selecione Add All Commands (adicionar todos os comandos) no menu. Defina a propriedade ActionName do AdapterActionButton resultante como SubmitAction. Finalmente, defina a propriedade
AdapterActionButton.PageName como PreferencesPage. (Essa a pgina para onde a ao ir quando
terminar com o processamento da ao. Voc criar essa pgina em um minuto.)
Se algo no for conectado corretamente no Web Surface Designer, voc ver uma mensagem
de erro na guia Browser. A mensagem o instruir sobre as propriedades que precisam ser definidas
para que tudo seja conectado corretamente e para que a HTML seja exibida de modo adequado.
Depois que tudo isso for feito e a HTML estiver correta, a pgina estar pronta. Agora, se voc
executar a aplicao, ver uma pgina adicional no menu. Se voc efetuar o login, poder ver os
controles de entrada para inserir seus dados de preferncia. No d um clique no boto Submit,
simplesmente porque ainda no h um lugar para ir.
Em seguida, crie uma pgina para exibir as preferncias do usurio usando a barra de ferramentas e depois chame-a de PreferencesPage. Publique a pgina e exija que os usurios efetuem o
login para exibi-la. (Novamente, o assistente far tudo isso por voc, como antes.) Salve a nova unidade como wmPreferences.
Depois, v para a HTML da pgina e, na rea logo abaixo da tabela que mantm o menu de navegao, inclua o seguinte script:
<P>
<B>Favorite Movie: <%= Modules.PreferenceInput.PrefAdapter.FavoriteMovieField.
Value %>
<BR>
Password Hint: <%= Modules.PreferenceInput.PrefAdapter.PasswordHintField.
Value %>
<BR>
<% s = ''
if (Modules.PreferenceInput.PrefAdapter.LikesChocolateField.Value)
s = 'You like Chocolate'
else
170
%>
Agora, quando voc compilar e executar a aplicao, poder digitar suas preferncias e dar um
clique no boto Submit a aplicao se lembrar e mostrar suas preferncias na pgina Preferences. Voc tambm pode acessar esses valores no script para qualquer outra pgina, uma vez definidos. Os valores so mantidos entre os pedidos HTTP pelo objeto Session e apanhados do componente Adapter por meio do script.
NOTA
Cada uma das pginas em uma aplicao WebSnap possui um arquivo HTML associado, como
voc viu. Como esses arquivos existem fora da aplicao, voc pode edit-los, salvar as alteraes,
atualizar a pgina no seu browser e ver os resultados sem recompilar sua aplicao. Isso significa
que voc mesmo pode atualizar a pgina sem ter que parar seu servidor Web. Voc tambm pode
experimentar facilmente seu script no servidor durante o desenvolvimento, sem ter que recompilar
sua aplicao. Mais adiante, neste captulo, voc ver meios alternativos de armazenar e apanhar sua
HTML.
171
Esses tratadores de evento armazenam os dados em um arquivo INI, mas no h motivo para
que voc no armazene os dados em um banco de dados ou em qualquer outro mtodo de armazenamento persistente.
A varivel Lock uma varivel global do tipo TMultiReadExclusiveWriteSynchronizer, e criada
na seo de inicializao da pgina Home. Como vrias sesses poderiam estar lendo e gravando no
arquivo INI, esse componente torna a leitura e escrita no arquivo INI prova de threads. Inclua a
declarao a seguir na parte de interface da sua unidade wmHome:
var
Lock: TMultiReadExclusiveWriteSynchronizer;
Esse cdigo tambm utiliza uma funo chamada IniFileName, que declarada da seguinte forma:
const
...
172
sIniFileName ='DDG6Demo.ini';
Inclua isso em sua unidade wmHome e voc dever ter uma aplicao Web totalmente funcionando, que efetua login de usurios e mantm suas preferncias, at mesmo entre diferentes sesses.
Tratamento de imagens
Praticamente toda aplicao Web apresenta grficos. Os grficos podem melhorar a atratividade e a
funcionalidade da sua aplicao. Naturalmente, o WebSnap torna a incluso de imagens e grficos
nas suas aplicaes to fcil quanto, bem, tudo o mais o que o WebSnap faz. Como voc poderia esperar, o WebSnap permitir usar grficos e imagens de qualquer origem que voc preferir arquivos, recursos, streams de banco de dados e assim por diante. Se os dados da sua imagem puderem ser
colocados em um stream, eles podem ser usados em uma aplicao WebSnap.
Use a barra de ferramentas Internet para incluir outra pgina na sua aplicao. Use um TAdapterPageProducer, publique a pgina e exija que os usurios efetuem login para obter acesso a ele. Em
seguida, chame a pgina de Images e salve a unidade resultante como wmImages. Depois que isso for
feito, v para o mdulo Web Imagens, inclua um TAdapter no mdulo e d-lhe o nome ImageAdapter. Finalmente, d um clique duplo em ImageAdapter e inclua dois campos do tipo TAdapterImageField. Cada um desses mostrar um modo diferente de exibir imagens.
Primeiro, voc pode exibir uma imagem com base em um URL. Destaque o primeiro AdaperImageField e defina a propriedade HREF como um URL totalmente qualificado, que aponta para uma
imagem no seu sistema ou em qualquer lugar na Internet, pelo mesmo motivo. Por exemplo, se voc
quiser ver o histrico de um ano do preo das aes da Borland, defina a propriedade HREF como
http://chart.yahoo.com/c/1y/b/borl.gif.
D um clique duplo no TAdapterPageProducer, no mdulo Web Images, inclua um AdapterForm
e depois inclua um AdapterFieldGroup neste. Defina a propriedade adaptadora desse novo AdapterFieldGroup para o ImageAdapter. Depois, d um clique com o boto direito novamente no AdapterFieldGroup e selecione Add All Fields. Em seguida, defina o campo ReadOnly do AdapterImageField como True. Se essa propriedade for True, ela mostrar a imagem na sua pgina. Se for definida
como False, ela dar uma caixa de edio e um boto para pesquisar um nome de arquivo. Obviamente, para ver as imagens, voc definiria essa propriedade como True. Quando voc olhar para a
imagem pela primeira vez, notar que a imagem possui uma pequena e incmoda legenda. Normalmente, voc no desejar isso, de modo que, para livrar-se dela, defina a propriedade Caption como
um nico espao. (Observe que uma legenda em branco no aceita.) Voc dever ento ver o grfico aparecendo no Web Surface Designer, como mostra a Figura 23.12.
173
NOTA
Se voc quiser que as imagens sejam referenciadas por links relativos, para aparecerem durante o
projeto, ter que incluir o diretrio onde elas residem em Search Path (caminho de pesquisa) na pgina Options do Web App Debugger.
Agora voc pode exibir imagens com base em um URL. Outras vezes, no entanto, voc pode
querer apanhar uma imagem de um stream. O componente AdapterImageField tambm oferece suporte para isso. Selecione o segundo AdapterImageField no seu ImageAdapter e abra o Object Inspector. V para a pgina de eventos e d um clique duplo em OnGetImage. Coloque uma imagem JPG no
mesmo diretrio da aplicao (a demonstrao neste CD-ROM usa athena.jpg) e faa com que seu
tratador de evento fique semelhante a este:
procedure TImages.AdapterImageField2GetImage(Sender: TObject;
Params: TStrings; var MimeType: String; var Image: TStream;
var Owned: Boolean);
begin
MimeType := 'image\jpg';
Image := TFileStream.Create('athena.jpg', fmOpenRead);
end;
Esse cdigo bastante simples Image uma varivel de stream que voc cria e preenche com
uma imagem. Naturalmente, a aplicao precisa saber que tipo de imagem est apanhando, de
modo que voc pode retornar essa informao no parmetro MimeType. Um TFileStream uma soluo simples, mas poderia apanhar a imagem de qualquer origem, como um BlobStream de um banco
de dados, ou criar a imagem em plena execuo e retorn-la em um stream da memria. Agora,
quando voc executar a aplicao, dever ver o JPG que escolheu logo abaixo do grfico de aes.
Exibindo dados
Logicamente, voc deseja que sua aplicao faa mais do que as coisas simples que ela faz at aqui.
Voc certamente deseja ser capaz de exibir dados a partir de um banco de dados, tanto em formato
tabular quanto registro por registro. Naturalmente, o WebSnap facilita isso, e voc pode criar aplicaes de banco de dados poderosas com somente uma pequena quantidade de cdigo. Usando o
TDatasetAdapter e seus campos e aes internas, voc pode facilmente exibir dados, alm de fazer
acrscimos, atualizaes e excluses em qualquer banco de dados.
Na realidade, exibir um dataset em um formulrio muito fcil. Inclua uma nova unidade na
sua aplicao de demonstrao mas, dessa vez, torne-a um WebDataModule, usando o terceiro
boto na barra de ferramentas Internet. Esse assistente simples; portanto, simplesmente aceite as
opes default. Depois inclua um TDatasetAdapter a partir da guia WebSnap na Component Palette
e uma TTable a partir da guia BDE. Aponte a TTable para o banco de dados DBDemos e depois para a
tabela BioLife. Depois defina a propriedade Dataset de DatasetAdapter1 como Table1. Finalmente,
defina Table1.Active como True para abrir a tabela. Chame o WebDataModule de BioLife data e
salve a unidade como wdmBioLife.
NOTA
Sua aplicao est usando uma tabela do Paradox simples, baseada no BDE, mas o componente
TDatasetAdapter exibir dados a partir de qualquer descendente de TDataset. Observe, tambm,
que no realmente uma boa idia usar uma TTable em uma aplicao Web sem suporte explcito
para sesso. A aplicao de demonstrao faz isso simplesmente para facilitar o uso e manter sua
ateno nos recursos do WebSnap, e no nos dados.
174
Depois, como uma mudana de rumo, use a Object Treeview para definir as propriedades dos
componentes. Se a Object Treeview no estiver visvel, selecione View, Object Treeview no menu
principal. Selecione o DatasetAdapter, d um clique com o boto direito no n Actions e selecione
Add All Actions (adicionar todas as aes). Depois, conecte a TTable ao TAdapterDataset por meio
de sua propriedade Dataset. Selecione o n Fields e d um clique com o boto direito, selecionando
Add All Fields. Faa o mesmo para a TTable, incluindo todos os campos do dataset ao WebDataModule. Depois, como o WebSnap cria servidores sem estado para operaes de banco de dados, voc
precisa indicar uma chave primria para o dataset, para permitir a navegao solicitada pelo cliente
e a manipulao de dados. O WebSnap far tudo isso por voc automaticamente, depois que voc
especificar a chave primria. Faa isso selecionando o campo Species_No Field na Object Treeview
e incluindo o valor pfInKey em sua propriedade ProviderFlags.
Em seguida, inclua uma pgina normal na aplicao. Torne essa pgina Login Required,
d-lhe um TAdapterPageProducer e chame a pgina de Biolife. Salve a unidade como wmBioLife.
Como voc deseja exibir os dados nessa nova pgina, inclua o nome de unidade wdmBioLife clusula uses da sua unidade wmBioLife. Depois, d o foco ao mdulo Web Biolife e d um clique com o boto direito no componente AdapterPageProducer. D um clique com o boto direito no n WebPageItems, logo abaixo dele, selecione New Component (novo componente) e selecione um
AdapterForm. Selecione o AdapterForm, d um clique com o boto direito nele e inclua uma AdapterErrorList. Depois inclua uma AdapterGrid. Defina a propriedade Adapter dos dois componentes
como DatasetAdapter. D um clique com o boto direito na AdapterGrid e selecione Add All Columns (adicionar todas as colunas). Depois, selecione o n Actions sob o DatasetAdapter, d um clique com o boto direito nele e selecione Add All Actions. Em seguida, selecione o n Fields, d um
clique com o boto direito e inclua todos os campos tambm. Voc agora dever ter todas as propriedades definidas corretamente para exibir dados.
V at o mdulo Web BioLife e d um clique duplo no AdaperPageProducer. Voc ver o Web
Surface Designer com dados vivos. Se no, verifique se voc abriu a tabela e conectou todas as propriedades Adapter para os componentes dentro do DatasetAdapter. O campo Notes torna a tabela
muito longa. Portanto, selecione a AdapterGrid no canto superior esquerdo e o componente ColNotes no painel superior direito; depois, apague-o. Agora voc dever ter algo semelhante ao que
mostramos na Figura 23.13.
Os grficos no aparecem durante o projeto, mas sim em runtime. Na realidade, voc agora
pode compilar e executar a aplicao, e pode ver todos os dados na pgina BioLife tudo sem escrever uma nica linha de cdigo.
175
Naturalmente, olhar apenas para os dados no muito til. Provavelmente voc desejar manipular os registros individuais. Certamente, isso fcil de se fazer no WebSnap. V para o Web
Surface Designer e selecione a AdapterGrid. D um clique com o boto direito nela e inclua uma
AdapterCommandColumn. Depois, d um clique com o boto direito nisso e selecione os comandos DeleteRow, EditRow, BrowseRow e NewRow, como mostra a Figura 23.14.
Figura 23.14 Use a caixa de dilogo Add Commands para selecionar as aes que voc deseja
fazer sobre as linhas individuais do dataset.
D um clique em OK. Depois, empilhe verticalmente os botes, definindo a propriedade DisplayColumns do componente AdapterCommandColumn como 1. Depois de fazer isso, voc dever ver
176
Agora, para usar essa pgina a fim de editar um nico registro, volte unidade wmBioLife onde
a grade se encontra e use a Object TreeView e a tecla Shift para selecionar todos os quatro botes na
AdapterCommandColumn. Defina sua propriedade de pgina como EditBioLife o nome da pgina que
exibir um nico registro. Agora, quando voc der um clique no boto na grade, a pgina EditBioLife ser exibida. Se voc pedir para navegar pelo registro, os dados sero exibidos como texto simples. Mas, se voc pedir para editar o registro, eles aparecero em caixas de edio. O campo grfico
at mesmo permitir que voc inclua um novo grfico no banco de dados, procurando um novo arquivo. Voc pode navegar pelo dataset usando os botes de comando. Novamente, tudo isso foi
conseguido sem a escrita de uma nica linha de cdigo ou script, tanto faz.
NOTA
Voc pode querer mexer um pouco na apresentao do campo Notes. Por default, o controle TextArea, usado quando a pgina est no modo de edio, muito pequeno e no quebra o texto. Voc
pode selecionar o componente FldNotes e ajustar as propriedades TextAreaWrap, DisplayRows e
DisplayWidth para obter melhores resultados.
Tpicos avanados
At aqui, voc viu o que pode ser considerado o bsico. Voc criou uma aplicao WebSnap que gerencia usurios e informaes de sesso sobre esses usurios, e que tambm gerencia e manipula dados. Entretanto, o WebSnap faz muito mais do que isso, dando-lhe mais controle sobre o que a aplicao pode fazer. A prxima seo aborda alguns dos tpicos avanados que permitiro ajustar
mais de perto suas aplicaes WebSnap.
LocateFileServices
O desenvolvimento de aplicaes WebSnap para Windows normalmente exige a coordenao de
diferentes recursos. HTML, script no servidor, cdigo Delphi, acesso a banco de dados e grficos
precisam ser unidos corretamente a uma nica aplicao. Normalmente, muitos desses recursos encontram-se embutidos e so gerenciados por meio de um arquivo HTML. O WebSnap oferece suporte para separar a HTML da implementao de uma pgina Web dinmica, significando que voc
pode editar os arquivos HTML separadamente do arquivo binrio da aplicao Web. No entanto,
por default, essa HTML precisa residir em arquivos no mesmo local do arquivo binrio. Isso nem
sempre conveniente ou possvel, e pode haver ocasies em que voc deseja que a HTML resida em
locais longe do seu binrio. Ou ento voc pode querer obter contedo HTML de origens que no
sejam arquivos, digamos, de um banco de dados.
O WebSnap oferece a capacidade de obter HTML de qualquer origem que voc queira. O
componente LocateFileService permite apanhar HTML de qualquer local de arquivo, arquivos include ou qualquer descendente de TStream. Ser capaz de acessar a HTML por um TStream significa
que voc pode apanhar a HTML de qualquer origem, desde que ela possa ser colocada em um
TStream.
Por exemplo, a HTML pode ser colocada em um stream a partir de um arquivo RES embutido
no arquivo binrio na sua aplicao. A aplicao de demonstrao pode mostrar como isso feito.
Naturalmente, voc precisar de alguma HTML para embutir. Usando um editor de textos ou o seu
editor de HTML favorito, apanhe o arquivo wmLogin.html como modelo e salve-o no diretrio da
sua aplicao de demonstrao como embed.html. Depois, inclua algum texto no arquivo para notar
que o arquivo est embutido no arquivo RES. Desse modo, voc saber com certeza que possui o arquivo correto quando ele for exibido.
Depois, naturalmente, voc precisa embutir essa HTML na sua aplicao. O Delphi gerencia
isso com facilidade por meio de arquivos RC, compilando-os automaticamente e acrescentando-os
em uma aplicao. Portanto, use o Bloco de Notas ou alguma ferramenta de edio de textos para
criar um arquivo de texto e chame-o de HTML.RC. Salve-o no mesmo diretrio da sua aplicao de demonstrao e acrescente-o ao seu projeto. Depois, inclua este testo ao arquivo RC:
#define HTML 23 // Identificador de recurso HTML
EMBEDDEDHTML HTML embed.html
passado para o tratador de evento, de modo que ficar por sua conta a criao de um stream usando
a varivel. Nesse caso, AFoundStream torna-se um TResourceStream, que apanha a HTML dos recursos no executvel. A definio de AHandled como True garante que LocateFileServices no se esforar mais para localizar o contedo HTML.
Execute a aplicao e voc ver sua HTML aparecendo quando exibir a pgina Embedded.
Uploading de arquivo
No passado, uma das tarefas mais desafiadoras para um desenvolvedor de aplicao Web era fazer o
uploading de arquivos do cliente para o servidor. Isso normalmente envolvia recursos bastante arcaicos da especificao HTTP e a contagem cuidadosa de cada byte passado. Como voc pode imaginar, o WebSnap torna essa tarefa anteriormente difcil em algo fcil. O WebSnap oferece toda a
funcionalidade para o uploading de arquivo dentro de um TAdapter, e sua parte no muito mais difcil do que colocar um arquivo em um stream.
Como sempre, crie outra pgina na sua aplicao, que far o upload de arquivos para o servidor a partir do cliente. Chame a pgina de Upload e d-lhe um TAdapterPageProducer. Depois, salve
o arquivo como wmUpload. Em seguida, inclua um TAdapter no formulrio. D ao TAdapter um novo
AdapterFileField. Esse campo controlar todo o uploading dos arquivos selecionados no cliente.
Alm disso, d ao Adapter uma nica ao e chame-a de UploadAction.
Em seguida, d ao AdapterPageProducer um AdapterForm com uma AdapterErrorList, um AdapterFieldGroup e um AdapterCommandGroup. Conecte os dois primeiros a Adapter1 e o AdapterCommandGroup ao AdapterFieldGroup. Depois, acrescente todos os campos ao AdapterFieldGroup e todas as
aes ao AdapterCommandGroup. Mude a legenda no boto para Upload File. A Figura 23.17 mostra o
que voc dever ver no Surface Designer.
Figura 23.17 O Web Surface Designer para a pgina Upload, com o boto
Browse (procurar) acrescentado automaticamente.
179
O cdigo pode ser includo em dois lugares. O primeiro deles no tratador de evento Adapter1.AdapterFileFieldOnFileUpload. Esse cdigo dever ser semelhante ao da Listagem 23.5.
Listagem 23.5 Tratador de evento OnFileUpload
procedure TUpload.AdapterFileField1UploadFiles(Sender: TObject;
Files: TUpdateFileList);
var
i: integer;
CurrentDir: string;
Filename: string;
FS: TFileStream;
begin
// Uploading do arquivo aqui
if Files.Count <= 0 then
begin
raise Exception.Create('You have not selected any files to be uploaded');
end;
for i := 0 to Files.Count 1 do
begin
// Cuida para que o arquivo seja um .jpg ou .jpeg
if (CompareText(ExtractFileExt(Files.Files[I].FileName), '.jpg') < > 0)
and (CompareText(ExtractFileExt(Files.Files[I].FileName), '.jpeg')
< > 0) then
begin
Adapter1.Errors.AddError('You must select a JPG or JPEG file to upload');
end else
begin
CurrentDir := ExtractFilePath(GetModuleName(HInstance)) + 'JPEGFiles';
ForceDirectories(CurrentDir);
FileName := CurrentDir + '\' + ExtractFileName(Files.Files[I].FileName);
FS := TFileStream.Create(Filename, fmCreate or fmShareDenyWrite);
try
FS.CopyFrom(Files.Files[I].Stream, 0); // 0 = copia tudo do incio
finally
FS.Free;
end;
end;
end;
end;
Esse cdigo primeiro verifica se voc selecionou um arquivo e depois certifica-se de que voc
selecionou um arquivo JPEG. Depois de determinar que voc fez isso, ele apanha o nome do arquivo, garante que o diretrio receptor existe e coloca o arquivo em um TFileStream. O trabalho real
aqui feito nos bastidores pela classe TUpdateFileList que gerencia todo o tratamento de formulrio esotrico e em vrias partes do HTTP, necessrio para o uploading de um arquivo do cliente
para o servidor.
O segundo lugar para incluir cdigo no tratador OnExecute para UploadAction em Adapter1.
Ele o seguinte:
procedure TUpload.UploadActionExecute(Sender: TObject; Params: TStrings);
begin
Adapter1.UpdateRecords;
end;
180
que simplesmente diz ao Adapter para atualizar seus registros e apanhar os arquivos que foram solicitados.
A classe abstrata implementa a funo Content somente porque a funo GetHTML declarada
como abstrata. A funo Content basicamente verifica se o componente que a contm um Layout- 181
Group. Se for LayoutGroup, a funo Content coloca seu contedo dentro do LayoutGroup. Caso contrrio, Content simplesmente retorna os resultados de GetHTML. Os componentes descendentes, portanto, s precisam implementar a funo GetHTML, retornando o cdigo HTML apropriado, e
podem ser registrados para trabalhar dentro de um TAdapterPageProducer.
O cdigo neste CD-ROM implementa dois componentes que permitem incluir contedo
HTML em um TAdapterPageProducer, seja como uma string ou como um arquivo. O cdigo para o
componente Tddg6HTMLCode aparece na Listagem 23.7.
Essa uma classe muito simples. Ela simplesmente oferece uma propriedade publicada do tipo
TString, que apanhar qualquer cdigo HTML e depois o colocar no TAdapterPageProducer como
se encontra. A funo GetHTML simplesmente retorna a HTML em formato de string. Voc pode
criar componentes para retornar qualquer cdigo HTML que queira incluir imagens, links, arquivos e outro contedo. Tudo o que os componentes descendentes precisam fazer oferecer seu contedo HTML em um mtodo GetHTML( ) modificado. Observe que existem funes de registro de
suporte na unidade onde os componentes so implementados. Ao criar componentes, certifique-se
de registr-los na sua unidade, semelhante aos que esto neste CD-ROM. Para usar esses componentes, basta instal-los em um pacote durante o projeto e os componentes aparecero no Web Surface Designer do TAdapterPageProducer (veja a Figura 23.18).
182
Resumo
Essa foi uma viso geral rpida do poder do WebSnap. Este captulo apenas arranhou a superfcie
do que o WebSnap pode fazer. No deixe de verificar as diversas aplicaes de demonstrao no diretrio <Delphi>\Demos\WebSnap. Muitas delas do mais funcionalidade ao estado padro dos componentes WebSnap.
Logicamente, o WebSnap uma tecnologia poderosa, mas preciso que voc faa algum esforo para entend-la. Entretanto, depois que voc passar pela curva de aprendizado inicial, logo
estar criando, com facilidade, sites Web poderosos, dinmicos e controlados por banco de dados.
183
Desenvolvimento para
dispositivo sem fio
NESTE CAPTULO
l
Tecnologias de rdio
CAPTULO
24
Sem sombra de dvida, duas tecnologias nos ltimos 10 anos tocaram nossas vidas mais do que
quaisquer outras: a Internet e os dispositivos portteis. Apesar dos altos e baixos dos negcios baseados na Internet, esta e, em particular, a Web tem mudado nossas vidas permanentemente.
Ela tem afetado o modo como nos comunicamos, compramos, trabalhamos e brincamos. Mais
que isso, muitos de ns agora sentem arrepios ao pensar que poderia estar em algum lugar sem
nosso confivel celular (ou telefone porttil) ou Personal Digital Assistant (PDA). Devido sua
importncia em nossas vidas, parece natural que essas duas tecnologias agora estejam em um estado de convergncia, com os celulares se tornando tentculos sem fio que saem da Internet com fio
para nos oferecer os servios e as informaes s quais nos tornamos viciados.
Com dispositivos de informao portteis tornando-se mais uma necessidade do que uma novidade no clima comercial e social de hoje, ns, os desenvolvedores encaramos o desafio de aproveitar esse hardware e infra-estrutura para atender demanda cada vez maior para colocar dados e
aplicaes nos dispositivos portteis. Com uma gama incrvel de dispositivos portteis, redes e tecnologias no mercado, as principais perguntas para os desenvolvedores se tornam: Qual das plataformas sem fio voc deve focalizar? Qual o modo mais eficaz de focaliz-las? Que tecnologias posso aproveitar para fazer com que meus dados e aplicaes se adaptem a dispositivos portteis?
Quais so os prs e os contras de todas essas plataformas e tecnologias?
Este captulo, de forma alguma, tem como objetivo servir como um guia completo, descrevendo como implementar todas as diversas tecnologias portteis. Isso exigiria muitos volumes. No entanto, estaramos com o dever cumprido se voc conseguisse duas coisas deste captulo. Primeiro,
voc conseguir usar este captulo como uma introduo para entender o papel que muitos dos vrios tipos de hardware, software e tecnologias desempenham na computao mvel, do ponto de
vista de um desenvolvedor. Segundo, voc dever entender como algumas dessas tecnologias portteis podem ser implementadas com o Delphi.
banco de dados de desktop no-escalveis, ambos importantes para os negcios. A resposta estava
em sistemas cliente/servidor, a noo de banco de dados poderosos de empresas como Oracle,
Sybase e Informix, conectados a interfaces com o usurio rodando em PCs. Isso permitiu que os sistemas aproveitassem o poder em cada desktop enquanto permitiram que os servidores de banco de
dados realizassem suas tarefas especializadas. Ferramentas de desenvolvimento de quarta gerao,
como Visual Basic e Delphi, facilitam o desenvolvimento mais do que nunca, e o suporte para o banco de dados estava embutido como um cidado de primeira classe das ferramentas.
Telefones portteis
Os telefones portteis, sem dvida, so o tipo mais difundido de dispositivo porttil sem fio. Os telefones portteis ultrapassaram o mbito dos sistemas de comunicao puramente para vez e entraram para a comunicao de dados. A grande maioria dos novos telefones que chegam ao mercado
186 aceita mensagens de texto usando o Short Message Service (SMS) e navegao tipo Web, usando o
Wireless Application Protocol (WAP). As taxas de dados atuais ainda so baixas, entre 9,6 e 14,4k,
mas novas tecnologias prometem oferecer velocidades de at 2 Mbits dentro de 2 a 3 anos.
Dispositivos PalmOS
Dispositivos rodando o sistema operacional PalmOS, da Palm Computing, tm liderado o mercado
no espao do PDA h vrios anos. Alguns dispositivos PalmOS possuem capacidade de sem fio embutida (como a srie Palm VII ou o Smartphone da Kyocera), e a capacidade sem fio pode ser acrescentada a outros, por meio de um modem sem fio (como aqueles fabricados pela Novatel) ou por
um conector para telefone porttil, disponvel na Palm. Diversas empresas licenciaram o PalmOS,
da Palm, Inc., para incluso em seus prprios dispositivos, incluindo Handspring, Sony, Kyocera,
Symbol, Nokia, Samsung e TRG. Entre as vantagens do PalmOS est o fato de que eles possuem
uma fatia esmagadora do mercado de PDAs e existe uma forte comunidade de desenvolvimento
com um ativo mercado de terceiros.
Pocket PC
Compaq, HP, Casio e outros fabricantes produzem PDAs baseados no sistema operacional Pocket
PC (anteriormente Windows CE) da Microsoft. At agora, nenhum desses dispositivos possui capacidade interna para uso sem fio, mas eles oferecem suporte para modems sem fio de modo semelhante aos dispositivos PalmOS. Mais ainda, dispositivos Pocket PC costumam ser um pouco mais
poderosos do que seus correspondentes PalmOS, e alguns tm a capacidade de aceitar PC Cards
(PCMCIA) padro. Isso tem o potencial de permitir uma faixa de expanso ainda mais extensa para
redes sem fio com maior largura de banda.
RIM BlackBerry
A BlackBerry oferece a funcionalidade tipo PDA no tamanho de um pager. Com um modem sem fio
interno e um teclado, o BlackBerry especialmente adequado para tarefas de e-mail porttil. No entanto, o BlackBerry tambm oferece suporte para navegao Web por meio de um browser de terceiros. Descobri que o BlackBerry uma plataforma incrvel para e-mail corporativo, graas integrao interna com MS Exchange ou Lotus Domino, mas o dispositivo pretende se tornar um utenslio
para Web, por causa do tamanho de sua tela e suas capacidades de navegao.
Tecnologias de rdio
As tecnologias de rdio oferecem a conexo entre os dispositivos portteis e a Internet ou a rede local da empresa.
CDPD
Cellular Digital Packet Data (CDPD) uma tecnologia que permite a transferncia de dados em pacotes atravs de redes sem fio, oferecendo um aumento na largura de banda e a funcionalidade de 187
estar sempre ligada. CDPD comum com o servio de PDA sem fio nos Estados Unidos, como
aquele oferecido pela GoAmerica ou OmniSky, com velocidades chegando a 19,2k.
3G
Redes portteis 3G, ou de terceira gerao, so criadas desde o princpio para lidar com diversos tipos diferentes de streams de mdia e ostentam uma largura de banda estimada como algo na faixa de
384k-2M. Os candidatos mais provveis para serem os suportes do padro 3G so tecnologias conhecidas como EDGE e UMTS. No entanto, embora exista a tecnologia e vrias operadoras possuam
espectro suficiente para implementar redes 3G, parece que nenhuma deseja ser a primeira a fazer o
investimento multibilionrio em upgrades de rede a fim de seguir adiante com o 3G.
GPRS
General Packet Radio Service (GPRS) considerado como o caminho de migrao do 2G para o
3G, e normalmente considerado como 2.5G. GPRS permite que o trfego baseado em pacote
atravs da infra-estrutura 2G existente, apenas com upgrades relativamente pequenos. O throughput observado nas redes GPRS provavelmente estar na faixa de 20 a 30k.
Bluetooth
Dispositivos incorporando a tecnologia de rdio Bluetooth s agora esto comeando a aparecer no
mercado. Bluetooth uma importante tecnologia emergente, pois permite o uso de rede ocasional
em onda curta entre diferentes tipos de dispositivos. Como os mdulos de rdio Bluetooth so muito pequenos, consomem pouca energia e so relativamente baratos, eles estaro embutidos em todos os tipos de dispositivos portteis, incluindo telefones, PDAs, laptops e assim por diante. A maioria dos rdios Bluetooth ter um alcance de cerca de 10 metros e desfrutar de aproximadamente
700k de largura de banda. As aplicaes em potencial para o Bluetooth incluem o sincronismo de
dados entre PDA e componente, quando estiverem nas proximidades um do outro, ou o fornecimento de conectividade com Internet para um laptop, por meio de um telefone porttil no bolso de
uma pessoa. Um novo termo, Personal Area Network (PAN), usado para descrever essa noo de
pequena rede sem fio, onde todos os nossos dispositivos portteis pessoais se comunicam regularmente uns com os outros.
mais correto pensar no Bluetooth como um substituto para os cabos serial, USB ou IEEE
1394 do que como uma tecnologia de rede tipo Ethernet. A verso atual do Bluetooth oferece suporte apenas para um dispositivo mestre controlando um mximo de sete dispositivos escravos simultneos.
802.11
Embora a tecnologia Bluetooth seja projetada como uma tecnologia de rede pessoal de curto alcance,
o 802.11 foi criado visando o uso em redes locais. A gerao atual dessa tecnologia, 802.11b ou WiFi,
oferece at 11Mb de largura de banda, com uma verso de 45MB conhecida como 802.11a no horizonte. 802.11 possui um alcance de cerca de 30 metros, com alcances maiores sendo possveis com
antenas especiais. Os requisitos de energia do 802.11 so maiores do que os do Bluetooth, e os dispositivos possuem maior tamanho; o dispositivo de rdio pode caber dentro de um PC Card padro, o
que timo para laptops, mas no conveniente para telefones ou para a maioria dos PDAs.
Uma observao importante que se deve ter em mente que o Bluetooth e o 802.11 compartilham o mesmo espectro de 2,4 GHz, de modo que possvel que os dois interfiram um com o outro
quando ocupam o mesmo espao. Embora no seja provvel que eles congelem completamente um
ao outro, visto que ambos usam tecnologia de espectro amplo para pular de freqncias vrias vezes
por segundo, provvel que o desempenho sofra em uma ou ambas as conexes, devido interfe188 rncia mtua.
SMS
A tecnologia Short Message Service (SMS) usada para enviar mensagens de texto curtas (geralmente
de 100 a 160 caracteres no mximo) para telefones portteis. Fora o tamanho limitado para a mensagem, a tecnologia SMS limitada devido a questes de interoperabilidade entre as operadoras de rede
e protocolos SMS variados, empregados pelos operadoras. No entanto, SMS est se tornando bastante popular principalmente na Europa devido sua facilidade de uso e grande disponibilidade.
Como cada operadora pode empregar pequenas variaes ao tema SMS, as tcnicas para se desenvolver aplicaes que oferecem suporte ao SMS podem variar, dependendo da operadora que voc esteja visando. Embora o GSM tenha vantagem em relao a outras redes de telefone mvel porque o suporte para SMS est embutido no padro GSM, do ponto de vista de um desenvolvimento de aplicao,
ainda pode ser algo desafiador o envio de mensagens SMS de um servidor conectado Internet para um
cliente porttil. Isso porque voc precisa trabalhar com servidores SMS no lado da operadora, o que poderia envolver um suporte para padres variados e at mesmo taxas de licenciamento.
Recomendamos um dentre dois caminhos para incorporar o suporte a SMS nos servidores. A
primeira opo simplesmente usar e-mail; a maioria das operadoras oferece suporte para o envio
de uma mensagem SMS, enviando uma mensagem de e-mail para um endereo de e-mail especfico,
que contm o nmero do telefone do destinatrio. Embora essa seja uma tcnica relativamente simples, do ponto de vista tcnico, a desvantagem que o suporte no universal e acrescenta outra camada de falha em potencial. A segunda opo comprar qualquer uma das muitas ferramentas de
terceiros que cuidam do envio de mensagens SMS por diversos tipos de redes. Essa a tcnica preferida, embora envolva alguns custos iniciais e/ou taxas de licenciamento.
WAP
Wireless Application Protocol (WAP) foi estabelecido como meio padro para acessar informaes
da Internet por meio de um dispositivo porttil. A aceitao geral do WAP no mercado tem sido dividida. Por um lado, WAP tem sido bem recebido por operadores de rede e fabricantes de telefone,
pois foi projetado desde o incio para trabalhar com qualquer servio sem fio e padro de rede em
praticamente qualquer dispositivo. No entanto, a experincia com WAP da parte do usurio no
tem sido totalmente positiva, devido a limitaes no tamanho da tela, em suas capacidades de entrada de dados e na largura de banda sem fio. Alm do mais, como os sites WAP geram receita limitada
com base no uso, no existe um incentivo comercial forte para o desenvolvimento de sites WAP de
alta qualidade. O contedo para sistemas WAP foi desenvolvido em uma linguagem baseada em
XML, conhecida como Wireless Markup Language (WML) linguagem de marcao sem fio.
A arquitetura tpica da aplicao WAP ilustrada na Figura 24.1.
Dispositivo
WAP
Host de
destino
Gateway
WAP
URL
URL
WML
WML
HTML
(compilada)
Filtro
HTML
189
Se voc souber apenas um pouco sobre HTML e XML, provavelmente j desvendou esse cdigo com relativa facilidade. O prlogo do documento, composto das trs primeiras linhas, XML
padro e descreve a verso da XML desse documento e o local da DTD usada para descrever as tags
e os atributos nele contidos. Depois disso, o cdigo passa a criar um baralho com duas cartas, uma
com um boto OK e uma com uma saudao.
A sintaxe da WML tambm oferece suporte a coisas como eventos, timers, conjuntos de campos, listas e imagens (embora nem todos os dispositivos aceitem imagens). Algumas das verses mais
recentes dos browsers WAP aceitam ainda uma linguagem de scripting chamada WMLScript. No
podemos abordar toda a linguagem WML aqui, mas, se voc estiver interessado, poder ver os detalhes da especificao WML em http://www.WAPforum.org.
Se voc quiser experimentar o desenvolvimento de algum contedo WML, o modo mais fcil
de comear obter um simulador. Voc pode obter um simulador com o desenvolvedor do microbrowser, em http://www.openwave.com, ou dois outros simuladores populares que vm diretamente
dos lderes de comunicaes portteis Nokia e Ericsson; visite http://forum.nokia.com ou
http://www.ericsson.com/developerszone. sempre bom fazer com que as coisas funcionem nos simuladores primeiro, antes de passar para o hardware real, pois os simuladores oferecem um tempo
de escrita-execuo-depurao muito mais rpido. Antes da liberao final, tambm uma boa
idia testar seu produto final no mximo de dispositivos possvel, pois as caractersticas exclusivas
de cada dispositivo podem fazer com que seu baralho se comporte ou aparea de forma diferente do
que voc tinha em mente.
Segurana do WAP
A especificao WAP exige que uma pilha de criptografia sem fio, conhecida como Wireless Transport Layer Security (WTLS), seja usada para conexes seguras. Como a SSL exige muitos recursos
para ser usada com a gerao atual de dispositivos portteis, a WTLS foi criada para oferecer servios de criptografia e autenticao entre o dispositivo e o gateway WAP. O gateway, por sua vez, j
capaz de se comunicar com os hosts da Internet por meio do protocolo SSL padro. Apesar do fato
de que tanto WTLS quanto SSL sejam bastante seguros por si ss, existe a possibilidade de brechas
de segurana no gateway WAP, no ponto onde o stream de dados WTLS decodificado e recodificado com SSL. A arquitetura WTLS ilustrada na Figura 24.2.
Telefone
porttil
Gateway
WAP
Servidor Web
Comunicao
criptografada
com WTLS
Comunicao
criptografada
com SSL
Converso
WTSL/SSL
as aplicaes tradicionais, baseadas em browser. No entanto, o lado oposto da moeda que, para os
desenvolvedores, a criao de aplicaes que sejam atraentes e teis para os usurios finais mais
desafiadora, dadas essas limitaes.
Para este exemplo, comece criando uma aplicao WebBroker normal, conforme aprendeu
no Captulo 23. Essa aplicao possui um nico mdulo Web com uma nica ao. Essa ao marcada como default, conforme mostramos na Figura 24.3.
A Listagem 24.1 mostra o cdigo-fonte para a unidade principal dessa aplicao, incluindo o
tratador de evento OnAction para a ao default do mdulo Web.
Listagem 24.1 Main.pas a unidade principal para o projeto SimpWap
unit Main;
interface
uses
SysUtils, Classes, HTTPApp;
type
TWebModule1 = class(TWebModule)
procedure WebModule1WebActionItem1Action(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
private
{ declaraes privadas }
public
{ declaraes pblicas }
end;
var
WebModule1: TWebModule1;
implementation
{$R *.DFM}
const
SWMLContent = 'text/vnd.wap.wml';
SWBMPContent = 'image/vnd.wap.wbmp';
SWMLDeck =
'<?xml version="1.0"?>'#13#10 +
'<!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN"'#13#10 +
'"http://www.WAPforum.org/DTD/wml_1.1.xml">'#13#10 +
'<wml>'#13#10 +
' <card>'#13#10 +
'
<do type="accept">'#13#10 +
'
<go href="#hello"/>'#13#10 +
192
Quando a ao chamada, o tratador de evento responde definindo as propriedades ContentType e Content do objeto Response. ContentType definido como a string do tipo de contedo
WML, e o contedo retornado o mesmo baralho WAP simples que foi explicado anteriormente
neste captulo.
NOTA
Lembre-se de definir o ContentType do objeto Response como a string contendo o tipo MIME da variedade de contedo que voc est retornando. Isso define a informao de tipo de contedo no cabealho HTTP. Se voc retornar o tipo de contedo incorreto, seu contedo provavelmente ser mal
interpretado no dispositivo de destino. Alguns tipos de contedo WAP dignos de nota so
l
193
Informe de erros
O tratador de exceo default para aplicaes WebSnap envia uma mensagem HTML ao cliente
com informaes sobre o erro. Naturalmente, a maioria dos dispositivos WAP no poder entender
um erro HTML, de modo que importante garantir que quaisquer erros que possam ocorrer em
sua aplicao WAP sejam expostos ao cliente como mensagens WML, em vez de HTML. Isso pode
ser feito envolvendo-se cada tratador de evento OnAction com um bloco try..except que chama
uma rotina de formatao de mensagem de erro. Isso aparece na Listagem 24.2.
Wireless Bitmaps
Embora o WAP ainda no tenha suporte para os elegantes grficos JPEG e GIF, comuns na Web, a
maioria dos dispositivos WAP aceita imagens monocromticas na forma de Wireless bitmaps
(wbmp). A Listagem 24.2 acrescenta uma nova ao ao mdulo da Web, para dar suporte gerao de um wbmp. Essa ao gera um grfico de aparncia oficial, porm bastante aleatria, para a
exibio no dispositivo de destino. Embora no entremos em detalhes sobre o formato binrio
dos arquivos wbmp neste texto, voc pode ver que no preciso muita coisa para gerar wbmps
manualmente em suas aplicaes WAP. A Figura 24.5 mostra como ficaria um WBMP em uma
tela de telefone.
NOTA
Nem todos os browsers, dispositivos e simuladores WAP oferecem suporte para imagens wbmp.
No se esquea de testar antes de assumir que existe suporte.
195
I-mode
I-mode uma tecnologia prpria para contedo da Internet em telefones portteis, desenvolvida
pela NTT DoCoMo, gigante de telecomunicaes do Japo. I-mode tem muito sucesso no Japo,
com mais de 20 milhes de assinantes e crescendo. De vrias maneiras, i-mode tudo o que o WAP
no : aceita grficos ricos, com 256 cores, utiliza telas de telefone coloridas e uma conexo TCP/IP
sempre ligada. Alm do mais, a DoCoMo desenvolveu um modelo de compartilhamento de receitas que permite ao sites i-mode obterem uma fatia da torta financeira com base no uso. No entanto, o i-mode est dificultado pela aspecto da tecnologia prpria, e a disponibilidade fora do Japo
ainda escassa; os servios do i-mode estaro sendo distribudos nos Estados Unidos, Reino Unido
e Europa continental a partir deste ano.
De um ponto de vista do desenvolvedor, o direcionamento de telefones i-mode no muito
mais difcil do que a criao de contedo para a Web, pois o contedo i-mode desenvolvido usando-se um subconjunto da HTML, conhecido como Compact HTML (cHTML). As tags cHTML
aceitas e as regras esto disponveis na DoCoMo, em http://www.nttdocomo.com/i/tagindex.html.
Observe que, alm de usar apenas as tags aceitas pela cHTML, os sites i-mode tambm precisam garantir que ser usado o conjunto de caracteres S-JIS, a imagens so no formato GIF e as pginas no
possuem qualquer contedo de script ou Java.
PQA
Palm Query Applications (PQA) so essencialmente pginas HTML normais, sem acrscimos como
scripting e imagens projetadas para exibio na tela de um dispositivo PalmOS sem fio. Os dispositivos PalmOS sem fio, como os Palm VIIx ou aqueles equipados com modems Novatel, atualmente
esto limitados Amrica do Norte. Assim como o i-mode, PQAs so desenvolvidas usando um sub- 197
conjunto da HTML, exceto que o Palm incluiu algumas extenses prprias. Os desenvolvedores interessados em criar PQAs devero baixar o Web Clipping Developers Guide do Palm, em
http://www.palmos.com/dev/tech/docs/.
Em geral, o ramo PQA da HTML inclui tudo o que existe na HTML 3.2, com a exceo dos
applets (miniaplicativos), JavaScript, tabelas aninhadas, mapas de imagem e as tags VSPACE, SUB, SUP,
LINK e ISINDEX. PQAs tambm incluem vrios acrscimos interessantes HTML. Os mais interessantes so a meta tag palmcomputingplatform e as tags %zipcode e %deviceid. Quando um documento
HTML contm a meta tag palmcomputingplatform, isso serve como um indicador para o proxy
Palm.net da Palm Computing (que atua como intermedirio entre um servidor Web e o dispositivo
Palm, semelhante a um gateway WAP) de que o documento est otimizado para exibio em um
porttil PalmOS e no exige anlise para que seu contedo invlido seja removido. Quando a tag
%zipcode aparece em um pedido postado pelo cliente, o proxy Palm.net substitui a tag pelo cdigo
postal de onde o dispositivo est localizado (com base nas informaes da torre de rdio). A tag %deviceid, semelhantemente, envia o ID exclusivo do dispositivo PalmOS para o servidor. Isso prtico principalmente porque a HTML PQA no oferece suporte para cookies, mas um meio semelhante de gerenciamento de estado pode ser preparado com o uso da tag %deviceid.
Com o Web clipping, o Palm utiliza uma tcnica diferente da maioria dos outros players nesse
espao. Em vez de usar uma entidade tipo browser para navegar at um site e apanhar seu contedo,
as PQAs existem localmente no dispositivo PalmOS. PQAs so criadas passando-se um arquivo
.HTML padro por um compilador especial, que vincula a HTML com arquivos grficos referenciados e outras dependncias. Os usurios instalam PQAs como aplicaes PRC normais do
PalmOS. As PQAs ganham eficincia incluindo partes da aplicao local e s indo para a rede para
as pginas de resultados.
Cliente PQA
A primeira etapa para o desenvolvimento de uma aplicao PQA criar a parte que residir fisicamente no dispositivo cliente. Isso feito criando-se um documento HTML e compilando-o com a
ferramenta PQA Builder da Palm Computing. Um exemplo de um documento HTML para uma
PQA aparece na Listagem 24.3.
Listagem 24.3 Um documento HTML para uma PQA
<html>
<head>
<title>DDG PQA Test</title>
<meta name="palmcomputingplatform" content="true">
</head>
<body>
<p>This is a sample PQA for DDG</p>
<img src="image.gif">
<form method="post" action="http://128.64.162.164/scripts/pqatest.dll">
<input type="hidden" value="%zipcode" name="zip">
<input type="hidden" value="%deviceid" name="id">
<input type="submit">
</form>
</body>
Voc pode ver que esse documento HTML simples contm uma referncia a uma imagem, algum texto, um formulrio com um boto de envio e campos ocultos usados para passar o cdigo
postal e o ID do dispositivo para o servidor. A Figura 24.6 mostra esse documento sendo compilado
no PQA Builder.
198
Uma vez compilado, gerado um arquivo com uma extenso .pqa. Esse arquivo contm o documento HTML, alm de quaisquer imagens referenciadas. Esse arquivo pode ser instalado no dispositivo PalmOS da mesma forma que qualquer outra aplicao PalmOS.
Servidor PQA
A parte do servidor, como WAP, uma aplicao WebSnap que cuida dos pedidos de pgina de
clientes e retorna pginas. No entanto, ao contrrio do WAP com sua WML, PQAs se comunicam
usando a variante da HTML descrita anteriormente. A Listagem 24.4 mostra a unidade principal de
uma aplicao WebBroker, criada para realizar o papel de servidor para o cliente PQA descrito anteriormente.
Listagem 24.4 Main.pas a unidade principal para a aplicao PQATest
unit Main;
interface
uses
SysUtils, Classes, HTTPApp;
type
TWebModule1 = class(TWebModule)
procedure WebModule1WebActionItem1Action(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
private
{ Declaraes privadas }
public
{ Declaraes pblicas }
end;
var
WebModule1: TWebModule1;
implementation
{$R *.DFM}
const
SPQAResp =
'<html><head><meta name="palmcomputingplatform" content="true"></head>'+
#13#10 +
'<body>Hello from a Delphi server<br>Your zipcode is: %s<br>'#13#10 +
199
200
A experincia do usurio , de longe, o fator mais importante para determinar se um sistema mvel
por fim ser til para os indivduos. No entanto, a experincia do usurio normalmente tem curta
durao em favor dos recursos gratuitos ou da tecnologia. Devido s limitaes inerentes ao mundo
mvel principalmente conectividade e tamanho de dispositivo , no h muito espao para erros
quando o desenvolvedor tenta fornecer aos usurios a funcionalidade de que precisam e no momento em que precisam dela. O truque focalizar o usurio: determine quais informaes ou servios os usurios precisam e esforce-se para conseguir isso da forma mais eficiente possvel.
A importncia do espao
Ao criar aplicaes portteis, voc sempre dever permanecer sensvel quantidade disponvel de
espao na tela. Seja criando uma aplicao baseada em WAP, vinculada a um microbrowser, ou uma
interface com o usurio personalizada em J2ME, os desenvolvedores de aplicao precisam equilibrar entre os problemas de comunicar uma quantidade suficiente de informaes em cada tela e
manter uma interface com o usurio legvel e navegvel.
M-Commerce
Assim como o e-commerce passou a se referir a transaes comerciais realizadas pela Web,
m-commerce refere-se a transaes comercias feitas por meio de dispositivos mveis. Os desenvolvedores de sites de comrcio por dispositivo mvel precisam entender que m-commerce fundamentalmente diferente de e-commerce. Evidentemente, no vivel que os clientes de dispositivos
mveis naveguem pelos itens. No caso de um telefone porttil, por exemplo, muito demorada a
entrada de toques de tecla imagens ou no existem ou so de m qualidade, e no h espao suficiente na tela para a descrio dos itens.
Em vez disso, os sistemas de m-commerce precisam ser projetados com a noo de que os usurios sabem o que querem; basta facilitar o modo como eles do seu dinheiro. Lembre-se: se o usurio
quiser comprar uma televiso ou um livro, no h motivo para que ele no possa simplesmente esperar at chegar em sua casa ou escritrio e fazer a compra em um computador normal. O fato de
que o usurio sequer deseja se engajar no m-commerce significa que existe algum sentido na urgncia de se fazer a compra, e os comerciantes de m-commerce bem-sucedidos sero aqueles que reconhecerem e tirarem proveito desse fato.
Por exemplo, um pas da Europa oriental permite que os motoristas paguem a tarifa do estacionamento com seus telefones celulares. Isso pode ser uma aplicao relativamente simples na superfcie, mas a proposio de valor para ambas as partes muito atraente. O motorista no precisa se
preocupar em ter moedas suficientes para colocar nos medidores, e o operador do aparelho no
precisa juntar as moedas de dezenas ou centenas ou milhares de medidores, a fim de troc-las no
banco. O policial monitorando os medidores pode usar um dispositivo mvel ligado ao mesmo sistema para saber instantaneamente quanto tempo resta para determinada vaga.
Resumo
O mundo da computao para dispositivo sem fio cresceu muito nos ltimos anos, e pode ser muito
difcil acompanhar todas as tendncias emergentes. Nossa esperana que, nesse ponto, voc j esteja armado com informaes suficientes para tomar algumas decises estratgicas e seguir adiante
com um projeto de mobilidade. Alm disso, voc viu que o Delphi uma ferramenta bastante capaz
quando se trata da criao da prxima gerao de aplicaes para dispositivos sem fio.
202