terça-feira, 24 de maio de 2011

Parâmetros de instalação para o Firebird 2.5


Meus caros, após muito procurar, localizei no código fonte do Firebird 2.5, junto ao script do Inno Setup, um documento de texto que mais tarde vim a saber por ele mesmo que há como exibi-lo chamando o arquivo de instalação Firebird-1.5.6.5026-0-Win32.exe com o parâmetro /HELP, o que seria super intuitivo se eu não tivesse já tentado isso e deparado com uma caixa de seleção de idioma e cancelado crendo que não teria efeito algum.

Todo este material é transcrição do documento em inglês, cujo não achei referência em local algum até agora, seja nos sites especializados com firebird ou mesmo o portal da HK-Software, desenvolvedora do nosso querido IBExpert. Este documento que encontrei estava em inglês, fiz a interpretação do mesmo em português e testei  parâmetro por parâmetro e aqui está o resultado.

De cara já digo que meus parâmetros padrões são: /FORCE /VERYSILENT /NORESTART /LOG="C:\LOGFB.TXT" /SAVEINF="C:\SAVEINF.TXT" /NOCANCEL

Configuro o Inno Setup para executar o Firebird-1.5.6.5026-0-Win32.exe com estes parâmetros e voilà, o firebird está instalado no ponto de bala sem exibir mensagem alguma ao usuário, sem pedir configuração alguma e finaliza no jeito para que minha aplicação acesse o banco usando o servidor recém instalado.

Mas vamos ao que interessa, os..

Parâmetros de instalação do firebird 2.5

/SP-
Retira a confirmação "This will install... Do you wish to continue?" iniciando diretamente a instalação.

/SILENT
Não exibe o assistente de instalação, porém exibe o progresso.

/VERYSILENT
Não exibe o assistente nem o progresso de instalação.

/SUPPRESSMSGBOXES

Instrui a instalação para não questionar o usuário, mas só possui efeito quando combinado com SILENT ou VERYSILENT, a resposta padrão nas seguintes situações são:

Sim para "Keep newer file?" - Manter os arquivos mais recentes
Não para "File existis, confirm overwrite." - Confirme sobrescrever arquivo existente.
Abort em situações "Abort/Retry"
Cancel em "Retry/Cancel"
Sim (=continue) para "DiskSpaceWarning / DirExists / DirDoesntExist  / NoUninstallWarning / ExitSetupMessage / ConfirmUninstall situation.
Sim (=restart)  para "FinishedRestartMessage/UninstalledAndNeedsRestart"

Cinco mensagens não são suportadas:

     "About Setup"
     "Exit Setup?"
     "FileNotInDir2" exibida quando o seup requer um novo disco a ser inserido e o disco não é encontrado.
     Qualquer mensagem de erro exibida antes da instalação ou desinstalação
     Qualquer mensagem exibida pelo suporte a [Code] do script de instalação.

/LOG

Cria um arquivo de log no diretório TEMP detalhando a instlação dos arquivos e as ações Run do processo de instalação. Isso pode ser uma ajuda de depuração úteis. Por exemplo, se você suspeitar quee um arquivo não está sendo substituído quando você acreditar que deveria ser (ou vice-versa), o arquivo de log vai dizer se o arquivo foi realmente ignorada, e por quê. O arquivo de log é criado com um nome único baseado na data atual e não vai substituir ou incrementar arquivos existentes.

As informações contidas no arquivo de log é de natureza técnica e portanto, não se destina a ser compreensível pelos usuários finais. Também não é projetado para ser analisável por programação, o formato do arquivo sofre alterações constantes.

/LOG="filename"

Igual ao log, exceto pelo fato de poder especificar um caminho e nome do arquivo para o arquivo de log. Caso o arquivo indicado já exista ele é substituído. Se o arquivo não puder ser criado, a instalação será abortada com uma mensagem de erro.

/NOCANCEL

Desabilita o botão de cancelar, faz efeito somente com /SILENT.

/NORESTART

Instrui a instalação a não reiniciar mesmo que isso seja necessário.

/RESTARTEXITCODE=exit code

Especifica o código de saída personalizado que a instalação irá retornar quando uma reinicialização é necessária. Use juntamente com '/ NORESTART.

Veja mais adiante "Setup Exit Codes".

/LOADINF="filename"

Encarrega instalação para carregar as configurações do arquivo especificado. Este arquivo pode ser preparado utilizando o '/ SAVEINF' que é explicado abaixo.

/SAVEINF="filename"

Instrui à instalação para salvar as configurações da instalação para o arquivo especificado.
Use "aspas" se possuir espaços no nome do arquivo.

/LANG=language

Indica o idioma a ser utilizado. Quando um parâmetro válido é informado, a caixa de seleção de idioma que aparece no início da instalação é suprimida. Os parâmetros para linguagem aceitas pelo instalador do firebird 2.5 são: en, ba, fr, de, es, hu, it, pl, pt e ru. Respectivos de inglês, bósnio, francês, alemão, espanhol, hungraniano, italiano, polonês, português e russo.

/DIR="x:\dirname"

Sobrescreve o caminho padrão exibido no formulário de selecionar diretório no assistente. O caminho completo do diretório deve ser informado.

/GROUP="folder name"

Sobrescreve o nome do grupo do menu iniciar exibido no assistente da instalação.

/NOICONS

Instrui a instalação a checar a opção "Não criar nenhuma pasta do Menu Iniciar".

Os parâmetros exibidos até aqui, servem para qualquer instalação criada pelo Inno Setup, que é o caso do firebird. A partir de agora, veremos as opções que valem somente para o instalador do firebird:

/COMPONENTS="lista de componentes separados por ;"

Escolhas:

    ServerComponent\SuperServerComponent,
    ServerComponent\ClassicServerComponent,
    ServerComponent,
    DevAdminComponent and
    ClientComponent

Este comando substitui os componentes das configurações padrão. Usando este parâmetro estará usando um tipo personalizado da instalação, um completo exige a combinação de componentes, por exemplo:
   /COMPONENTS="ServerComponent\SuperServerComponent,ServerComponent,DevAdminComponent,ClientComponent"

/TASKS="lista de tarefas separadas por ;"

Escolhas:

    UseGuardianTask
    UseApplicationTask
    UseServiceTask
    AutoStartTask
    InstallCPLAppletTask
    MenuGroupTask
    CopyFbClientToSysTask
    CopyFbClientAsGds32Task

Ao usar este comando somente as tarefas especificadas serão aplicadas, desabilitando os demais, o que torna o uso combinado deste parâmetro com /NOCPL, NOGDS32 ou /COPYFBCLIENT conflitantes.

/MERGETASKS="lista de tarefas separadas por ;"

É como o parâmetro /TASKS, porém indicar uma tarefa ou mais não indica que as tarefas checadas por default serão desmarcadas. Na prática, marca a tarefa indicada e as demais permanecem como são em default.
/FORCE

O instalador ignora a sua análise do atual de ambiente. Ele tentará instalar e configurar o Firebird como se nenhuma versão anterior do Firebird ou InterBase foi instalado. Isto pode ser útil se você tiver uma instalação corrompida que não é possível desinstalar.

/NOCPL

Não instala o applet de painel de controle. Útil para evitar exigir reiniciar a instalação em alguns sistemas operacionais.

/NOGDS32

Não instala a biblioteca de cliente para o diretório do sistema, mesmo que a isntalação queira copiar.

/COPYFBCLIENT

Copia o fbclient.dll ao diretório do sistema. Isto é recomendado para a instalação do cliente, se tiver certeza de que você só vai ser acesso a uma versão de servidor único. Se os pedidos de seus clientes são suscetíveis de tirar proveito de acessar as versões de servidor diferente desta não é recomendado.

Setup Exit Codes

O setup irá retornar um destes códigos de saída:

      0 - Sucesso na execução.
      1 - Falhou ao inicializar.
      2 - Usuário cancelou inciar a instalação.
      3 - Erro fatal preparando para a próxima fase da instalação, por falta de memória ou falha de recursos do windows.
      4 - Erro fatal durante o processo de instalação atual, gera diálogo Abort-Retry-Ignore.
      5 - Usuário clicou em cancel durante o processo de instalação ou abor em mensagem referenciada no código de saída 4.
      6 - Setup finalizado de maneira forçada pelo debugger. Run | Terminate usado em IDE



Comandos suportados pelo unistall.exe

/SILENT, /VERYSILENT

O desinstalador não irá pedir ao usuário confirmações ou exibir que a instalação foi concluída.
Arquivso compartilhados que não estão mais em uso serão excluídos sem confirmação.
Todas as mensagens de erro crítico ainda serão exibidas na tela.
Quando especificado /VERYSILENT a janela de progresso de desinstalação não será exibida.
Se a reinicialização for necessária e o comando /NORESTART não for usado e /VERYSILENT for especificado, o desinstalador irá reiniciar sem pedir.

/SUPPRESSMSGBOXES

Suprime as caixas de mensagens. Só tem um efeito quando combinado com /SILENT e /verysilent.
Consulte /SUPPRESSMSGBOXES dos comandos de instalação para mais detalhes.

/LOG

Cria arquivo de log da desinstalação com nome baseado na data e hora atual, não sobrescreve nem incrementa arquivo existente. É extremamente técnico e a estrutura das linhas podem ser modificadas sem aviso de uma instalação para outra.
/LOG="filename"

Igual ao log, exceto pelo fato de poder especificar um caminho e nome do arquivo para o arquivo de log. Caso o arquivo indicado já exista ele é substituído. Se o arquivo não puder ser criado, a desinstalação será abortada com uma mensagem de erro.

/NORESTART

Nada haver com a bandinha emo meia boca, somente instrui a instalação a não reiniciar mesmo que isso seja necessário.


Comando próprio de desinstalação do firebird

/CLEAN

Remove os arquivos: firebird.conf, aliases.conf, firebird.log e security2.fdb

Unistaller Exit Codes

Retorna diferente de 0 se for abortado por cancelamento ou erro fatal.

Microsoft, Skype e Delphi


Como você certamente sabe, a Microsoft anunciou na última semana a compra do Skype, uma das maiores da história da companhia. De fato, pagaram 8.5 bilhões de dólares para a empresa de telefonia virtual conhecida mundialmente. Aqui eu não entrar em detalhes na questão financeira ou comentar sobre o preço (bastante algo, pelo que vejo, mas possivelmente bom para posicionar a Microsoft além de seus negócios tradicionais).

O que me interessa, ao invés disso, é focar em um detalhe, não menos importante. O cliente do Skype para Windows, de longe mais utilizado e de maior sucesso é escrito em Delphi. Alguém poderia perguntar se uma aplicação feita em Delphi vale 8 bilhões, quanto vale o Delphi? Mas isso não seria realista. Certamente é um bom anúncio para o Delphi, espero que o marketing da Embarcadero possa tirar proveito disso.

Usar Delphi foi bom para o Skype?

Da perspectiva de desenvolvedor, o real e mais importante é tentar descobrir o quanto a decisão de usar o Delphi foi boa para o Skype em primeiro lugar, se isso contribuiu para o sucesso do produto e da companhia, e se eles tivessem escolhido uma ferramenta diferente. Eu acho que a resposta para esta questão destaca alguns aspectos positivos do Delphi para ISVs (independent software vendor – fornecedor de software independente) que querem fazer a distribuição de suas aplicações facilmente.

De fato, a não necessidade de um ambiente de execução (como Java ou .NET) torna a distribuição de um programa em Delphi significativamente mais simples do que uma aplicação C# ou Java. Também, torna possível suportar um grande número de versões de Windows de um único executável. No momento o download do Skype é bem alto, mas costumava ser menor antigamente, quando a banda era mais limitada. Ainda, não requerer dúzias, quando não centenas, de megabytes de bibliotecas torna a distribuição ainda menor do que de aplicações gerenciadas. Além de não necessitar bibliotecas, não existe componente COM ou outras bibliotecas de configuração específica em Windows, novamente fazendo o processo de distribuição e instalação bem simples.

De outro lado, embora eu não conheça muito sobre como é o Skype internamente, a alta qualidade da integração de aplicações Delphi para Windows, o suporte a múltiplas bibliotecas de Socket e conjuntos de componentes visuais, permitem ajudar a criar mais facilmente um clone do Skype do que com outra de ferramentas de desenvolvimento não gerenciadas, como o Microsoft Visual C++.

Skype e Delphi

Sendo assim, acho que o Delphi contribuiu para o sucesso do Skype, embora é difícil dizer o quanto. Agora, pode o Skype contribuir para o sucesso do Delphi? Possivelmente... você não usaria uma ferramenta que lhe permite criar uma empresa de 8 bilhões? Piadas a parte, deixemos que todos saibam que o cliente do Skype para Windows é feito em Delphi e é uma ótima maneira de alavancar a ferramenta de desenvolvimento da Embarcadero. Mas o Skype não esta sozinho, veja o Show Case para uma maior gama de aplicações feitas em Delphi.

domingo, 22 de maio de 2011

Delphi.Net - Evitando o re-envio das informações no Refresh


Olá todos! Segue aqui uma dica para o pessoal que desenvolve para .net com delphi (BDS 2006). Como demorei para encontrar essa solução, resolvi compartilhá-la para ajudar os demais usuários de delphi.net

O problema é o seguinte: desenvolvi uma página em asp.net web forms do delphi que faz insert em um banco de dados SQL Server. Tudo funcionou OK, porém quando o usuário clicava no refresh do browser, este executava o último request feito ao servidor web e, se no caso fosse um insert por exemplo, ele efetuava o insert dinovo, ou seja. "PAU" rsrs !!! Desenvolvi uma solução simples implementando o evento page_prerender e page_load do lado do servidor.

Observação: a procedure Page_PreRender(sender: System.Object; e: System.EventArgs); não é iniciada pelo delphi quando vc cria um novo asp.net web form, portanto, sua declaração, implementação e inicialização tem que ser feita manualmente.

Segue abaixo o código:

unit WebForm1;

interface

uses
  System.Collections, System.ComponentModel,
  System.Data, System.Drawing, System.Web, System.Web.SessionState,
  System.Web.UI, System.Web.UI.WebControls, System.Web.UI.HtmlControls;

type
  TWebForm1 = class(System.Web.UI.Page)
  {$REGION 'Designer Managed Code'}
  strict private
    procedure InitializeComponent;
    procedure Button1_Click(sender: System.Object; e: System.EventArgs);
  {$ENDREGION}
  strict private
    procedure Page_Load(sender: System.Object; e: System.EventArgs);
    procedure Page_PreRender(sender: System.Object; e: System.EventArgs);
  strict protected
    Button1: System.Web.UI.WebControls.Button;
    TextBox1: System.Web.UI.WebControls.TextBox;
    procedure OnInit(e: EventArgs); override;
  private
    { Private Declarations }
  public
    { Public Declarations }
  end;

implementation

{$REGION 'Designer Managed Code'}
///

/// Required method for Designer support -- do not modify
/// the contents of this method with the code editor.
/// By Reinaldo Holanda Carlos
/// Diferenciar postback de um controle em relação ao refresh ... para que o refresh não execute
/// o último request repetidamente , isso pode evitar erros pois se o seu último request foi um insert
/// ele vai executar de novo esse insert clicando no refresh do browser ou com a tecla f5
/// com esse código identifico quando é um postback de um controle e quando é um postback do refresh do browser.
///

procedure TWebForm1.InitializeComponent;
begin
  Include(Self.Button1.Click, Self.Button1_Click);
  Include(Self.Load, Self.Page_Load);
  include(self.PreRender, self.Page_PreRender);
end;
{$ENDREGION}

procedure TWebForm1.Page_Load(sender: System.Object; e: System.EventArgs);
begin
  // TODO: Put user code to initialize the page here
  if not (IsPostBack) then //na primeira vez coloca data
  begin
    Session['CheckRefresh'] := Server.UrlDecode(System.DateTime.Now.ToString());
  end;
end;

procedure TWebForm1.Page_PreRender(sender: TObject; e: System.EventArgs);
begin
  ViewState['CheckRefresh'] := Session['CheckRefresh'];
end;

procedure TWebForm1.OnInit(e: EventArgs);
begin
  //
  // Required for Designer support
  //
  InitializeComponent;
  inherited OnInit(e);
end;

procedure TWebForm1.Button1_Click(sender: System.Object; e: System.EventArgs);
begin
    if (Session['CheckRefresh'].ToString() =
       ViewState['CheckRefresh'].ToString())  then
    begin
      Response.Write('

Request disparado por um CONTROLE

>'+'
');
      Response.Write(Session['CheckRefresh'].ToString()+' - '+ViewState['CheckRefresh'].ToString());
      Session['CheckRefresh'] := Server.UrlDecode(System.DateTime.Now.ToString());
    end
    else
    begin
      Response.Write('

Request disparado por um refresh do Browser: ' + request.Browser.Browser + '


');
      Response.Write(Session['CheckRefresh'].ToString()+' - '+ViewState['CheckRefresh'].ToString());
    end;
end;

end.

Ordenando registros via SQL pelo título das colunas do DBGrid


Esta dica vem para mostrar como fazer a ordenação dos registros do DBGrid ao se clicar no título das colunas, para quem não utiliza o ClientDataSet e precisa fazer a ordenação via SQL.

Caso você utilize o CDS, leia a dica "ClientDataSet - Clicando no Título das Colunas do DBGrid para Ordenar".

Primeiramente, crie um variável global ou pública (no meu caso eu criei com o nome "ordena") do tipo Boolean.

Depois inicialize ela quando abre o formulário, no evento onCreate, passando ela para True.

procedure TfrmLista_Grupo.FormCreate(Sender: TObject);
begin
  Ordena := True;
end;

Depois no evento DBGrid1TitleClick, faça o seguinte código:

procedure TfrmLista_Grupo.DBGrid1TitleClick(Column: TColumn);
var
  i : integer;
  coluna : String;
begin
  //para todas as colunas
  for i := 0 to DBGrid1.Columns.count-1 do
  begin
    DBGrid1.Columns[i].Title.Color := clBtnFace; //fundo padrão
    DBGrid1.Columns[i].Title.Font.Color := clBlack; //texto preto
    DBGrid1.Columns[i].Title.Font.Style := []; //sem efeito
  end;

  coluna := Column.FieldName;
  Column.Title.color := clInfoBk; //quando clicado ele muda a cor do titulo,fonte
  Column.Title.Font.Color := clBlue;
  Column.Title.Font.Style := [fsBold, fsItalic];
  if (Ordena) then
  begin
    IBDataSet.Active := false;
    IBDataSet.SelectSQL.Clear;
    IBDataSet.SelectSQL.Add('SELECT * FROM NOME_DA_TABELA ORDER BY ' + coluna);
    IBDataSet.Active := True;
    Ordena := False;
  end
  else
  begin
    IBDataSet.Active := false;
    IBDataSet.SelectSQL.Clear;
    IBDataSet.SelectSQL.Add('SELECT * FROM NOME_DA_TABELA ORDER BY ' + coluna + ' DESC');
    IBDataSet.Active := True;
    Ordena := True;
  end;
end;

Arredondamento Matemático


Veja nesta dica uma função que faz o arredondamento matemático de um número, assim como é feito pela calculadora

// <--- Faz arredondamento de forma matemática igual da calculadora
function TdmVendas.Arredondamento(Valor: Extended; Decimais: integer): Extended;
var
  Factor, Fraction, Arred : Extended;
  i : integer;
  vFraction : string;
  vlrFrac : Variant;
begin
  Factor := IntPower(10, Decimais);
  Valor := StrToFloat(FloatToStr(Valor * Factor));
  Result := Int(Valor);
  Fraction := Frac(Valor);
  vFraction := floattostr(Fraction);
  for i := 0 to Length(vFraction) do
  begin
      vlrFrac := Copy(vFraction,(Length(vFraction)-i),1);
      if vlrFrac = ',' then
      begin
         if Arred > 0 then
            Result := Result + 1;
         Result := Result / Factor;
         exit;
      end;
      vlrFrac := vlrFrac + Arred;
      Arred  := 0;
      if vlrFrac > 5 then
         Arred := 1;
  end;
end;

Antes de compilar, não esqueça de declarar ao uses a unit Math.

Recompilando as bibliotecas RTL no Delphi 7 - Adequação ao XP SP3


Com a última atualização do windows em vigor (Service Pack 3) percebi que meu Delphi 7 estava produzindo executáveis que estavam sendo bloqueados pelos Antivirus mais populares (como o AVG e o Avast), acusando-os de contaminados

Após muitas pesquisas, baseado na suposição de que a razão do fenômeno seria o SP3, experimentei a recompilação das bibliotecas RTL, o que resultou em solução do problema.

Para tanto, segue abaixo o procedimento:

    Ir para o diretório Delphi7 e fazer um backup do diretório 'lib';
    Ir para o diretório Delphi7\Source\Rtl. Neste local deve haver o arquivo 'makefile';
    Neste local, deve-se criar um subdiretório '\lib';
    Entrar no command-line e digitar 'MAKE';
    Serão produzidos arquivos 'dcu' no diretório 'lib' criado no passo 3;
    Sobrescrever estes arquivos no diretório lib original do Delphi7;

Pronto. RTL re-compilada.

segunda-feira, 16 de maio de 2011

Retornar o mês por extenso, usando array

Nesta dica, vamos criar uma função para retornar o mês por extenso, somente informando o número do mês.

Programando:

interface

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

type
TForm1 = class(TForm)
Button1: TButton;
Label1: TLabel;
procedure Button1Click(Sender: TObject);
function MesExtenso( Mes:Word ) : string;
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.DFM}

function TForm1.MesExtenso( Mês:Word ) : string; const meses : array[0..11] of PChar = ('Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho', 'Julho', 'Agosto', 'Setembro','Outubro', 'Novembro', 'Dezembro');
begin
result := meses[mes-1];
End;

procedure TForm1.Button1Click(Sender: TObject);
begin
label1.Caption := MesExtenso(3);
end;

end.

terça-feira, 10 de maio de 2011

Usando MessageBox

Para que as mensagens apareçam em portugues (na lingua no sistema) não é necessário a tradução das units.

Invés de usar a função messagedlg eh melhor usar a função MessageBox

Sintax:

MessageBox (Handle, Messagem, Caption, Botoes)
onde

Handle : Endereço do form na memória ; Sempre usu Application.Handle
Messagem : A messagem a ser mostrada
Caption : O titulo da messagem
Botoes : Os Botoes que irao ser mostrados. Na lingua do sistema

MB_ABORTRETRYIGNORE A messagem mostra os tres botoes: Abort, Retry, and Ignore.
MB_OK A messagem mostra um botoao: OK. This is the default.
MB_OKCANCEL A messagem mostra os dois botoes: OK and Cancel.
MB_RETRYCANCEL A messagem mostra os dois botoes: Retry and Cancel.
MB_YESNO A messagem mostra os dois botoes: Yes and No.
MB_YESNOCANCEL A messagem mostra os tres botoes: Yes, No, and Cancel.
Sons
MB_ICONEXCLAMATION, MB_ICONWARNING: Mostra o icone de exclamação e som conrrespondente. Analo aos demais
MB_ICONINFORMATION, MB_ICONASTERISK
MB_ICONQUESTION
MB_ICONSTOP,
MB_ICONERROR,
MB_ICONHAND
Botoes padrao
MB_DEFBUTTON1: Padrao nao precisa ser colocado.
MB_DEFBUTTON2: Coloca o segundo botao como padrao
MB_DEFBUTTON3: Coloca o terceiro botao como padrao
MB_DEFBUTTON4: Coloca o quarto botao como padrao
Respostas
IDABORT
IDCANCEL
IDIGNORE
IDNO
IDOK
IDRETRY
IDYES

Exemplo:

Case MessageBox (Application.Handle, Pchar ('Deseja excluir o arquivo' + #13 + Label1.caption), 'Exclusao de arquivo', MB_YESNOCANCEL+MB_EXCLAMATION+MB_DEFBUTTON2) of
idYes: Procedimento
idNo: Procedimento
idCancel: Procedimento
end;

O que fazer quando o StrToIntDef gera uma exceção

Quando utilizamos a função StrToInt e a string original não é um inteiro, o programa gera uma exceção. Para evitar isto, podemos utilizar a função StrToIntDef, que insere um valor predeterminado (default) caso a string não possa ser convertida em inteiro.

Veja o exemplo:

procedure TForm1.Button1Click(Sender: TObject);
var
    NumberString: string;
    Number: Integer;
begin
    NumberString := Edit1.Text;
    Number := StrToIntDef(NumberString, 1000);
    Edit2.Text := IntToStr(Number);
end;

Para que servem OnGetEditMask, OnGetEditText e OnSetEditText do TStringGrid

O evento OnGetEditMask ocorre quando entramos no modo de edição. Neste momento podemos verificar em qual linha/coluna se encontra o cursor e então, se quiser, poderá especificar uma máscara de edição. Exemplo:

procedure TForm1.StringGrid1GetEditMask(Sender: TObject; ACol, ARow: Integer; var Value: String);
begin
  if (ARow = 1) and (ACol = 1) then
  Value := '(999) 999-9999;1;_'; // Telefone
end;
O evento OnGetEditText ocorre também quando entramos no modo de edição. Neste momento podemos manipularmos o texto da célula atual (linha/coluna) e então podemos simular algo tal como uma tabela onde opções podem ser digitadas através de números. Exemplo:

procedure TForm1.StringGrid1GetEditText(Sender: TObject; ACol, ARow: Integer; var Value: String);
begin
  if (ARow = 1) and (ACol = 2) then begin
  if StringGrid1.Cells[ACol, ARow] = 'Ótimo' then
  Value := '1'
  else if StringGrid1.Cells[ACol, ARow] = 'Regular' then
  Value := '2'
  else if StringGrid1.Cells[ACol, ARow] = 'Ruim' then
  Value := '3';
  end;
end;

O evento evento OnSetEditText ocorre quando saímos do modo de edição. Neste momento podemos manipular a entrada e trocar por um texto equivalente. Normalmente usamos este evento em conjunto com o evento OnGetEditText. Exemplo:

procedure TForm1.StringGrid1SetEditText(Sender: TObject; ACol, ARow: Integer; const Value: String);
begin
  if (ARow = 1) and (ACol = 2) then begin
  if Value = '1' then
  StringGrid1.Cells[ACol, ARow] := 'Ótimo'
  else if Value = '2' then
  StringGrid1.Cells[ACol, ARow] := 'Regular'
  else if Value = '3' then
  StringGrid1.Cells[ACol, ARow] := 'Ruim'
  end;
end;
Observações

Para testar o exemplo anterior crie um novo projeto e coloque no Form1 um TStringGrid. Mude os três eventos mencionados conforme os exemplos. Execute e experimente digitar nas céluas 1 e 2 da primeira linha (na parte não fixada).

domingo, 8 de maio de 2011

Tutorial para iniciante sobre JOIN's


Para aqueles que tinham dúvidas quanto ao funcionamento dos JOIN's das tabelas no SQL, desenvolvi um pequeno tutorial de apoio...
Tabelas e seus registros:

TABELA_A
Codigo
Nome
1
um
2
dois
3
três
4
quatro
5
cinco

TABELA B
Lanca
Codigo
Valor
1
1
1.000
2
1
2.000
3
1
5.000
4
2
4.000
5
2
9.000
6
3
7.000
7
5
4.000
8
8
7.000

Para a relação entre as tabelas temos:
3 registros para a empresa 1 (que existe na tabela de empresas);
2 registros para a empresa 2 (que existe na tabela de empresas);
1 registros para a empresa 3 (que existe na tabela de empresas);
0 registros para a empresa 4 (que existe na tabela de empresas);
1 registros para a empresa 5 (que existe na tabela de empresas);
1 registros para a empresa 8 (que NÃO existe na tabela de empresas);

Agora vamos ver como ficariam as pesquisas* (SELECT's) com os JOIN's ( INNER, [ LEFT | RIGHT | FULL ] OUTER ):


* Para tais pesquisas vamos usar a seguinte linguagem:

SELECT [CAMPOS]
FROM "TABELA_DA_ESQUERDA"
[INNER] JOIN | {LEFT | RIGHT | FULL } [OUTER]} JOIN "TABELA_DA_DIREITA"

1) INNER JOIN:

SELECT A.NOME "A.NOME",
B.VALOR "B.VALOR"
FROM TABELA_A A
INNER JOIN TABELA_B B ON B.CODIGO = A.CODIGO
 


A.NOME
B.VALOR
1
UM
1.000
2
UM
2.000
3
UM
5.000
4
DOIS
4.000
5
DOIS
9.000
6
TRÊS
7.000
7
CINCO
4.000


Nas pesquisas com INNER JOIN o resultado trará somente as linhas que sejam comum nas 2 tabelas, ligadas pelos campos das tabelas em questão na pesquisa.


2) LEFT OUTER JOIN:

SELECT A.NOME "A.NOME",
B.VALOR "B.VALOR"
FROM TABELA_A A
LEFT OUTER JOIN TABELA_B B ON B.CODIGO = A.CODIGO
 


A.NOME
B.VALOR
1
UM
1.000
2
UM
2.000
3
UM
5.000
4
DOIS
4.000
5
DOIS
9.000
6
TRÊS
7.000
7
QUATRO
NULL
8
CINCO
4.000

Nas pesquisas com LEFT OUTER JOIN o resultado trará todas os registros que estejam na tabela da esquerda do JOIN (neste caso é a TABELA_A) ao menos 1 vez, mesmo que não tenham registros na tabela da direita do JOIN (neste caso é a TABELA_B) ligadas à tabela da esquerda, como é o caso da linha 7.

3) RIGHT OUTER JOIN:
SELECT A.NOME "A.NOME",
B.VALOR "B.VALOR"
FROM TABELA_A A
RIGHT OUTER JOIN TABELA_B B ON B.CODIGO = A.CODIGO


A.NOME
B.VALOR
1
UM
1.000
2
UM
2.000
3
UM
5.000
4
DOIS
4.000
5
DOIS
9.000
6
TRÊS
7.000
7
CINCO
4.000
8
NULL
7.000

Nas pesquisas com RIGHT OUTER JOIN o resultado trará todas os registros que estejam na tabela da direita do JOIN (neste caso é a TABELA_B) ao menos 1 vez, mesmo que não tenham registros na tabela da esquerda do JOIN (neste caso é a TABELA_A) ligadas à tabela da direita, como é o caso da linha 8.

4) FULL OUTER JOIN:
SELECT A.NOME "A.NOME",
B.VALOR "B.VALOR"
FROM TABELA_A A
FULL OUTER JOIN TABELA_B B ON B.CODIGO = A.CODIGO


A.NOME
B.VALOR
1
UM
1.000
2
UM
2.000
3
UM
5.000
4
DOIS
4.000
5
DOIS
9.000
6
TRÊS
7.000
7
QUATRO
NULL
8
CINCO
4.000
9
NULL
7.000

Nas pesquisas com FULL OUTER JOIN o resultado trará todas os registros, ao menos 1 vez, que estejam nas 2 tabelas, tanto a da esquerda do JOIN (neste caso é a TABELA_A) quanto a da direita do JOIN (neste caso é a TABELA_B), como é o caso das linhas 7 e 9. O FULL poderíamos dizer que é uma junção entre o LEFT OUTER JOIN e o RIGHT OUTER JOIN.

domingo, 1 de maio de 2011

Armazanando sons, vídeos em bancos de dados

Um dos recursos mais interessantes nos bancos de dados atuais é a possibilidade de armazenar recursos multimídia juntamente com outras informações. Dessa forma, é possível criar registros com informações mais ricas sobre um determinado assunto. Por exemplo, pode-se armazenar informações sobre um funcionário juntamente com a sua própria foto. No Paradox, por exemplo, existe um tipo de campo destinado especialmente para esse fim.
No entanto, as possibilidades vão muito além do que o simples armazenamento de imagens. É possível armazenar sons, filmes ou qualquer tipo de arquivo, usando o "obscuro" campo BLOB (Binary Large Object), que permite que qualquer arquivo seja "embutido" no banco de dados. Na verdade, o conteúdo do arquivo não é armazenado no registro em si, mas num arquivo separado que é manipulado internamente pelo banco de dados.
Trabalhar com esse tipo de campo no Delphi é muito simples, mas não tão óbvio à primeira vista. A manipulação de campos BLOB, diferentemente dos campos comuns, não deve ser feita diretamente, mas sim por meio do objeto TBlobField, descendente do TField, que disponibiliza recursos especiais para esse fim. Para isso, você deve sempre se referir ao campo BLOB no código como sendo um TBlobField, utilizando typecasting.
Vamos supor que você tenha um arquivo de som MyWave.wav que será armazenado no campo "SOM" da tabela "MyTable". Para carregar o conteúdo do arquivo no campo SOM, basta fazer assim:
TBlobField(MyTable.FieldByName('SOM')).LoadFromFile('MeuWave.wav');
Neste exemplo, você usou o recurso de typecasting para manipular o campo como sendo um objeto TBlobField, carregando o arquivo através do método LoadFromFile que existe nesse objeto. Fácil, não é?
Se você quiser salvar o conteúdo do campo "SOM" no arquivo "MyWaveCopy.wav", basta fazer assim:
TBlobField(MyTable.FieldByName('SOM')).SaveToFile('MyWaveCopy.60wav');
Para remover o conteúdo do campo "SOM" (limpá-lo), você também deve usar um método apropriado:
TBlobField(MyTable.FieldByName('SOM')).Clear;
Para saber se o campo SOM possui algum conteúdo, use a propriedade IsNull:
with MyTable do
  if not TBlobField(FieldByName('SOM')).IsNull then
  TBlobField(FieldByName('SOM')).SaveToFile('MyWaveCopy.wav');
A maneira mais simples de utilizar o conteúdo de um campo BLOB já armazenado é primeiro gravá-lo novamente em um arquivo em disco, usando o método SaveToFile, e depois manipulá-lo normalmente. No caso de um conteúdo em formato wave, por exemplo, você deve primeiro gravá-lo num arquivo .wav, e então reproduzí-lo através do TMediaPlayer ou pela função SndPlaySound.
Usando os mesmos métodos, você também pode armazenar e manipular filmes AVI, apresentações, documentos, enfim, o que a sua imaginação mandar. Mas tenha em mente que o armazenamento de arquivos de som ou outros tipos vai aumentar consideravelmente o tamanho do seu banco de dados, por isso esse recurso deve ser utilizado com cuidado. Uma boa medida para reduzir esse problema é compactar o arquivo antes de armazená-lo. Para utilizá-lo depois, basta gerar o arquivo novamente em disco e descompactá-lo. Para saber quanto um campo BLOB está ocupando num registro do seu arquivo, em bytes, use a propriedade BlobSize:
TBlobField(MyTable.FieldByName('SOM')).BlobSize;
Obs: Para armazenar imagens ou texto em formato RichText (RTF), use os tipos GRAPHIC e FORMATED MEMO do Paradox, que são destinados a esses tipos de dados. O Delphi também possui objetos para a manipulação desses campos: TGraphicField e TMemoField, ambos descendentes do TBlobField.

sábado, 30 de abril de 2011

Acessando o banco de dados Oracle a partir do Delphi

Em vista de muitos hoje possuírem sistemas rodando com banco de dados Oracle, resolvemos publicar em detalhes todos passos necessários para se conectar a um banco Oracle a partir do Delphi de modo nativo (usando BDE) e através do ODBC. Temos observado também que dúvidas sobre este assunto estão sempre presentes nas listas de discussão sobre Delphi e sobre Oracle.

Utilizamos com bons resultados as versões do Delphi 2.0 até a 4.0, BDE versões 4.5 e 5.0, e o Oracle7 Workgroup Server Release 7.3.2.1. Naturalmente tais informações serão de grande ajuda para configuração em outras versões.

Passos:

1 - Caso tenha instalado em sua máquina algum cliente do Oracle 16 bits, poderá ter algum tipo de conflito com drives de 32 bits. Portanto, desinstale todos os clientes Oracle e instale somente o cliente Oracle 32 bits. Normalmente isto é feito a partir do CD de instalação do Oracle executando o programa d:\win95\install\setup.exe

2 - Ao executar o instalador do cliente Oracle para Windows 95, você deverá de inicio informar o idioma (o mesmo que foi informado durante a instalação do próprio banco), tendo o English como padrão.

3 - Entre com o nome da empresa e o diretório onde serão armazenados os arquivos do cliente Oracle.

4 - Será solicitado o tipo da instalação. Escolha a opção "Selective Products Install".

5 - Será apresentada uma lista dos produtos ou componentes disponíveis. Apesar de poder instalar todos, serão apenas necessários para a conexão com o banco Oracle a partir do Delphi os seguintes componentes:

Sql *Net Client (para criação do alias no cliente Oracle)

Oracle Installer (para instalar/remover componentes)

6 - Selecione os protocolos desejados para comunicação com o banco, ou poderá deixar selecionado a sugestão do instalador e prosseguir.

7 - Após completar 100% da instalação, você visualizará os componentes instalados:

Oracle Installer
Oracle Named Pipes Adapter (protocolo de acordo com sua rede)

Oracle SPX Adapter (protocolo de acordo com sua rede)

Oracle TCP/IP Adapter (protocolo de acordo com sua rede)

Required Support Files

Sql *Net Client

8 - Saia do instalador. Não será necessário reiniciar a máquina por enquanto.

9 - Clique no botão iniciar -> programas -> Oracle for windows 95 -> Sql Net Easy Configuration

10 - Selecione "Add Database Alias", e clique OK

11 - Informe na sequência:

Database Alias (nome na sua máquina que representará o acesso ao banco)

Escolha o Protocolo (normalmente TCP/IP)

TCP/IP Host Name (informe o numero IP do servidor Oracle)

Database Instance (nome da instância do banco, consulte o DBA)

12 - Clique em "yes" e saia do Sql Net Easy Configuration

13 - Chame o BDE Administrator, e clique na guia Configuration -> Drivers ->Native e selecione ORACLE. Como sugestão use as seguintes configurações:

VERSION
  4.0

TYPE
  SERVER

DLL32
  SQLORA32.DLL

VENDOR INIT
  ORA73.DLL

DRIVER FLAG
 (DEIXAR VAZIO)

TRACE MODE
  0

BATCH COUNT
  200

BLOB SIZE
  32

BLOBS TO CACHE
  64

ENABLE BCD
  FALSE

ENABLE INTEGERS
  FALSE

ENABLE SCHEMA CACHE
  FALSE

LANGDRIVER
  (DEIXAR VAZIO)

LIST SYNONYMS
  NONE

MAX ROWS
  –1

NET PROTOCOL
  TNS

OBJECT MODE
  TRUE

OPEN MODE
  READ/WRITE

ROWSET SIZE
 20

SCHEMA CACHE DIR
 (DEIXAR VAZIO)

SCHEMA CACHE SIZE
  8

SCHEMA CACHE TIME
  –1

SERVER NAME
  (COLOQUE O NOME DA INSTANCIA DO BANCO, DEFAULT: ORCL)

SQLPASSTHRU MODE SHARED
  AUTOCOMMIT

SQLQRYMODE
   SERVER

USER NAME
  (NOME DE USUARIO, OPCIONAL)

14 - Clique no item de menu Object -> Apply

15 - Agora precisamos apenas criar um Alias que será enxergado no Delphi. Para isso, clique na guia Database, clique com o botão direito do mouse sobre o item da lista ´Databases´ e selecione a opção ´New´. Escolha a opção ORACLE. Entre com o nome do Alias, que pode ser qualquer um que não exista. Agora altere do lado esquerdo na guia Definition, no item SERVER NAME, e coloque o nome do Database Alias que você criou no Sql Net Easy Configuration.

16 - Clique no item de menu Object -> Apply

17 - Reinicialize seu computador.

18 - Ok, agora basta abrir o Delphi e utilizar este Alias como qualquer outro!

Nomes dos arquivos que estão sendo executados

É comum e até relativamente fácil encontrarmos rotinas para listar todas as janelas abertas. Mas muitas vezes não é apenas o caption das janelas que queremos listar e sim o nome do arquivo executável.

Veja então uma rotina que cria uma lista de strings com esses nomes:

uses TLHelp32; // não esqueça de incluir esta unit
procedure ListProcess(List: TStrings);
var
  ProcEntry: TProcessEntry32;
  Hnd: THandle;
  Fnd: Boolean;
begin
  List.Clear;
  Hnd := CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
  if Hnd <> -1 then
  begin
  ProcEntry.dwSize := SizeOf(TProcessEntry32);
  Fnd := Process32First(Hnd, ProcEntry);
  while Fnd do
  begin
  List.Add(ProcEntry.szExeFile);
  Fnd := Process32Next(Hnd, ProcEntry);
  end;
  CloseHandle(Hnd);
  end;
end;
E para utilizar esta rotina é muito simples, veja:
procedure TForm1.Button1Click(Sender: TObject);
begin
  ListProcess(ListBox1.Items);
end;

Lendo e gravando arquivos de texto

Existem vários métodos em Delphi para gravar arquivos texto a partir de informações gravadas em bases de dados ou para ler arquivos texto e armazená-los em bases de dados. Esta dica apresenta um destes métodos: o uso de TextFiles.

TextFile é um tipo de dado pré-definido no Delphi e corresponde ao tipo Text do Turbo Pascal e do Object Pascal.

Inicialmente para acessar um arquivo de texto, você precisa definir uma variável tipo TextFile, no local que você achar mais apropriado, da seguinte forma:

var arq: TextFile;
Vamos precisar também de uma variável tipo string para armazenar cada linha lida do arquivo:

var linha: String;
Antes de se iniciar a leitura do arquivo, precisamos associar a variavel TextFile com um arquivo fisicamente armazenado no disco:

AssignFile ( arq, 'C:\AUTOEXEC.BAT' );
Reset ( arq );
A rotina AssignFile faz a associação enquanto Reset abre efetivamente o arquivo para leitura. AssignFile corresponde à Assign do Turbo Pascal. Em seguida é necessário fazer uma leitura ao arquivo, para isto utilizaremos a procedure ReadLn:

ReadLn ( arq, linha );
O comando acima lê apenas uma linha de cada vez, assim precisamos de um loop para efetuar várias leituras até que o arquivo acabe. Para verificar o fim do arquivo, utilizaremos a função Eof:

while not Eof ( arq ) do
Agora uma rotina quase completa para fazer a leitura de um arquivo texto. Esta rotina recebe como parâmetro o nome do arquivo que será lido:

procedure percorreArquivoTexto ( nomeDoArquivo: String );
var arq: TextFile;
linha: String;
begin
AssignFile ( arq, nomeDoArquivo );
Reset ( arq );
ReadLn ( arq, linha );
while not Eof ( arq ) do
begin
{ Processe a linha lida aqui. }
{ Para particionar a linha lida em pedaços, use a função Copy. }
ReadLn ( arq, linha );
end;
CloseFile ( arq );
end;
E também uma rotina quase completa para gravação de um arquivo texto. Esta rotina recebe como parâmetro o nome do arquivo que será gravado e uma tabela (TTable) de onde os dados serão lidos:

procedure gravaArquivoTexto ( nomeDoArquivo: String; tabela: TTable );
var arq: TextFile;
linha: String;
begin
AssignFile ( arq, nomeDoArquivo );
Rewrite ( arq );
tabela.First;
while not tabela.Eof do
begin
Write ( arq, AjustaStr ( tabela.FieldByName ( 'Nome' ).AsString, 30 ) );
Write ( arq, FormatFloat ( '00000000.00', tabela.FieldByName ( 'Salario' ).AsFloat ) );
WriteLn ( arq );
tabela.Next;
end;
CloseFile ( arq );
end;
Note nesta segunda rotina, a substituição de Reset por Rewrite logo após o AssignFile. Rewrite abre o arquivo para escrita, destruindo tudo que houver lá anteriormente .

Note também o uso de Write e WriteLn para gravar dados no arquivo texto.

Finalmente note o uso de AjustaStr e FormatFloat para garantir que campos string e numericos sejam gravados com um número fixo de caracteres. FormatFloat é uma rotina do próprio Delphi enquanto AjustaStr está definida abaixo:

function AjustaStr ( str: String; tam: Integer ): String;
begin
while Length ( str ) < tam do
str := str + ' ';
if Length ( str ) > tam then
str := Copy ( str, 1, tam );
Result := str;
end;
O uso da função AjustaStr é fundamental quando você estiver gravando arquivos texto com registros de tamanho fixo a partir de bases de dados Paradox que usualmente não preenchem campos string com espaços no final.

Exibindo as propriedades do arquivo

A dica abaixo apresenta o código de implementação para exibir na tela uma janela padrão Windows de propriedades do arquivo.

Para implementar este procedimento é necessário acrescentar a unit ShellAPI.As propriedades do arquivo são armazenadas numa estrutura chamada TShellExecuteInfo, que corresponde a um registro com os campos: tamanho do arquivo (cbSize), atributos (fMask), nome (lpFile) , shell (lpVerb) e modo de apresentação da janela (nShow).O primeiro passo do procedimento é zerar todas a propriedades da Shell ( FillChar(S,SizeOf(S),0) ), segundo passo é atualizar estas propriedades (With S do ) em relação ao arquivo indicado (Arq :String) e o terceiro passo é abrir a janela de propriedades com os valores atualizados (ShellExecuteEx(@S)).

Código Completo:

Procedure Propriedades(Arq:String);
Var
s:TShellExecuteInfo;
Begin
FillChar(S,SizeOf(S),0);
With S do Begin
cbSize := SizeOf(S);
fMask := SEE_MASK_FLAG_NO_UI or SEE_MASK_INVOKEIDLIST or SEE_MASK_NOCLOSEPROCESS;
wnd := Handle;
lpVerb := 'properties';
lpFile := Pchar(Arq);
nShow := sw_ShowNormal;
End;
ShellExecuteEx(@S);
End;

Criando um diretório

Para criar um diretório você precisa usar a função ForceDirectories, o exemplo a baixo testa se não existe um diretório e cria o diretório apartir de uma variável string testando se o diretório já existe

Unit
 
FileCtrl
 
procedure TForm1.Button1Click(Sender: TObject);
var
  Dir: string;
begin
Dir := 'C:\APPS\SALES\LOCAL';
 
if not DirectoryExists(Dir) then
  ForceDirectories(Dir);
  Label1.Caption := Dir + ' foi criado';
end;

terça-feira, 26 de abril de 2011

Colocar uma barra de progresso na inicialização do sistema

É bem simples, trata-se de abrir por código todas as tabelas na entrada do programa, portanto deixe todas as tabelas fechadas no datamodule.

procedure DataModule.OnCreate;
var Tabela, i: Integer;
begin
  Tabela := 0;
  for I := 0 to ComponentCount -1 do
    if Components[I] is TTable then
      with TTable(Components[I]) do
        if (Tag = 9) and not Active then  // Tag = 9 identifica as tabelas à serem abertas
         try
           Inc(Tabela);  // contador das tabelas q já foram abertas.
           lblInfo.Caption := Format('Abrindo as Tabelas (%d/%d)', [Tabela, Total]);
           {aqui, informo à uma label o andamento do processo, usei o formato totalTabelasAbertas/TotalàAbrir, pode-se trocar para porcentagem ou até usar o ProgressBar.}
           lblInfo.Refresh; // vital para atualizar a tela durante o processo.
           Application.ProcessMessages;
           Open;
        except
   Raise; // quem estiver a fim, pode-se colocar aqui uma verificação, se deu zebra e a zebra é indice corrompido, rodar rotina de recriação de indice da tabela.
end;

Alternando Bitmaps no Fundo de um Form.

Um leitor perguntou recentemente sobre imagens de fundo em Forms.... Apesar de já se ter escrito diversos artigos sobre este tópico, sua questão tinhas algumas novidades. Primeiramente, ele desejava que o fundo se alternasse periodicamente.
Essencialmente, a solução para isto seria a aplicação de outros artigos similares. Em primeiro lugar, para resolver a questão de se ter diversas imagens, os bitmaps devem ser carregados em um array quando o Form for criado. A mudança periódica da imagem s
Finalmente, para tratar do assunto "não leia as imagens do disco", recorremos aos resources. Um arquivo de resource é apenas um caminho para empacotar muito bem qualquer tipo de dados que será anexado ao executável durante o processo de Linkedição. Para o
BITMAP1 BITMAP IMAGE1.BMP
BITMAP2 BITMAP IMAGE2.BMP
BITMAP3 BITMAP IMAGE3.BMP
BITMAP4 BITMAP IMAGE4.BMP
BITMAP5 BITMAP IMAGE5.BMP
A primeira parte de cada linha é o identificador que você utilizará no código para capturar uma imagem em particular. A segunda parte é o tipo de resource (neste caso, bitmap). E a última parte é o nome do arquivo que deve ser utilizado para a imagem. Ist
{$R MYBMPS.RES}
Quando o executável está sendo linkeditado (um passo antes da compilação), o resource será automaticamente anexado ao executável. Para carregar as imagens do resource no array de bitmaps no programa, fiz simplesmente:
    for a := 1 to NumBmps do
    begin
       Bmps[a] := TBitmap.Create;
       Bmps[a].LoadFromResourceName(hInstance,'BITMAP'+IntToStr(a));
    end;
É basicamente isto... A seguir a listagem da Unit principal:
unit Unit1a;
interface
uses
    Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
    ExtCtrls, StdCtrls;
const
    NumBmps = 5;
type
    TForm1 = class(TForm)
        Timer1: TTimer;
        Edit1: TEdit;
        CheckBox1: TCheckBox;
        ListBox1: TListBox;
        procedure FormCreate(Sender: TObject);
        procedure FormDestroy(Sender: TObject);
        procedure FormPaint(Sender: TObject);
        procedure Timer1Timer(Sender: TObject);
    private
       Bmps : Array[1..NumBmps] of TBitmap;
       SelectedBmp : Integer;
    public
    { Public declarations }
    end;
var
    Form1: TForm1;
implementation
{$R *.DFM}
{$R MYBMPS.RES}
procedure TForm1.FormCreate(Sender: TObject);
var
    a : Integer;
begin
    for a := 1 to NumBmps do
    begin
        Bmps[a] := TBitmap.Create;
        Bmps[a].LoadFromResourceName(hInstance,'BITMAP'+IntToStr(a));
    end;
    SelectedBmp := 1;
end;
procedure TForm1.FormDestroy(Sender: TObject);
var
    a : Integer;
begin
    for a := 1 to NumBmps do Bmps[a].Free;
end;
procedure TForm1.FormPaint(Sender: TObject);
var
    x,y,w,h : Integer;
begin
    w := Bmps[SelectedBmp].Width;
    h := Bmps[SelectedBmp].Height;
    for x := 0 to (Width div w) do
        for y := 0 to (Height div h) do
            Canvas.Draw(x*w,y*h,Bmps[SelectedBmp]);
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
    Inc(SelectedBmp);
    if SelectedBmp > NumBmps then SelectedBmp := 1;
    Paint;
end;end.

Técnicas de depuração em Delphi e Prevenção de Bug


Muitas vezes pegamos um sistema legado de outro programador para realizarmos implementação de novas funcionalidades ou corrigir eventuais "bugs". Neste caso você vai precisar depurá-lo para encontrar os "bugs". O Delphi oferece ótimas ferramentas de depuração e quando você sabe como usá-las, você vai economizar muito tempo para corrigir o "bug" e chegar no resultado desejado
Neste caso quero indicar um tutorial que encontrei e ajudou quando o assunto era depuração de programas.
Aqui está o conteúdo:

Project options - antes que você possa começar a utilizar as ferramentas depurador do Delphi, você tem que certificar-se de todas as configurações necessárias são definidas opções depurador .

Breakpoints - Quando pressionar a tecla F5 ou clicando na barra esquerda do seu editor você pode adicionar uma linha vermelha para a sua fonte. Esta linha de origem terão um ponto de interrupção. Ao executar o programa, a execução irá parar quando ele passa a linha de origem. Agora você pode seguir em sua origem, usando algumas teclas de função.

Call stack - a janela Call Stack exibe as chamadas de função que o trouxe para a sua localização atual no programa e os argumentos passados para cada chamada de função.

Local variables - essa janela vai mostrar todas as variáveis locais e o seu valor atual no atual função ou procedimento.

Watches - você adicionar um Watches para controlar os valores das variáveis do programa ou expressões como você passar por cima ou em código de rastreamento.

Idéias para criar os seus próprios recursos de depuração Prevenção Bug Try-Finally Gotchas Try-Except Gotchas

domingo, 17 de abril de 2011

Adicionando o sistema no menu de contexto do Windows Explorer


Aqui vai uma dica para adicionar um item ao menu de contexto do Windows Explorer, exibido ao clicarmos com o botão direito do mouse sobre uma pasta.

Quando o usuário clica com o botão direito sobre uma pasta no Windows Explorer (ou em um componente TShellTreeView) um menu de contexto é exibido. Entre os itens padrões é possível adicionar um para a sua própria aplicação. Quando o usuário clicar neste seu item, o programa será iniciado e o caminho daquele diretório será passado como parâmetro.

A procedure a seguir adiciona uma entrada no registro do Windows para "registrar o programa" como um comando válido no menu de contexto:

procedure EnsureShellFolderPopupItem(const itemName: string);
begin
  with TRegistry.Create do
  try
    RootKey := HKEY_CLASSES_ROOT;
    if OpenKey('Directory\shell', true) then
    begin
      if OpenKey(itemName+'\command', true) then
        WriteString('', '"'+Application.ExeName+'" "%1"');
    end;
  finally
    Free;
  end;
end;

Este código fará a adição de uma chave na seguinte forma:

HKEY_CLASSES_ROOT\Directory\shell\itemName

Onde itemName será passado por parâmetro à função (vide código abaixo) e que na verdade será o texto de exibição no menu de contexto. Seu valor será o caminho do programa e o %1, para indicar que a pasta clicada será enviada por parâmetro à aplicação:

;@="c:\...\Project1.exe" "%1" - Onde "c:\...\Project1.exe" é o local do seu executável.

Agora no evento onCreate do Form, façamos:

procedure TForm1.FormCreate(Sender: TObject);
begin
  //certifica que nosso item está no menu de contexto de diretórios
  //não é necessário chamar sempre, mas para testes está ok!
  EnsureShellFolderPopupItem('Processar com Meu Programa');

  //exibe qual a pasta que foi selecionada para processamento
  if ParamCount > 0 then
    ShowMessage('Pasta selecionada: ' + ParamStr(1))
  else
    ShowMessage('Sistema iniciado diretamente');
end;

Para testar, rode o programa pelo Delphi para que seja processada a procedure EnsureShellFolderPopupItem. Você receberá a mensagem "Sistema iniciado diretamente". Em seguida, feche o programa e clique com o botão direito sobre qualquer diretório no seu computador.