Revista The Club Megazine - 08/2003 A utilização, reprodução, apropriação, armazenamento em banco de dados, sob qualquer forma ou meio, de textos, fotos e outras criações intelectuais em cada publicação da revista “The Club” são terminantemente proibidos sem autorização escrita dos titulares dos direitos autorais. Copyright© The Club® 2003 EDITORIAL Editorial THE CLUB Av. Celso Ferreira da Silva, 190 Jd. Europa - Avaré - SP - CEP 18.707-150 Informações: (0xx14) 3732-3689 Suporte: (0xx14) 3733-1588 - Fax: (0xx14) 3732-0987 Internet http://www.theclub.com.br Cadastro: [email protected] Suporte: [email protected] Informações: [email protected] Dúvidas Correspondência ou fax com dúvidas devem ser enviados ao - THE CLUB, indicando "Suporte". Opinião Se você quer dar a sua opinião sobre o clube em geral, mande a sua correspondência para a seção "Tire sua dúvida". Reprodução A utilização, reprodução, apropriação, armazenamento em banco de dados, sob qualquer forma ou meio, de textos, fotos e outras criações intelectuais em cada publicação da Revista “The Club” são terminantemente proibidos sem autorização escrita dos titulares dos direitos autorais. Olá amigos, aqui estamos com mais uma edição da The Club Megazine, trazendo até você informações que com certeza tornam o dia-a-dia do programador Delphi um pouco mais fácil! Começamos apresentando o mais recente lançamento da Borland, o C#Builder, que é a primeira IDE da Borland para desenvolvimento de aplicações para a plataforma Microsoft .Net e fizemos uma pequena análise da ferramenta, a qual nos deixou ainda mais anciosos para a nova versão do Delphi que está por vir, vamos aguardar. No mês passado, publicamos uma entrevista exclusiva com Jason Wharton, o criador do IBObjects e neste mês estamos dando continuidade ao assunto, demonstrando como instalar esta ótima suíte de componentes. Um assunto que tem sido bastante solicitado ao suporte técnico, é referente a relacionamentos master/detalhe no dbExpress, e mediante isso, estamos publicamos um artigo completo a respeito deste assunto, procurando assim sanar todas ou pelo menos as principais dúvidas que têm chegado até nós. Nosso artigo de capa deste mês, com certeza irá resolver um problema comum entre muitos programadores! Você acha complicado gerar instalações para distribuir sua aplicação? Bem, neste artigo estamos apresentando a ferramenta Inno Setup, a qual é muito poderosa, gratuíta e simples de utilizar. Através dela você poderá gerar instalações, inclusive embutindo seu banco de dados, vale a pena conferir! Como mencionei no início, temos muita coisa interessante este mês! Confira ainda um artigo que mostra como controlar a ociosidade da aplicação, permitindo chamar uma tela de login após o aplicativo ficar inativo por um certo tempo. Outro assunto que estamos abordando, é a atualização de versão de aplicativos em rede, sendo este tema, também muito solicitado aos nossos técnicos. E finalizando, não poderia faltar nossa consagrada sessão Dicas & Truques, que dispensa comentários. Bom leitura e até a próxima, Copyright© The Club® 2003 Impressão e acabamento: Celso Jefferson Paganelli Presidente - The Club Impressos Gril - Gril Gráfica e Repr. Ind. Ltda. Tel.: (0xx14) 3762.1345 - Fax: (0xx14) 3762.1259 Rua São Paulo, 447 - Cep 18.740-000 Taquarituba - SP Tiragem: 5.000 exemplares Diretor - Presidente Celso Jefferson M. Paganelli Diretor Técnico Mauro Sant’Anna Delphi é marca registrada da Borland International, as demais marcas citadas são registradas pelos seus respectivos proprietários. Editorial ............................................................................ 03 C#Builder - A primeira IDE da Borland para .NET ............... 04 Como instalar e utilizar o IBObjects ................................... 06 DBEXPRESS - Criando um relacionamento Master/Detalhe 11 Criando Instalações personalizadas com o Inno Setup ....... 16 TimeOut - Controlando a ociosidade da aplicação ............. 22 Criando aplicativos Auto-Atualizáveis ............................... 25 Dicas & Truques ................................................................. 28 MeGAZINE 3 NEWS C# Builder A primeira IDE da Borland para desenvolvimento .NET Equipe de suporte do The Club - [email protected] Recentemete a Borland lançou o C#Builder, sendo esta a sua primeira IDE de desenvolvimento para a plataforma Microsoft .Net. Nós do The Club já estamos trabalhando com a plataforma Microsoft .Net há um bom tempo, mais precisamente a partir do final do ano 2000, utilizando o Microsoft Visual Studio .Net, com enfoque na linguagem C#. Estivemos analisando o C#Builder e achamos a IDE bastante atrativa (não poderia ser diferente, visto essa característica ser marca registrada das ferramentas Borland), o compilador bastante rápido e sem contar nas semelhanças com a IDE do Delphi, o que nos fez sentir-se em casa. O C#Builder pode ser encontrado em quatro versões, sendo a Personal, a Professional, a Enterprise e a Architect. Nas versões Enterprise e Architect existem drivers nativos para acesso aos principais bancos de dados do mercado, sendo Oracle, SQLServer, DB2 e Interbase. Um detalhe importante é que a versão Personal está disponível gratuitamente para download no site da Borland, desde que utilizada para fins não comerciais e é uma boa alternativa para que está querendo apenas conhecer ou começar a estudar C#/.Net. Para maiores informações acesse http:// www.borland.com/csharpbuilder/. No C#Builder temos suporte ao desenvolvimento de aplicações baseadas em WinForms (formulários windows, semelhantes aos que trabalhamos atualmente no Delphi) e WebForms/ASP.Net (na realidade, são páginas web, tratadas como formulários), WebServices e criação de classes e controles Conclusão Podemos apenas ter certeza de que temos mais uma ótima ferramenta para desenvolvimento .Net no mercado e ficamos ainda mais anciosos aguardando a nova versão do Delphi com IDE para .Net, o qual promete bastante! Figura 1 – Menu do C#Builder 4 MeGAZINE NEWS Figura 2 – IDE C#Builder – projeto WinForms. Figura 3 – IDE C#Builder –projeto WebForm/Asp.Net MeGAZINE 5 DELPHI Como instalar e utilizar o IBObjects Por Claudinei Rodrigues – [email protected] No mês passado eu publiquei a entrevista com o nosso amigo Jason Wharton, falando um pouco sobre o IBObjects. Neste mês vou continuar a falar deste componente e a melhor maneira é mostrar a vocês como se instalar o componente. Não que sua instalação seja complicada, mas nada melhor do que começar a conhecer um componente, como fazendo a sua instalação. Eu sei que isto pode ser maçante para alguns programadores mais experientes, mas também existem programadores que estão conhecendo o Delphi agora. Antes, porém, você pode fazer o download deste componente a partir da página do The Club. Para fazer o download é muito simples, acesse a nossa home page, www.theclub.com.br, clique no ícone Área de download e depois no ícone Pesquisar arquivos. No campo Digite a informação a ser pesquisada informe IBObjects e depois no campo Selecione o filtro desejado informe a versão do Delphi que você está utilizando. Agora clique no botão Pesquisar, fazendo isto você terá acesso ao arquivo e basta clicar sobre o seu nome e fazer o download. Caso você não queira baixar o arquivo através da nossa página, você poderá acessar diretamente a página do fabricante no endereço www.IBObjects.com. Como já foi dito na edição anterior desta revista, a licença para utilização do IBObjects é Trustware. Isso significa que você pode utilizar o componente sem precisar a principio pagar por ele. Eu digo a principio porque o autor do componente diz que você poderá pagar pelo componente a partir do momento que você começar a ganhar algum dinheiro com o software que você desenvolveu utilizando o IBObjects. 6 Instalando o IBObjects O arquivo IBO4.zip contém quase uma instalação completa do IBObjects versão 4, incluindo muitos componentes com código fonte, pacotes com todas as versões e vários exemplos e também códigos com boas contribuições que foram enviadas por membros da comunidade IBO. Além deste arquivo você tem um outro arquivo para cada versão do Delphi, que lhe dará uma instalação completa sem limite de tempo. Os arquivo estão nomeados como Dn_DCU.zip, onde n é a versão do Delphi. Por exemplo, para o Delphi 7 o arquivo é D7_DCU.zip. Antes de instalar o componente você deve seguir alguns passos: 1 – Desinstale qualquer versão do IBObjects anterior a esta versão. 2 – Delete todos os arquivos .BPL do IBObjects desta versão mais antiga. 3 – Delete o diretório onde você descompactou a versão mais antiga do IBObjects. Pronto, agora você já pode descompactar o arquivo .ZIP da nova versão do IBObjects. Para facilitar a instalação é recomendado que você crie um diretório chamado IBO4 a partir da raiz do seu drive desejado. Aqui nós vamos criar no drive C. Então ficará assim, C:\IBO4. Você deve também descompactar o MeGAZINE Delphi outro arquivo baixado de acordo com a versão do seu Delphi e copiar o seu conteúdo para o diretório C:\IBO4. Ordem dos pacotes Uma observação para os usuários do Delphi 7. Os arquivos .DPK do Delphi 7 estão sem o sufixo _D7, por ser a versão mais recente do Delphi. Os pacotes do Delphi 7 estão nomeados como “IBO40*RT.dpk” e “IBO40*DT.dpk”. Veja abaixo a lista de pacotes na ordem que eles foram construídos. O “n” representa o numero da versão do seu Delphi, exceto nos pacotes do Delphi 7. Runtime packages: * * * * * * * IBO40CRT_Dn.dpk (Delphi 7 IBO40CRT.dpk) IBO40TRT_Dn.dpk IBO40VRT_Dn.dpk IBO40FRT_Dn.dpk IBO40XRT_Dn.dpk IBO40WRT_Dn.dpk IBO40WXRT_Dn.dpk Design-time packages: * * * * * * * * * IBO40CDT_Dn.dpk (Delphi 7 IBO40CDT.dpk) IBO40ADT_Dn.dpk IBO40TDT_Dn.dpk IBO40VDT_Dn.dpk IBO40FDT_Dn.dpk IBO40XDT_Dn.dpk IBO40WDT_Dn.dpk IBO40WXDT_Dn.dpk IBO40EDT_Dn.dpk definiu outro caminho, então os arquivos compilados serão gerados neste caminho que você definiu. Você deve informar ao Delphi onde estão localizados os arquivos fontes e as bibliotecas do IBObjects. Para isto então, vá até o menu do Delphi na opção Tools | Environment Options, clique na pasta Library. Informe o diretório nos campos Library Path e Browsing Path. Caso contrário você não conseguirá compilar as suas aplicações utilizando o IBObjects. Instalando no Delphi 3 1 – Feche todos os arquivos que estejam abertos na IDE do Delphi. 2 – Abra cada arquivo .DPK. Para isto vá até o menu do Delphi, clique em File | Open. Clique sobre o primeiro arquivo mencionado na lista anterior e clique no botão Compile para compilar o pacote e depois clique no botão Install para instalar o componente. Usando o arquivo .BPG A partir da versão do Delphi 4 surgiram os arquivos .BPG (Grupo de projeto) que são os arquivos IBO40_Dn.bpg ou IBO40_Cn.bpg. O arquivo .BPG lhe auxilia a compilar todos os pacotes de uma só vez. 1 - Vá até o menu do Delphi e clique em File | Open. Selecione o arquivo IBO40_Dn.bpg. Lembrando sempre que você deve substituir o n pela versão do seu Delphi, exceto para o Delphi 7. 2 – Volte ao menu do Delphi e clique em View | Project Manager. 3 – Clique com o botão direito do mouse sobre qualquer arquivo .BPL e selecione a opção Open. Runtime and Design time packages: 4 – Agora volte ao menu do Delphi clique em Project | Build All Projects. Isto fará com que todos os pacotes sejam recompilados. * IBO40RPL_Dn.dpk * IBO40FTS_Dn.dpk Não tente instalar nenhum pacote que não faça parte da versão do seu Delphi. Instalando os pacotes No diretório onde você descompactou os arquivos você encontrará alguns arquivos .BPG ( Grupo de projetos ) que instalarão os pacotes compilados no diretório $(DELPHI)\Projects\BPL\ a menos que você tenha definido outro caminho de saída para os arquivos compilados. Se você 5 – Agora clique com botão direito do mouse em cada pacote que termine com as letras DT e clique em Install. 6 – Agora faça o mesmo passo anterior para os arquivos que terminam com as letras FTS e RPL. Não há necessidade de fazer isto com os arquivos terminados com RT, pois estes arquivos devem ser apenas compilados e isto nós já fizemos no passo 4. MeGAZINE Aviso importante aos usuários do Delphi 4. Um erro pode ocorrer na etapa de instalação. Os arquivos 7 Delphi .BPL podem não serem encontrados a menos que você informe o caminho. Caso isto aconteça faça o seguinte: 1 – Vá até o menu do Delphi em Components | Install Packages. 4 – Salve o arquivo. Agora você deverá encontrar o Service Application Framework pronto para ser utilizado em suas aplicações. 5 – Use como qualquer outro objeto que você cria no Delphi usando File | New .... 2 – Para cada pacote faça o seguinte: Pronto, agora o seu componente já está instalado. 2.a – Clique no botão Add 2.b – Vá até o arquivo onde estão os arquivos .BPL. (o padrão é $(DELPHI)\Projects\bpl) e selecione cada pacote individualmente, (IBO4_*DT_D4.bpl). Agora nós vamos ver algumas informações básicas sobre os vários componentes incluídos no IBObjects. O objetivo agora é dar uma introdução em algumas propriedades comuns e as configurações necessárias para se utilizar este componente. Estes passos deverão instalar o componente para você. Componentes de Acesso a Dados Mais uma dica para os usuários do Delphi 4. Se você ainda estiver com problemas na instalação deste componente no Delphi 4, tente copiar os arquivos .BPL para o diretório de sistema do Windows, por exemplo C:\Windows\System32. Você pode instalar o Service Application Framework (ibs_base.pas) no repositório do Delphi. Para fazer isto, siga os passos: 1 – No diretório onde foi instalado o seu Delphi você encontrará o subdiretório ..\Bin. Neste diretório você encontrará o arquivo delphi32.dro. Abra este arquivo utilizando o NotePad. 2 – Vá até o final do arquivo e acrescente o seguinte: [C:\IBO4\IBS_BASE] <- ou onde você instalou o arquivo ibs_base.pas Type=FormTemplate Name=IB Service Base Module Page=IB Objects Icon=C:\IBO4\IB_SVCAPP.ICO // ou onde você instalou o IBO Description=Base module for IB/Firebird Service apps Author=Jason Wharton DefaultMainForm=0 DefaultNewForm=0 Ancestor= 3 – Agora volte ao inicio do arquivo e adicione uma entrada para [Repository Pages]: IB Objects= Alguns conceitos básicos do IBObjects Os componentes de acesso a dados do IBO trabalham mais ou menos da mesma forma que os componentes básicos de acesso a dados do Delphi. Tradicionalmente utilizasse um banco de dados, um dataset e um datasource. No IBO é a mesma coisa. Porém, com bancos SQL, como Interbase/FireBird existe também um fator adicional que devemos considerar que são as Transações. Antes toda a referência a transações ficava dentro do componente TDatabase do BDE. Ele usa uma conexão e uma transação envolvidas em uma única relação. Mas o InterBase/ FireBird não está limitado a apenas uma única transação por conexão. Por causa do Interbase/Firebird ter a capacidade de ter múltiplas transações simultâneas em cada conexão, o IBO foi desenhado para implementar transações em um componente separado. Desta forma, ele é capaz de ter um componente de conexão e vários componentes de transação ligados a ele ao mesmo tempo. Outra característica significante do Interbase/ Firebird é que ele suporta que uma única transação utilize mais de um banco de dados. Portanto foi necessário projetar o IBO para controlar o caso onde uma transação se referencia a múltiplas conexões simultaneamente. Com o IBO você tira proveito disto de uma maneira muito fácil, basta utilizar as propriedades IB_Connection1 e IB_Connection2 do componente TIB_Transaction. Se você tiver mais de três transações, será necessário então utilizar o método AddConnection() para adicionar quantas conexões forem necessárias. Portanto, um dataset não referencia a apenas um database com o IBO. Ele deve também fazer referência a uma transação. Isto é que determina o contexto de dados que são lidos e escritos. Tanto uma instrução quanto um dataset no IBO possuem as propriedades Database e Transaction. Elas são IB_Connection e IB_Transaction. O objetivo é tornar as coisas mais flexíveis. Conexão Padrão Se uma referência da conexão não for fornecida em uma 8 MeGAZINE Delphi instrução ou em um dataset então ele verificará com a sessão default e verá se existe uma conexão default presente. Se existir então alinha automaticamente a essa conexão. Isto é tipicamente o primeiro componente da conexão criado na aplicação. Se existir apenas uma única conexão para a aplicação inteira não é necessário prestar muita atenção nisto visto que o IBO automaticamente configura isto para você. Transação Padrão Se uma referência da transação não for fornecida em uma instrução ou em um dataset então uma transação interna será gerada automaticamente. Isto dá uma isolação que permite que você veja o estado “Committed” da base de dados em todas as vezes e o auto-commit será executado em todas as mudanças que forem postadas. Desta forma, se você projetar uma aplicação sem usar a camada de transação ela irá funcionar da mesma forma. Os datasets se comportam como se não existisse o conceito de transações. Modo Normal Uma instrução ou Query normalmente aponta suas propriedades IB_Connection e IB_Transaction para os componentes correspondentes. A propriedade IB_Connection pode conectar a um componente TIB_Connection, TIB_Database, TIBODatabase ou qualquer outra subclasse derivada deles. A propriedade IB_Transaction conecta ao componente TIB_Transaction ou qualquer outro de sua subclasse. TIB_DataSource que funcionasse quase igual ao TDataSource. Ele se referencia a um TIB_Dataset que poderia ser um componente TIB_Cursor ou TIB_Query. Se você quer saber o porque disto, é porque o IBO foi iniciado muito tempo antes do Delphi 3 e da classe virtual TDataset introduzida por ele. Portanto a única forma foi construir a funcionalidade necessária para tirar todo o proveito do InterBase/ FireBird. Sendo assim foi escrita uma camada de acesso a dados própria e também controles baseados nas APIs do InterBase e nas classes TComponent, TWinControl, TCustomEdit, etc. Classes TIBO* para compatibilidade com a VCL Desde que o Delphi 3 introduziu a classe virtual TDataset, foi necessário escrever os componentes TIBODatabase, TIBOTable, TIBOQuery e TIBOStoredProc para se igualarem aos componentes de acesso a dados padrões do Delphi e assim ter compatibilidade para todos os controles padrões da VCL. Isso foi possível escrevendo uma camada derivada de TDataset que faz a ligação com uma instância interna do TIB_Query. Isto dá habilidade ao IBO para ser usado com geradores de relatórios como Report Printer Pro, Quick Report e outros. Os controle que acompanham o IBO não são compatíveis com os controles padrões da VCL. Para continuar a usar os controles da VCL e controles de terceiros que são compatíveis com a VCL, escolha os componentes de acesso a dados iniciados com TIBO* e o TDataSource padrão do Delphi. Emulando o BDE As Classes TIB_* O componente TIBODatabase foi projetado para fazer o que o componente TDatabase do BDE faz. Ele combina a conexão com uma transação interna e por default faz com que todas as instruções e datasets a ele ligados compartilhem da mesma transação. Ele ainda permite que você arraste para o seu form um componente TIBOTransaction e configure-o se desejado. Isto faz a emulação e ao mesmo tempo as transações adicionais podem explicitamente ser introduzidas. Obs: Os componentes TIB_Database e TIBODatabase trabalham muito bem quando cached update é o seu principal modo de operação. Conectando controles Data-Aware O IBO permite o mesmo tipo de conectividade a seus controles visuais que a encontrada nos controles padrões do Delphi. Cada controle terá uma propriedade DataSource e uma DataField, se for o caso. Por causa dos controles IBO não serem compatíveis aos controles padrões do Delphi, foi necessário criar um componente Os controles nativos do IBO podem ser usados apenas com componentes de acesso a dados também nativos do IBO, como por exemplo: TIB_DataSource, TIB_Cursor, TIB_Query e TIB_StoredProc. As Classes TIB_xxxxxSource Os componentes StatementSource, ConnectionSource e TransactionSource foram desenvolvidos com o propósito de fazer os formulários ou outros controles cientes de instruções, conexões ou transações específicadas, respectivamente. Você pode observar esses componentes em funcionamento olhando os controles TIB_StatementBar, TIB_ConnectionBar ou TIB_TransactionBar. Você pode desenvolver formulários sem dependência de conexões ou transações. Em run-time você pode fazer uma linkagem rápida a um contexto de transação e conexão simplesmente se referenciando aos componentes ConnectionSource e TransactionSource no formulário. Nos eventos AfterAssignment você tem uma rotina que replica essa referência a todos os datasets no formulário. MeGAZINE 9 Delphi Focando o Controle Global Todos os controles de acesso a dados nativos do IBO são coordenados com um outro componente de sessão, ao qual pertencem. Uma rede de eventos fica monitorando constantemente quem tem o foco atual, juntamente com qual declaração SQL, Dataset, Datasource, Transação e Conexão que estão ligados a ele. Isso permite os controles como as barras de botões possam ficar escutando essa rede de eventos e esperando por eventos que decidam qual o Dataset tem o foco no momento. O mesmo acontece para Transações e Conexões. Isso é possível configurando as propriedades AnnounceFocus e AllowFocus do componente TIB_DataSource e a propriedade ReceiveFocus de vários controles que podem ter foco. Os exemplos disto são as várias barras com controles, como por exemplo, o TIB_Navigator e a TIB_SearchBar. Resumindo aplicação. Por acessar os objetos IB_Connection e IB_Transaction através das propriedades encontradas no TIBODataset, é possível utilizar a mesma conexão e transação em aplicações híbridas. Isso se torna especialmente útil quando você está fazendo uma migração de um aplicativo baseado na BDE para um baseado em controles nativos do IBO. Finalizando Você poderá encontrar diversos exemplos documentados no subdiretório ..\Samples onde você instalou o seu componente que mostram como utilizar os diversos componentes do IBObjetcs. Todo o texto desta matéria foi traduzido e adaptado a partir das informações disponíveis no site do fabricante, www.ibobjects.com. Sobre o autor Sintam-se a vontade para fazer uma mistura de componentes como por exemplo utilizar o TIBO* mais os controles VCL e os TIB_* mais controles nativos do IBO na mesma 10 Claudinei Rodrigues, Consultor Técnico do The Club - [email protected] MeGAZINE Delphi por André Colavite – Suporte THE CLUB [email protected] Nesta matéria iremos indicar os passos para criar um relacionamento Master/Detail com DBExpress usando o banco de dados FireBird/Interbase. DESCRICAO VARCHAR (20) CHARACTER SET WIN1252 COLLATE PXW_INTL850, VALOR NUMERIC (18, 2)); ALTER TABLE DETALHE ADD CONSTRAINT PK_DETALHE PRIMARY KEY (CODDETALHE); Para montar o relacionamento Master/Detail primeiramente iremos verificar a estrutura de nossas tabelas, criadas dentro do banco GDB. ALTER TABLE DETALHE ADD CONSTRAINT FK_DETALHE FOREIGN KEY (CODMASTER) REFERENCES MASTER (CODMASTER) ON DELETE CASCADE; A tabela pai chamaremos de MASTER e será composta pela seguinte estrutura: CREATE TRIGGER AI_DETALHE_CODDETALHE FOR DETALHE ACTIVE BEFORE INSERT POSITION 0 AS BEGIN IF (NEW.CODDETALHE IS NULL) THEN NEW.CODDETALHE = GEN_ID(DETALHE_CODDETALHE_GEN, 1); END CREATE TABLE MASTER ( CODMASTER INTEGER NOT NULL, NOME VARCHAR (20) CHARACTER SET WIN1252 COLLATE PXW_INTL850); ALTER TABLE MASTER ADD CONSTRAINT PK_MASTER PRIMARY KEY (CODMASTER); CREATE TRIGGER AI_MASTER_CODMASTER FOR MASTER ACTIVE BEFORE INSERT POSITION 0 AS BEGIN IF (NEW.CODMASTER IS NULL) THEN NEW.CODMASTER = GEN_ID(MASTER_CODMASTER_GEN, 1); END A tabela filha chamaremos de DETALHE e será composta pela seguinte estrutura: CREATE TABLE DETALHE ( CODDETALHE INTEGER NOT NULL, CODMASTER INTEGER, Conhecendo a estrutura das tabelas podemos começar a criar o projeto com o relacionamento, e para isso Dica importante: começaremos pela A definição do nome dos campos visualização da tabela idênticos nas duas tabelas foi Master. Visualizando os registros da tabela Master Primeiramente vamos criar um novo projeto, contendo um form e um MeGAZINE 11 feito para que possamos utilizar um recurso do DBExpress ao qual no momento da inclusão do registro Detalhe o código da tabela Master será enviado automaticamente para o campo da tabela Detalhe, e o DBExpress envia para o campo que tenha o mesmo nome. Delphi Veja que na instrução de SELECT coloquei uma cláusula de WHERE com um parâmetro ao qual será utilizada para fazer o relacionamento com a tabela Master, sendo assim esse parâmetro deve ter o mesmo nome do campo da tabela Dica importante: Master. datamodule e neste datamodule colocaremos o componente SQLConnection. Neste componente faça a conexão normal ao banco de dados FireBird/Interbase. Coloque um novo componente do tipo SQLDataSet e na sua propriedade Name especifique o nome SQLMaster, logo em seguida ligue-o com o componente SQLConnection através da sua propriedade SQLConnection. Na propriedade CommandText deste componente SQLMaster iremos especificar a instrução de SELECT que permitirá visualizar os dados da tabela Master. Veja a instrução de SELECT que usaremos: SELECT * FROM MASTER Evite usar um Select trazendo todos os registros, pois quanto mais registros você trouxer na visualização mais lento ficará a abertura da tabela. Portanto recomendamos que sempre utilize uma clausula de Where para filtrar os dados a serem visualizados, podendo até na abertura do form fazer uma clausula de where com o campo código IS NULL, pois assim não irá visualizar registros, deixando o processo bastante rápido. O Select da tabela Master com essa clausula de Where ficará da seguinte forma: SELECT * FROM MASTER WHERE CODMASTER IS NULL O próximo componente que iremos colocar é o DataSetProvider, aba Data Access, com o nome DataSetProvider1 ao qual será ligado ao componente SQLMaster através de sua propriedade DataSet. E para finalizar a visualização dos dados da tabela Master iremos colocar o componente ClientDataSet, aba Data Access, ao qual ligaremos ao DataSetProvider1 através da sua propriedade ProviderName. A esse ClientDataSet daremos o nome de CDSMaster. Pronto, até aqui criamos uma visualização de dados simples usando o DBExpress, a partir desse momento começaremos a criar o relacionamento Master/Detail e agora mudará um pouco a forma de trabalhar. Visualizando os registros da tabela Detalhe Em seguida temos que ligar esse SQLDetalhe ao SQLMaster e faremos isso através da propriedade DataSource do componente SQLDetalhe, portanto temos que colocar no datamodule um componente DataSource, ao qual daremos o nome de DSMaster, e em sua propriedade DataSet ligaremos o componente SQLMaster. Agora podemos indicar na propriedade DataSource do componente SQLDetalhe o componente DSMaster que acabamos de configurar. Um fato importante na visualização dos dados da tabela Detalhe é que não usaremos um novo componente DataSetProvider, pois os dados serão controlados diretamente pelo provider da tabela Master. Sendo assim voltaremos o foco para os componentes SQLMaster e CDSMaster para preparar essa visualização dos dados da Detalhe. Mais adiante explicaremos com detalhes a utilização da propriedade ProviderFlags. Neste componente SQLMaster iremos criar a lista de tFields, para isso dê duplo click sobre o componente, onde será aberta uma lista em branco, pressione o botão direito do mouse sobre essa lista e selecione a opção Dica importante: Add All fields. Faça o mesmo para o Adicionamos os TFields no SQLMaster componente CDSMaster pois quando o Provider montar a instrução de Insert, Update ou Delete, a ser enviada ao banco de dados, ele pega as configurações dos TFields do SQLDataSet para saber quais campos serão usados na instrução. E assim podemos controlar a criação dessas instruções através da propriedade ProviderFlags de cada tField. Mais adiante explicaremos com detalhes a utilização da propriedade ProviderFlags. Para visualizar os dados do Detalhe usaremos um novo componente SQLDataSet e daremos o nome de SQLDetalhe, A esse componente ligaremos o componente SQLConnection1 através da sua propriedade SQLConnection. Em sua propriedade CommandText coloque a instrução de Select para visualizar os dados da tabela Detalhe, ficando a instrução da seguinte forma: SELECT * FROM DETALHE WHERE DETALHE.CODMASTER = :CODMASTER 12 Ao adicionar os TFields no CDSMaster podemos verificar que foi criado o campo do tipo DataSetField, com o nome SQLDetalhe. Esse campo irá trazer as informações da tabela Detalhe, por esse motivo que não utilizaremos um componente DataSetProvider para a tabela DETALHE. Em seguida colocaremos um componente do tipo ClientDataSet para visualizar os dados do Detalhe e em sua propriedade Name coloque o nome CDSDetalhe. Como não utilizaremos um DataSetProvider para a Detalhe então a ligação do CDSDetalhe será feita através da sua propriedade DataSetField, ao qual selecionaremos o tField SQLDetalhe, criado no CDSMaster. O nome apresentado na MeGAZINE Delphi ligação da propriedade DataSetField deve ser parecido com CDSMasterSQLDetalhe. Reconcile Error Dialog, conforme figura 2, e em seguida pressione Ok. No próximo passo devemos adicionar os Tfields nos componentes SQLDetalhe e CDSDetalhe, sendo assim dê duplo click sobre o componente SQLDetalhe, pressione o botão direito do mouse sobre a lista e selecione a opção Add All fields. Faça o mesmo para o componente CDSDetalhe. Agora, dentro do evento onReconcileError do CDSMaster escreva a instrução, conforme o exemplo a seguir: procedure TDM.CDSMasterReconcileError(DataSet: TCustomClientDataSet; E: EReconcileError; UpdateKind: TUpdateKind; var Action: TReconcileAction); begin Action := HandleReconcileError(DataSet, UpdateKind, E); end; Para finalizar as instruções do DataModule, criaremos o evento onReconcilieError do CDSMaster pois através deste evento iremos tratar os erros gerados no banco de dados durante a atualização dos dados. Para tratarmos este erro iremos utilizar um form padrão do Delphi e para criá-lo faça os seguintes passos: No menu do Delphi selecione a opção File / New depois a opção Other, onde será aberto o form New Items do Delphi. Neste form selecione a aba Dialogs e dentro dessa aba selecione a opção Em outra ocasião explicaremos com detalhes o tratamento de erro utilizado com o ClientDataSet. Pronto, neste momento já estamos com o relacionamento Master/Detail concluido, basta agora visualizar os registros no form, e para isso colocaremos dois componentes Datasources, DataSource1 e DataSource2 e ligaremos esses dois componentes aos ClientDataSets, CDSMaster e CDSDetalhe respectivamente. O DataModule ao final das configurações dos componentes ficará conforme a figura 1. Criando o Form para apresentação dos dados Esta parte do projeto é bastante simples, basta criarmos o forms para visualizar os dados e para isso colocaremos os DBEdits para visualizar os campos da tabela Master e um DBGrid para visualizar os registros da tabela Detalhe. Não se esqueça de colocar o DBNavigator para controlar os registros tanto da tabela Master quando da Detalhe. Figura 2: Criando o form ReconcileErrorForm no projeto Como último detalhe do form, colocaremos um componente Button ao qual será utilizado para executar a instrução de ApllyUpdates do ClientDataSet, pois como já sabemos toda atualização de dados realizada no ClientDataSet ficam em sua memória e somente será enviada ao banco de dados após executarmos o método ApplyUpdates. No evento onclick do Button colocaremos a instrução do ApplyUpdates com o parametro 0, pois assim qualquer erro ocorrido todo o processo será cancelado e nenhum registro será gravado no banco. Veja o Figura 1: DataModule MeGAZINE 13 Delphi exemplo a seguir: procedure TForm1.Btn_GravaClick(Sender: TObject); begin DM.CDSMaster.ApplyUpdates(0); end; Em outra oportunidade explicaremos melhor a utilização do ApplyUpdates. DataSetProvider. Pois essas duas propriedades em conjunto fornecem informações ao Provider para a geração das instruções de Insert, Update e Delete a serem enviadas para o banco de dados. Essas instruções são geradas automaticamente no momento que executarmos a instrução de ApplyUpdates no ClientDataSet. A propriedade ProviderFlags contém os valores pfInUpdate, pfInWhere, pfInKey e pfHidden. A propriedade UpdateMode indica a forma como serão pesquisados os registros no momento da atualização dos dados e contêm os valores upWhereAll, upWhereChanged e upWhereKeyOnly. A seguir iremos verificar como utilizar os valores dessas duas propriedades, ProviderFlags e UpdateMode: pfInUpdate : Indica se o campo poderá enviar valores para o banco de dados, através do Insert e Update; pfInWhere : Indica se o campo será utilizado na cláusula Where para encontrar o registro que está sendo atualizado, isso quando o UpdateMode estiver com o valor upWhereAll ou upWhereChanged. Figura 3: Form de visualização dos dados da tabela Master e da tabela Detalhe Controlando a atualização dos dados no DBExpress pfInKey : Indica que o campo é um campo chave primária e será utilizado para encontrar o registro que está sendo atualizado, isso quando o UpdateMode estiver com o valor upWhereKeyOnly. pfHidden : Indica que a coluna será utilizada somente para a localização dos registros. Essas configurações que iremos realizar agora são muito importantes, pois serão utilizadas em qualquer situação com o DBExpress, tanto numa conexão a uma simples tabela quanto para a utilização num relacionamento Master/Detail. A configuração que normalmente utilizo é com a propriedade UpdateMode igual a upWhereKeyOnly, e como o ProviderFlags é configurado em cada tField a seguir vou especificar como ficarão todos os campos do projeto de exemplo. Através dessas configurações iremos controlar quais campos serão atualizados dentro do Delphi e quais campos serão indicados na cláusula Where das instruções de Update e Delete enviadas para o banco de dados. Componente SQLMaster: Campo CODMASTER - deixe como True somente o pfInKey Campo NOME - deixe como True somente o pfInUpdate A nossa configuração será feita na propriedade ProviderFlags de cada tField do SQLDataSet e na propriedade UpdateMode do Componente SQLDetalhe: Campo CODDETALHE - deixe como True somente o valor 14 MeGAZINE Delphi pode ser gravado em branco, mas esse campo é incremental e seu valor é gerado pelo Generator através de uma Trigger. Sendo assim não permitiremos que o usuário digite qualquer valor neste campo CODMASTER. Portanto a solução para resolver este pequeno detalhe é abrir a lista de Tfields dos componentes SQLMaster e CDSMaster, selecionar o campo CODMASTER e configurar a sua propriedade Required com o valor FALSE, para que assim não seja obrigatório o seu preenchimento. Esse mesmo detalhe deverá ser observado para o campo CODDETALHE da tabela Detalhe e sua solução também é a mesma, mas deverá ser feita nos componentes SQLDetalhe e CDSDetalhe. Conclusão Neste artigo podemos observar os passos para montar um relacionamente Master/Detail com o DBExpress bem como configurar a propriedade ProviderFlags de cada campo. Figura 4: Configurando ProviderFlags do SQLMaster pfInKey Campo CODMASTER - deixe como True somente o valor pfInUpdate Campo DESCRICAO - deixe como True somente o valor pfInUpdate Campo VALOR - deixe como True somente o valor pfInUpdate Espero ter lhes ajudado e caso tenham alguma dúvida sobre a matéria ou sugestão sobre outras matérias a serem publicadas favor enviá-las ao suporte THE CLUB. Um grande abraço a todos. O projeto de exemplo referente a essa matéria está disponível em nosso site: http://www.theclub.com.br/revista/download/ DBXMasterDetail.zip Veja na Figura 4 a configuração do ProviderFlags do SQLMaster. Dica importante: O ProviderFlags dos tFields serão configurados somente no componente SQLDataSet (SQLMaster e SQLDetalhe), pois o Provider busca informações somente desses tFields. O ProviderFlags existente nos tFields do ClientDataSet não são utilizados pelo Provider. Com essas configurações as instruções de Update e Delete geradas pelo Provider conterão em sua cláusula WHERE somente o campo indicado como chave primária, isso é, o campo CODMASTER para a tabela Master e CODDETALHE para a tabela Detalhe. E os demais campos somente mandarão os valores para o banco de dados. Uma última configuração nos tFields do projeto Se executarmos o projeto agora ele irá funcionar corretamente, mas ao incluir um registro apresentará uma mensagem de que o campo CODMASTER é obrigatório e não MeGAZINE Sobre o autor André Colavite, Consultor Técnico do The Club [email protected] 15 Delphi Criando Instalações Personalizadas com Inno Setup Instalando sua aplicação com o banco de dados embutido por Alessandro Ferreira, [email protected] Introdução A distribuição de aplicações é uma tarefa as vezes não muito simples devido a complexidade e falta de versatilidade de muitas ferramentas existentes no mercado. Porém, estamos aqui para apresentar-lhes uma ferramenta muito legal para criar instalações personalizadas e contanto ainda com uma grande vantagem entre as demais: “É gratuíta!”. Estou falando do Inno Setup, que foi desenvolvido por Jordan Russel e está disponível para download em www.innosetup.com, onde atualmente está na versão 4.0.5 beta, porém, neste artigo utilizamos a versão 3.0.6. no decorrer deste aplicativo, onde iremos demonstrar a instalação de uma aplicação desenvolvida em Delphi, acessando banco de dados Firebird via dbExpress. Criando nossa primeira instalação Bom, estando com o Inno Setup previamente instalado, vamos criar nossa primeira instalação. Abra o Inno Setup e será apresentada uma tela semelhante a Figura 1, na qual poderá selecionar um wizard para criar nossa instalação. Como funciona? O Inno Setup é baseado em scripts com uma estrutura que lembra arquivos INI, possuindo sessões e dentro das sessões chaves que irão receber e fornecer valores para gerar a instação. Veja um pequeno exemplo na Listagem 1: [Files] Source: C:\Projetos\SisMed.exe; DestDir: {app}\Exec; Flags: confirmoverwrite Source: C:\Projetos\DADOS\DEFAULT.DAT; DestDir: {app}\Dados Listagem 1 Tudo é feito com base em scripts como mostrado na Listagem 1. Não pense que pela forma simples de trabalhar, o Inno Setup deixe algo a desejar em relação aos demais instaladores, e sim, muito pelo contrário! Você pode criar instalações para praticamente qualquer aplicativo que desejar, como poderá ver 16 Figura 1 – Tela inicial do Inno Setup. MeGAZINE Delphi Dando continuidade, você irá deparar-se com uma tela semelhante a Figura 2, na qual deverá preencher as opções apresentadas. Neste primeiro exemplo, estamos configurando as opções para o NotePad, mas você poderá adicionar informações do seu aplicativo. O próximo passo é informar onde nosso aplicativo deverá ser instalado, bastando aceitar a opção sugerida “Program Files directory”, que corresponde a pasta “Arquivos de Programas”. Em “Application directory name” você poderá definir o nome Figura 2 – Opções requisitadas pelo wizard. da pasta (dentro de Arquivos de Programas) que será criada para disponibilizar os arquivos de sua aplicação e ainda permitir ou não ao usuário alterar estas configurações, através da opção “Allow user to change the application directory”, veja a Figura 3. Após definido onde os arquivos de nossa aplicação serão disponibilizados, iremos selecionar os arquivos que deverão ser embutidos em nossa instalação, como exemplo o EXE de nosso aplicativo, as DLLs necessárias para a distribuição do dbExpress, nosso banco de dados, enfim, tudo que for necessário para nosso aplicativo rodar na máquina do cliente, conforme mostrado na Figura 4. (página seguinte) Figura 3 – Configuração da pasta para instalação. MeGAZINE 17 Delphi Figura 4 – Arquivos necessários Figura 7 – Configuração dos textos de licença. Dica: Você pode definir os locais onde os arquivos deverão ser instalados, bastando para isso clicar no botão “Edit” e configurar, como mostra a Figura 5: O próximo passo será a definição das configurações do atalho para nosso aplicativo, onde podemos configurar algumas ações que o usuário terá acesso, como exemplo, permitir criar ícone no desktop, veja a Figura 6: Figura 5 – Configuração de localização de arquivos. E finalizando, temos ainda a opção de adicionar arquivos de licença (arquivos texto) contendo o texto que será apresentado ao usuário no momento da instalação, bastando para isso selecionar o arquivo no disco que o mesmo será embutido na instalação, Figura 7. Bem, agora bastará finalizar e salvar nosso projeto de instalação e o resultado final deverá ser parecido com o exibido na Listagem 2: ; Script generated by the Inno Setup Script Wizard. ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING ; INNO SETUP SCRIPT FILES! [Setup] AppName=NotePad AppVerName=NotePad XP AppPublisher=Microsoft AppPublisherURL=http://www.microsoft.com AppSupportURL=http://www.microsoft.com AppUpdatesURL=http://www.microsoft.com DefaultDirName={pf}\NotePad DefaultGroupName=NotePad LicenseFile=C:\leiame.txt InfoBeforeFile=C:\leiame.txt InfoAfterFile=C:\leiame.txt [Tasks] Figura 6 – Configurações de atalho. 18 MeGAZINE Delphi a apresentada na Figura 8: Name: “desktopicon”; Description: “Create a &desktop icon”; GroupDescription: “Additional icons:” [Files] Source: “C:\WINDOWS\system32\notepad.exe”; DestDir: “{app}”; Flags: ignoreversion Source: “C:\Program Files\Borland\ Delphi7\Bin\dbexpint.dll”; DestDir: “{app}”; Flags: ignoreversion Source: “C:\WINDOWS\system32\midas.dll”; DestDir: “{sys}”; Flags: ignoreversion ; NOTE: Don’t use “Flags: ignoreversion” ; on any shared system files [Icons] Name: “{group}\NotePad”; Filename: “{app}\notepad.exe” Name: “{userdesktop}\NotePad”; Filename: “{app}\notepad.exe”; Tasks: desktopicon Figura 8 – script aberto via IsTool [Run] Filename: “{app}\notepad.exe”; Description: “Launch NotePad”; Flags: nowait postinstall skipifsilent Listagem 2 – Script gerado em nossa primeira instalação. Para concluir a instalação, você deverá agora compilar o script afim de gerar o arquivo de instalação “Setup.exe”, que geralmente é gerado em uma sub-pasta \Output, dentro da pasta onde você salvou o script. Pronto, agora você já poderá testar a instalação, bastando executar o setup gerado. Ferramentas adicionais Como você pode observar, utilizando o wizard do Inno Setup, é bastante simples criar uma instalação, porém, algumas manutenções futuras talvez exijam que você altere diretamente o script, o que para alguns usuários pode ser incômodo, além de ser menos produtivo e intuitivo. Felizmente, alguém já pensou nisso e criou uma ferramenta chamada IsTool, a qual tem a finalidade de gerar scripts para o Inno Setup, porém, através de uma interface gráfica bastante simples e intuitiva, o irá permite aproveitar melhor todos os recursos que o Inno Setup oferece. A IsTool, assim como o Inno Setup também é uma ferramenta gratuíta e poderá ser encontrada em http://www.istool.org/ e um detalhe interessante é que esta ferramenta permite a escolha do idioma no momento da instalação, tendo suporte ao português. Para testar a IsTool, você poderá abrir o script (.iss) que geramos anteriormente via Inno Setup, e verá uma interface semelhante Traduzindo a instalação Como você pode ver, a instalação gerada é em inglês, porém, isso é um incoveniente, visto a maioria dos usuários finais não estarem habituados a trabalhar neste idioma. Contudo, você poderá baixar um script traduzido para português no seguinte endereço: http://www.jrsoftware.org/files/istrans/. Certifique-se de baixar a versão correspondente a versão do Inno Setup que você possui instalado em sua máquina. Após baixar o arquivo, para traduzir o Inno Setup para português, coloque o arquivo baixado na pasta do programa e renomeie-o para Default.isl (tomando o cuidado de fazer um backup do original), e pronto, as instalações serão geradas em português. Embutindo o Firebird na instalação Um grande problema quando trabalhamos como bancos de dados Client/Server, é que necessitamos fazer a instalação e configuração dos mesmos, o que em muitos casos exige deslocar um profissional apenas para isso. Neste exemplo, vamos demonstrar como embutir o Firebird 1.0.3 (www.ibphoenix.com) juntamente com nosso aplicativo, fazendo a instalação, o registro e a inicialização do mesmo na máquina do cliente... Abra o IsTool e mãos a obra. Primeiramente, crie um novo projeto de instalação e salve o mesmo. Após isso, clique na opção “Options” no IsTool e faça as configurações de nosso aplicativo exemplo, preenchendo MeGAZINE 19 Delphi as solicitações, semelhante ao que já fizemos no início deste artigo. Após isso, vamos para as definições de pastas e arquivos necessários para nosso aplicativo e o Firebird serem instalados corretamente, veja na Figura 9: Figura 9 – Pastas para a instalação da aplicação e do Firebird. Dica: Para criar pastas, apenas clique com o botão direito e selecione “Criar Diretório”, veja a Figura 10: Figura 10 – Opções. Figura 11 - Arquivos Crie as seguintes pastas (Listagem 3): [Dirs] Name: {app}\Exec Name: {app}\Dados Name: {pf}\Firebird\bin Name: {pf}\Firebird\intl Name: {pf}\Firebird\udf Listagem 3 – Pastas. Observe que existem constantes que identificam as pastas padrões, tanto para a aplicação, quanto para as pastas do Windows, sendo: {app} = pasta padrão onde a aplicação será instalada {pf} = program files ou arquivos de programas Agora vamos selecionar os arquivos à serem instalados, bastando para isso clicar com o botão direito e selecionar a opção “Inserir Arquivos”. Confira os arquivos na Figura 11: 20 Observe que na coluna DestDir está configurado “onde” os arquivos deverão ser instalados. No caso do Firebird, é importante manter estes paths, visto existirem chaves no registro do Windows que irá buscar estes arquivos. Bem, após selecionar os arquivos, vamos criar o grupo e o ícone de nosso aplicativo exemplo, para isso estando na sessão “Ícones”, com o botão direito selecione “Novo ‘Item” e configure, como mostra a Listagem 4: Name: {group}\TheClub\Exemplo; Filename: {app}\Exec\Exemplo.exe; WorkingDir: {app}; MeGAZINE Delphi piler” ative a opção “Ativar Divisão”, com isso, serão gerados arquivos de até 1.44MB, os quais poderão ser copiados para disquetes. IconFilename: {app}\Exec\Exemplo.exe; Comment: Exemplo de Instalação.; Flags: createonlyiffileexists runmaximized; IconIndex: 0 Conclusão Listagem 4 – Configuração do grupo e ícone. Agora vamos para as configurações no registro do Windows, sendo esta, uma configuração muito importante para que o Firebird possa rodar após nossa instalação. Teremos as seguintes entradas no registro do Windows, Figura 12. Com sabem, o Interbase/Firebird utiliza como porta padrão de comunicação, a porta 3050 e após finalizamos a instalação temos que setar esta configuração no arquivo SERVICES (do Windows), registrar a instalação e inicializar o Firebird. Para isso, na sessão “Executar na instalação” defina as configurações apresentadas na Figura 13 Existem outras configurações que podem ser feitas, como por exemplo, criar grupos de instalação “Completa”, “Personalizada”, “Mínima”, etc, contudo não iremos nos aprofundar nestes tópicos, pois nosso intuito neste artigo é apenas o de apresentar a ferramenta e mostrar do que ela é capaz, mas volto afirmar, apesar de sua aparência simples, é uma poderosa aliada na distribuição e atualização de aplicativos, bastando explorar sua criatividade e necessidades. Até a próxima... Downloads Os exemplos utilizados neste artigo estão disponíveis para download no endereço: Nossa instalação está pronta! Agora, apenas clique no botão “Compilar” e se tudo estiver configurado corretamente, seu “Setup”sera gerado e bastará você testar nossa instalação. http://www.theclub.com.br/revista/download/innoexemplos.zip Para saber mais Escolha da mídia para distribuição Você pode observar nos dois exemplos acima, que não nos preocupamos com o tamanho final da instalação, visto hoje ser muito comum a distribuição de aplicativos em CDs, pelo seu baixo custo, tamanho e facilidade de distribuição. Contudo, caso seja necessário distribuir o aplicativo em disquetes de 1.44MB, acesse o botão “Options” e na aba “Com- No site do fabricante você poderá encontrar informações e ferramentas adicionais para o Inno Setup, http://www.jrsoftware.org/is3rdparty.php. No endereço http://ibinstall.defined.net poderão ser encontrados scripts e informações sobre a instalação do Interbase e Firebird. Figura 12 – Chaves no registro do Windows. Sobre o autor Alessandro Ferreira, Consultor Técnico do The Club [email protected] Figura 13 – Executar após instalação MeGAZINE 21 Delphi TimeOut Controlando a ociosidade da aplicação. por Alessandro Ferreira, [email protected] Introdução Em nossa aplicação, iremos estar verificando as seguintes mensagens: Uma funcionalidade bastante interessante no Windows e mesmo em alguns aplicativos que conheço, é a possibilidade de tratar a ociosidade do sistema, ou seja, verificar se o aplicativo não está sendo utilizando e assim, fechá-lo ou solicitar ao usuário que efetue o login novamente, questão esta até mesmo de segurança, pois um usuário pode logar-se na aplicação, sair para tomar um café ou visitar um cliente e por esquecimento não efetuar um logoff na aplicação, deixando-o assim aberto para qualquer pessoa ter acesso ao sistema através do usuário logado atualmente. Mediante isso, neste pequeno artigo iremos demonstrar como implementar um controle simples e eficaz, mãos a obra. Como identificar ociosidade? Mensagem do Windows Finalidade WM_MOUSEMOVE Qualquer movimentação do mouse na aplicação. WM_KEYDOWN Qualquer tecla pressionada em nossa aplicação. WM_LBUTTONDOWN Click do botão esquerdo do mouse em nossa aplicação. WM_RBUTTONDOWN Click do botão direito do mouse em nossa aplicação. WM_SYSKEYDOWN Como sabem, praticamente tudo no Windows gera mensagens que podemos facilmente capturar e tratar de acordo com nossas necessidades. Um simples movimento ou clique do mouse, uma tecla pressionada, enfim, qualquer execução dispara diversas mensagens indicando o evento realizado, o que pode ser tratado de forma bastante simples em nossa aplicação. Caso tenha curiosidade, abra a unit Windows, e encontrará diversas constantes declaradas com o prefixo “WM_” que significa “Windows Message”, que nada mais são do que uma nomeação para facilitar nosso trabalhado, como exemplo a constante “WM_MOUSEMOVE”, que indica uma mensagem de movimento do mouse. 22 Qualquer tecla de sistema (Windows) pressionada em nossa aplicação. Figura 1 – Mensagens do Windows. No objeto TApplication temos um evento chamado OnMessage o qual iremos utilizar para fazer o tratamento das mensagens apresentadas na Figura 1. Vamos iniciar nossa implementação. Na sessão private do formulário, faça as seguintes declarações: type TMainForm = class(TForm) private MeGAZINE Delphi ligação da mesma ao evento OnMessage. { Private declarations } T: integer; procedure Mensagem(var Msg: TMsg; var Handled: Boolean); public { Public declarations } End; Observe que o código da procedure Mensagem é bastante simples, onde basicamente, enquanto houver “operação” no sistema, vamos atualizando a variável T com o tempo de execução que o aplicativo está sendo “operado” pelo usuário. Se por algum motivo, o sistema ficar inativo, esta variável ficará desatualizada, e se o valor da variável, subtraído do tempo atual de “operação” for maior que o tempo definido como TimeOut (na constante cTimeOut, declarada na Listagem 1), iremos chamar o formulário de login, para que o usuário entre novamente com sua senha, isso caso o formulário de login não esteja em evidência. Em seguida, ligamos nossa procedure ao evento OnMessage do objeto TApplication no evento OnCreate de nosso formulário. const cTimeOut = 5000; // Tempo para acionar a tela de senha. cChamaSenha: Boolean = True; // ativação do Form de senha. Listagem 1 – Declaração da procedure, constantes e variáveis de controle. Declaramos uma váriavel chamada T que irá ser utilizando para guardar o tempo de execução da aplicação. Após declarar a procedure Mensagem, tecle Ctrl+Shift+C, com isso a Delphi irá criar a procedure, onde iremos fazer as implementações mostradas na Listagem 2: procedure TMainForm.Mensagem (var Msg: TMsg; var Handled: Boolean); begin if (Msg.message = WM_MOUSEMOVE) or (Msg.message = WM_KEYDOWN) or (Msg.message = WM_LBUTTONDOWN) or (Msg.message = WM_RBUTTONDOWN) or (Msg.message = WM_SYSKEYDOWN) then begin T := GetTickCount; end; Bem, antes de continuar nossas implementações no formulário principal, vamos criar um simples form para solicitar a senha ao usuário. Para isso, vá o menu File | New | Other | Dialogs e selecione “Password Dialog”, como mostra a Figura 2: Figura 2 – Criação do formulário de senha. if ((GetTickCount - T) > cTimeOut) and (PasswordDlg=Nil) then begin cChamaSenha := True; Activate; end; end; procedure TMainForm.FormCreate(Sender: TObject); begin Application.OnMessage := Mensagem; end; Com isso, será criado um formulário semelhante ao que pode ser visto na Figura 3: Listagem 2 – Implementação da procedure Mensagem e MeGAZINE Figura 3 – Formulário de senha 23 Delphi Neste exemplo, não estamos nos preocupando em fazer tratamento da senha digitada, pois neste artigo nosso foco principal é o controle de ociosidade da aplicação. um componente TTimer (palheta System), no qual obrigatoriamente teremos que ativar seu evento OnTimer, mesmo que sem nenhuma programação, veja a Listagem 4: Caso deseje mais detalhes sobre implementação de controle de acesso de usuários na aplicação, poderá consultar o artigo “Acesso: Identificando e controlando usuários no sistema” em nossa revista de Junho/2003. Bem, vamos voltar às nossas implementações no formulário principal, partindo agora para o evento OnActivate do mesmo, inserindo o código demonstrado na Listagem 3: procedure TMainForm.FormActivate(Sender: TObject); begin T := GetTickCount; if cChamaSenha then begin PasswordDlg := tPasswordDlg.Create(Self); PasswordDlg.ShowModal; FreeAndNil(PasswordDlg); cChamaSenha := False; end; end; Listagem 4 – Evento OnTimer do componente Timer. Neste exemplo, configuramos a constante cTimeOut com 5000, que correspondem a cinco segundos. Compile e execute esta aplicação de exemplo, digite um valor qualquer no formulário de login, tecle OK e deixe a aplicação ociosa (sem mexer no mouse e teclado) que dentro de cinco segundos o login será solicitado novamente! Conclusão Vimos neste pequeno artigo um artifício bastante interessante que será mais um diferencial em sua aplicação. Pequenas implementações como estas, vão dando um toque ainda mais profissional ao seu produto e com sua criatividade poderão ser utilizadas para outras finalidade, como exemplo, fechar a aplicação após algum tempo de ociosidade. Listagem 3 – Implementação do evento OnActivate do formulário principal. Neste evento, estamos utilizando a API GetTickCount que retorna o tempo decorrido de execução do Windows, o qual guardamos na variável T. Caso tenha alguma sugestão ou comentário, sinta-se a vontade em nos enviar. Após isso, se a constante cChamaSenha estiver com valor igual a True, iremos fazer a chamada do formulário de login, e atribuir False para a constante. Estamos trabalhando com esta constante, devido ao fato do evento OnActivate ser acionado toda vez que o referido formulário recebe foco (A partir do Delphi 6, para que seja possível atribuir valores à uma constante, será necessário habilitar esta opção declarando a seguinte constante {$J+}). Lembre-se de declarar a unit referente ao formulário de senha na lista de uses do formulário principal. Forte abraço, e até a próxima. Download O projeto de exemplo referente este artigo está disponível em: www.theclub.com.br/revista/download/TimeOutLogin.zip Sobre o autor Um detalhe bastante importante, é que iremos necessitar de 24 procedure TMainForm.tmrControleTimer(Sender: TObject); begin // Evento OnTimer do nosso componente Timer. end; Alessandro Ferreira, Consultor Técnico do The Club - [email protected] MeGAZINE Delphi Criando Aplicativos Auto-Atualizáveis Aprenda a criar um EXE para verificação de versão na rede por Alessandro Ferreira ([email protected]) Introdução É muito comum os desenvolvedores disponibilizarem uma cópia de seu aplicativo (EXE) em cada estação da rede para vários motivos, como por exemplo, evitar a elevação do tráfego em rede no momento da carga da aplicação. Isso é interessante, contudo, pode trazer alguns efeitos colaterais, sendo o mais grave, ter versões diferentes do mesmo aplicativo rodando na rede. Temos recebido diversas solicitações de nossos associados buscando uma solução para este problema, e, pensando nisso estamos publicando este artigo, o qual esperamos que venha a atender ou pelo servir como parâmetro para resolver este problema! Figura 1 – Formulário Principal do Projeto Atualiza. Agora, crie o evento OnShow para este Form, pois é neste evento que iremos concentrar nossa programação, conforme poderá conferir na Listagem 1: A Lógica Em nosso exemplo, iremos criar duas aplicações, sendo a primeira, um aplicativo que iremos chamar de “Atualiza” que recebe dois parâmetros, sendo o caminho da atualização (geralmente no Servidor) e o caminho local do aplicativo (EXE na estação). A segundo projeto, será a aplicação propriamente dita, a qual em sua inicialização irá comparar sua data com a data do arquivo no servidor e caso seja inferior, irá executar a atualização. O projeto “Atualiza” Este projeto é bastante simples, porém muito importante e poderá ser utilizado para atualizar um ou várias aplicações, devido ao fato dele não fazer referência direta as EXEs que deverão ser autualizados. Bom, chega de papo e mãos a obra. Abra o Delphi, e crie um novo projeto e salve-o como “Atualiza”. Poderá ajustar o Form principal como mostra a Figura1: MeGAZINE procedure TMainForm.FormShow(Sender: TObject); var Origem, Destino: string; begin (* Verifica passagem de parâmetros... *) if ParamCount < 2 then Exception.Create (‘Erro na passagem de parâmetros, verifique!’); (* Pega valores dos parâmetros *) Origem := ParamStr(1); Destino := ParamStr(2); (* Check dos arquivos *) if not FileExists(Origem) then Exception.Create (‘Arquivo de origem não existe!’); if not FileExists(Destino) then 25 Delphi efetuamos a chamada do novo EXE (aplicativo já atualizado) e para finalizar, iremos fechar o “Atualiza”. Exception.Create(‘Arquivo de destino não existe!’); (* Copia novo EXE para a pasta do aplicativo *) CopyFile(PChar(Origem), PChar(Destino), False); (* Chama aplicativo já com o novo EXE *) WinExec(PChar(Destino), sw_ShowNormal); (* Fecha este aplicativo *) PostMessage(Handle, wm_Quit, 0, 0); end; Aplicação Exemplo Agora, vamos criar uma aplicação de exemplo que iremos chamar de “auto-atualizável”, bastante simples, porém funcional, onde você poderá entender como a verificação e atualização irão acontecer. Mãos a obra! Crie um novo projeto, e como sugestão de tela, veja a Figura 2. Salve o projeto e acesse o código fonte do projeto (para isso, vá ao menu Project | View Source), pois é diretamente neste arquivo de projeto que iremos codificar nossa lógica de atualização. Primeiramente, declare as seguintes Units na sessão Uses, como poderá visualizar na Listagem 3. Listagem 2 – Evento OnShow do projeto Atualiza. Bem, este é todo o código que iremos necessitar neste projeto. Como pode observar, primeiramente verificamos a quantidade de parâmetros recebidos pela aplicação e se estiverem dentro do número esperado, capturamos os parâmetros para duas variáveis, como apresentado no Quadro 1 Quadro 1 – Parâmetros do aplicativo Atualiza. Depois verificamos se ambos os arquivos informados existem e caso positivo, iremos efetuar a atualização (cópia do arquivo do Servidor para a Estação). Após efetuada a cópia, program TheClub; uses Dialogs, Windows, SysUtils, IniFiles, Forms, unPrincipal in ‘unPrincipal.pas’ {Form1}; {$R *.res} (* Variáveis para Controle da Atualização *) var PathUpdate, Command: String; TSI: TStartupInfo; TPI: TProcessInformation; Ini: TIniFile; Listagem 3 – Units e variáveis no projeto. Continuando, você irá perceber que verificamos o caminho da atualização em um arquivo INI (que chamamos de version.ini) que deverá estar junto como o EXE (na estação), o qual tem a seguinte estrutura: [ATUALIZACAO] PATH=\\Servidor\Atualizacoes\ THECLUB.EXE Figura 2 – Formulário principal do projeto Auto-Atualizável 26 Na Listagem 4 estamos apresentando o código restante de nosso projeto. MeGAZINE Ori De Delphi (* Segue carga da aplicação *) Application.CreateForm(TForm1, Form1); Application.Run; begin Application.Initialize; (* Verifica path da atualização *) end. Ini := TIniFile.Create(ExtractFilePath (ParamStr(0))+’version.ini’); try PathUpdate := Ini.ReadString Listagem 4 – Código restante do projeto. (‘ATUALIZACAO’, ‘PATH’, ‘’); finally Ini.Free; end; Vamos analisar o código da Listagem 4. Começamos criando um objeto TIniFile o qual irá ler as configurações do arquivo INI citado anteriormente. Após isso, verificamos se existe arquivo de atualização referente ao caminho contido no arquivo INI e caso (* Verifica se a atualização existe *) if FileExists(PathUpdate) then begin (* Compara pela Data do Arquivo. Se a data do exista, comparamos se a data do arquivo de atualização (no servidor) é maior que a data do aplicativo corrente e caso seja, executamos o aplicativo “Atualiza” passando como parâmetros o caminho e arquivo de origem (servidor) e o caminho e arquivo de EXE existente na pasta de atualização for > do que este EXE... *) if FileAge(PathUpdate) > FileAge(ParamStr(0)) then begin (* Caminho do atualizador *) Command := ExtractFilePath(PathUpdate) + ‘Atualiza.Exe ‘ + PathUpdate destino (aplicativo corrente na estação), fazendo a execução através da criação de um processo no Windows. Após efetuar a cópia e se esta for bem sucedida, inibimos a apresentação do MainForm da aplicação corrente (apenas para evitar o efeito de “piscar” o Form) e finalizamos a aplicação corrente, isso porque a aplicação “Atualiza” irá fazer a chamada (conforme explicado anteriormente) através da API WinExec e + ‘ ‘ + ParamStr(0); FillChar(TSI, SizeOf(TSI), 0); TSI.CB := SizeOf(TSI); (* Cria processo para atualização *) com isso, colocar a nova versão para rodar. if not CreateProcess(nil, PChar(Command), nil, nil, False, CREATE_SHARED_WOW_VDM, nil, nil, TSI, TPI) then begin simples, porém, nos vários testes que efetuamos em nosso laboratório obtivemos ótimos resultados com esta abordagem, reduzindo bastante os problemas de atualização e diferença de versão. Esperamos estar contribuindo para tornar seus MessageBeep(0); Exception.Create(‘Problemas na atualização da versão!’); end; aplicativos cada vez mais profissionais e caso tenha alguma sugestão ou comentário, sinta-se a vontade para entrar em contato conosco, até a próxima. (* Não deixa mostrar o MainForm *) Application.ShowMainForm := False; (* Encerra aplicação para chamar nova versão do Executável *) Download O projeto de exemplo referente este artigo está disponível para download em: Conclusão Como mencionei no início deste artigo, a idéia é bastante http://www.theclub.com.br/revista/download/atualizador.zip Application.Terminate; (* Não executa daqui para baixo... *) Exit; end; Sobre o autor Alessandro Ferreira, Consultor Técnico do The Club - [email protected] end; MeGAZINE 27 Dicas & TTruques ruques Rave Reports – Como fazer cálculo (Campo1* Campo2/100) linha a linha Somente essas propriedades serão ligadas, e neste momento montamos a fórmula para fazer Campo1 * Campo2. Para fazer esse cálculo dentro do Rave Report iremos utilizar as seguintes configurações: Agora coloque um segundo componente CalcOp, chamado CalcOp2, onde completaremos a fórmula dividindo o resulto do CalcOp1 anterior por 100. Selecione o componente chamado CalcOp da aba REPORT e depois clique sobre a banda onde estão sendo impressos os dados; (Esse componente não ficará visível na banda somente é utilizado para fazer os cálculos entre os campos. Para trabalhar com esse componente selecione-o através do treeview existente ao lado do design do relatório.) Neste componente CalcOp, chamado CalcOp1 iremos configurar as seguintes propriedades: Operator : Src1CalcVar : Src2Value : DestPIVar : relatório) coDiv CalcOp1 100,000 vCalculo (nome da variável criada no Essa variável vCalculo deve ser criada da seguinte maneira: Operator : coMul (Indica que será feita multiplicação) Src1DataView : indique o dataview que contém o primeiro campo do cálculo Src1DataField : indique o primeiro campo do cálculo Src2DataView : indique o dataview que contém o segundo campo do cálculo Src2DataField : indique o segundo campo do cálculo DisplayFormat : indique a mascara, pode ser ###,##0.00 28 No CalcOp2 configure as seguintes propriedades: Selecione o relatório no Treeview, logo abaixo da opção Report Library, agora na propriedade PIVars escreva o nome da variável desejada, no caso vCalculo, e pronto está criada a variável que receberá o resultado do cálculo. Para finalizar este cálculo, precisamos somente mostrar o valor que está na variável, no relatório e para isso iremos colocar um componente chamado DataText da aba Report. Neste componente na propriedade DataField pressione o botão com reticências (...). Agora na tela Data Text Editor selecione no MeGAZINE Dicas & TTruques ruques combo “Post Initialize Variables” a variável que guardamos o resultado do cálculo, pressione o botão “Insert_PIVar” e depois pressione o botão Ok. Pronto o resultado do cálculo armazenado na variável será impresso no relatório. 3.Certifique-se de que a DLL “libmysql.dll” esteja em um path acessível (como exemplo, \Windows\System) Continuando, 1.insira um componente sqlConnection 2.dê um duplo clique sobre o mesmo 3.Adicione uma conexão do tipo MySQL 4.Configure host com o IP ou se for local = localhost 5.Configure usuário e senha, como exemplo root e 123456 6.Configure DataBase com o nome de seu banco de dados; Rave Report – Passando parâmetros para o relatório Para criar um parâmetro dentro do Rave faça o seguinte: Selecione a página do relatório e acesse a propriedade Parameters, dentro desta propriedade escreva o nome do seu parâmetro, exemplo PAnoMes. Depois coloque um componente DataText e na propriedade DataField clique no botão (...). Agora no combo Project Parameters selecione o parâmetro que foi criado e pressione o botão “Insert Parameter”. Pronto o parâmetro será impresso neste componente DataText. Para passar o valor do Delphi para o Rave utilize o seguinte comando: Rpt_Projeto.Open; { Passa parametro para o relatório para imprimir no DataText do cabeçalho } Rpt_Projeto.SetParam(‘PAnoMes’, Mes.Text+’/ ’+Ano.Text); Rpt_Projeto.ExecuteReport(‘RelFat’); Rpt_Projeto.Close; Feito isso, bastará ligar um sqlDataSet ao sqlConnection, um DataSetProvider ao sqlDataSet, um ClientDataSet e fazer as edições via ClientDataSet. Veja como criar atalhos no menu iniciar do Windows Neste exemplo, iremos demonstrar uma forma bastante simples para criar: - Grupos de programas - Items de programas - Atalhos para programas Em nossa abordagem, não iremos utilizar a criação via interface DDE por alguns questões de parametrização e sim, demonstraremos a partir da criação direta de “folders” na pasta “Programs” do Windows. Primeiramente, vamos criar uma procedure para criação de atalhos: MySQL – Conectando MySQL via dbExpress O acesso ao MySQL via dbExpress é bastante simples, contudo, primeiro recomendamos atualizar seu Delphi (caso esteja utilizando Delphi 6) com o “Update Pack#2” que pode ser encontrado no site da Borland: http://www.borland.com/delphi Após isso, faça a seguinte alteração: 1.Abra o arquivo dbxdrivers.ini, pasta: “/Program Files/ Common Files/Borland Shared/DBExpress” 2.Onde está escrito “dbexpmys.dll”, altere para “dbexpmysql.dll” MeGAZINE implementation uses ShlObj, ActiveX, ComObj; {$R *.dfm} procedure CriaShortCut(aNome, aFileName, aPathGroup: string; aLocation: integer); var IObject : IUnknown; ISLink : IShellLink; IPFile : IPersistFile; PIDL : PItemIDList; InFolder : array[0..MAX_PATH] of Char; TargetName : String; LinkName,s : WideString; 29 Perguntas & Respostas Observe que esta procedure recebe como parâmetro o nome do grupo (que na realidade será o nome de um “Folder” e a localização, ou seja, onde ele deverá ser criado. begin TargetName := aFileName; IObject := CreateComObject(CLSID_ShellLink); ISLink := IObject as IShellLink; IPFile := IObject as IPersistFile; Exemplo de utilização: with ISLink do begin SetPath(pChar(TargetName)); SetWorkingDirectory(pChar(ExtractFilePath(TargetName))); end; SHGetSpecialFolderLocation(0, aLocation, PIDL); SHGetPathFromIDList(PIDL, InFolder); s := InFolder; LinkName := s + ‘\’ + aPathGroup + ‘\’ + aNome + ‘.LNK’; IPFile.Save(PWChar(LinkName), false); end; Esta procedure recebe como parâmetro o Nome do atalho a ser apresentado, o caminho e nome do aplicativo a ser executado, e por último o grupo ao qual ele irá pertencer no menu Iniciar do Windows. Agora vamos para a procedure responsável em criar o Grupo e o Ítem de Programa: function CreateFolder(Foldername: string; aLocation: integer): boolean; var pIdl: PItemIDList; hPath: PChar; begin Result := False; if SUCCEEDED(SHGetSpecialFolderLocation(0, aLocation, pidl)) then begin hPath := StrAlloc(max_path); SHGetPathFromIDList(pIdl, hPath); SetLastError(0); if ForceDirectories(PChar(hPath + ‘\\’ + Foldername)) then Result := true; StrDispose(hPath); end; end; 30 procedure TForm1.Button1Click(Sender: TObject); begin // cria Grupo principal. CreateFolder(‘The Club’, CSIDL_PROGRAMS); // cria sub-grupo dentro do “The Club” CreateFolder(‘The Club\Grupo 1’, CSIDL_PROGRAMS); // cria sub-grupo dentro do “The Club” CreateFolder(‘The Club\Grupo 2’, CSIDL_PROGRAMS); // cria sub-grupo dentro do “The Club” CreateFolder(‘The Club\Grupo 3’, CSIDL_PROGRAMS); // cria sub-grupo dentro do “The Club\Grupo 1” CreateFolder(‘The Club\Grupo 1\Sub Grupo 1’, CSIDL_PROGRAMS); // cria sub-grupo dentro do “The Club\Grupo 2” CreateFolder(‘The Club\Grupo 2\Sub Grupo 1’, CSIDL_PROGRAMS); // cria sub-grupo dentro do “The Club\Grupo 3” CreateFolder(‘The Club\Grupo 3\Sub Grupo 4’, CSIDL_PROGRAMS); // cria atalho para os programas CriaShortCut(‘Calculadora’, ‘c:\windows\system32\calc.exe’ , ‘The Club\Grupo 1’, CSIDL_PROGRAMS); CriaShortCut(‘Bloco de Notas’, ‘c:\windows\system32\notepad.exe’, ‘The Club\Grupo 2’, CSIDL_PROGRAMS); CriaShortCut(‘WordPad’, ‘C:\Program Files\Windows NT\Accessories\wordpad.exe’, ‘The Club\Grupo 3’, CSIDL_PROGRAMS); end; Dica: A constante CSIDL_PROGRAMS indica a pasta padrão onde os ítens do menu iniciar do Windows ficam armazenados. Trabalhando com esta constante, independente da versão e linguagem do Windows, a pasta irá ser criada no local correto. Observe que primeiramente criamos um grupo principal, chamado “The Club”. Seguindo, dentro de “The Club” criamos sub-grupos e dentro dos sub-grupos os atalhos que irão chamar os aplicativos em questão. Este exemplo está disponível para download em nosso site no endereço: http://www.theclub.com.br/revista/download/ CriaGrupoAtalho.zip MeGAZINE