terça-feira, 15 de fevereiro de 2011

Executável que se auto-deleta


Veja nessa dica de nosso colunista Handem Vogel como implementar a auto-exclusão do programa em execução. Primeiramente, declare o procedimento
procedure TForm1.DeletaExe;
Var
  Arquivo: TextFile;
begin
  AssignFile(Arquivo, ChangeFileExt(ParamStr(0), '.bat'));
  try
    ReWrite(Arquivo);
    WriteLn(Arquivo, ':1');
    WriteLn(Arquivo, Format('Erase "%s"', [ParamStr(0)]));
    WriteLn(Arquivo, Format('If exist "%s" Goto 1', [ParamStr(0)]));
    WriteLn(Arquivo, Format('Erase "%s"', [ChangeFileExt(ParamStr(0), '.bat')]));
  finally
    CloseFile(Arquivo);
  end;
  WinExec(PChar(ChangeFileExt(ParamStr(0), '.bat')), sw_hide);
  Halt;
end;
Agora, no evento onClose do formulário, basta chamar a procedure acima, com o seguinte código:

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  DeletaExe;
end;
Isto fará com que o executável se "auto-exclua" assim que fechado!

segunda-feira, 14 de fevereiro de 2011

Procedimentos com parâmetros opcionais:

Quando você declara o procedimento:

procedure Esperar(Segundos: Byte);
Você está determinando que todas as vezes que o procedimento. Esperar for chamado, deverá ser passado um valor do tipo Byte. No entanto, esse tipo de declaração exige que em todas as chamadas ao procedimento Esperar seja especificado um parâmetro.

Se você fizer uma chamada do tipo:

procedure TForm1.Button1Click(Sender: TObject);
begin
  Esperar()
end;
Será gerado um erro do tipo: Not enough actual parameters. Mas você pode declarar e implementar o procedimento da seguinte forma:

procedure Esperar(Segundos: Byte = 1);
begin
  Sleep(Segundos * 1000);
end;
A declaração acima faz com que o procedimento Esperar assuma o valor 1 caso nenhum parâmetro seja passado. Assim você poderá fazer uma chamada ao procedimento em qualquer das situações abaixo:

procedure TForm1.Button1Click(Sender: TObject);
begin
  Esperar(); // nenhum parâmetro, será assumido o valor 1
  Esperar(1);
  Esperar // nenhum parâmetro, será assumido o valor 1
end;

Cuidados quando criar procedimentos e funções com parametros

Quando criamos procedimentos e funções podemos introduzir parâmetros com
valores defaultdevemos porém respeitar algumas regras:

Esse parâmetros precisam ocorrer no final da lista;

precisam ser constantes

Não pode fazer por referência(var alguma coisa)

Respeitando essas regras o delphi permite criar sem problemas funções ou
procedimentos do tipo:

function Resizer(X: Real; Y: Real = 2.0): Real;
procedimento Multiplicar(var X:Real;X:Real=1;Resposta:Real=0);
etc...

Se vc chamar "resizer" sem os parâmetros o delphi considera que vc quer
usar os valores default da função....

Tenha MUITA ATENÇÃO PORÉM com funções ou procedimentos que forma
declarados em overload ou "sobrecarga" que permite que dois ou mais
procedimentos ou funções tenham nomes iguais desde que tenham parâmetros
diferentes.Isso criaria problemas na lógica do compilador....Pense, O
que difere um procediemnto do tipo:

1)procedure teste(A:integer);overload

2)procedure teste(A:integer=0);overload;

Se vc chamar teste(x) o compilador não sabe se vc está querendo chamar o
procedimento 1 ou 2...

Função de potenciação - Juros

Segue abaixo uma função para efetuar a potenciação. É útil para compor formulas financeiras, como a de VP ("PV" valor presente) VF ("FV" valor futuro).

Exemplo:
Calcular o valor de um produto para o prazo de 30 dias com a taxa de juros de 5% mês.

var

 i: Real; // taxa de juros
 valor: Real; // valor base para calculo do valor futuro.
 pz: Integer // prazo em dias
begin
  i := 5//100;
  valor:= 1000.00
  pz := 30
  Result:= valor*( Pot( (1+i), (pz/30) ) //Resultado 1.050,00
end;
no excel a Pot é substituída pelo sinal ^ Ex. =E18*((1+C19)^(C20/30))

Function Pot( base, expoente: real ):real; // Potenciação
begin
        { utiliza a função de exponencial e de logaritmo }
     Result:= Exp((expoente * Ln( base )));
end;
 
Dicas:
Não amplie o nome da função, pois as funções financeiras costumam ser bem extensas.

domingo, 13 de fevereiro de 2011

Atualizando Dados em Rede com Zeos


Procurei muito na internet sobre como atualizar as informações de minha aplicação nas estações. Tentei todos os tipos de comandos e funções que eu poderia fazer e não funcionou. Os dados só eram visualizados após reiniciar o programa. Foi aí que descobri a solução
Nas propiedades do ZConnection (deve ser a antepenultima opção, 3ª de baixo para cima no Object Inspector), está a TransactIsolationLevel. Mude-a de itNone para tiReadCommited.
Pronto! Faça a alteração de um registro em qualquer estação e ao fazer uma busca novamente em um outro computador / executável, já verá carregado as novas informações.

sábado, 12 de fevereiro de 2011

Gerando imagem para validação de números.


Gerando imagem para validação de números
Hoje em dia está sendo muito usado a geração de imagens para confirmação de alguma ação. Um exemplo disse é o email da uol, onde você tem uma opção de solicitar a confirmação do envio.
Bom, segue uma função que faz isso:

Primeiro adicione no seu form os componentes TButton, TImage e TEdit.

unit Unit1;
  interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls;
type
  TForm1 = class(TForm)
  Image1: TImage;
  Button1: TButton;
  Edit1: TEdit;
  procedure Button1Click(Sender: TObject);
  procedure FormShow(Sender: TObject);
private
  { Private declarations }
public
 { Public declarations }
  function GeraImagem(Img: TImage): string;
end;
 
var
  Form1: TForm1;
  validapost : string;
implementation
{$R *.dfm}
{ TForm1 }
function TForm1.GeraImagem(Img: TImage): string;
  const
    f: array [0..4] of string =  ('Courier New', 'Impact', 'Times New Roman', 'Verdana', 'Arial');
    s = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
    C : ARRAY [0..14] OF tcOLOR = (clAqua, clBlack, clBlue, clFuchsia, clGray, clGreen, clLime, clMaroon, clNavy, clOlive, clPurple, clRed, clSilver, clTeal, clYellow);
 
var
  i, x, y : integer;
  r : string;
begin
  randomize;
  Img.Width := 160;
  Img.Height := 60;
  for i := 0 to 3 do
  r := r + s[Random(length(s)-1)+1];
  with Img.Picture.Bitmap do
  begin
    width := Img.Width;
    Height := Img.Height;
    Canvas.Brush.Color := $00EFEFEF;
    Canvas.FillRect(Img.ClientRect);
    for i := 0 to 3 do
    begin
      Canvas.Font.Size := random(20) + 20;
      Canvas.Font.Name := f[High(f)];
      Canvas.Font.Color := c[random(High(c))];
      Canvas.TextOut(i*40,0,r[i+1]);
    end;
    for i := 0 to 2 do
    begin
      Canvas.Pen.Color := c[random(High(c))];
      Canvas.Pen.Width := 2;
      canvas.MoveTo(random(Width),0);
      Canvas.LineTo(random(Width),Height);
      Canvas.Pen.Width := 1;
      x := random(Width-10);
      y := random(Height-10);
      Canvas.Rectangle(x,y,x+10,y+10);
    end;
  end;
  Result := r;
end;
 
procedure TForm1.Button1Click(Sender: TObject);
begin
  if (Edit1.Text = validapost) then
    Application.MessageBox('Parabéns, muito bem','Sucesso',
  MB_OK + MB_ICONINFORMATION);
  FormShow(self);
end;
 
procedure TForm1.FormShow(Sender: TObject);
begin
  validapost := GeraImagem(Image1);
end;
 
end.

terça-feira, 8 de fevereiro de 2011

Chave Estrangeira (Foreign Key) com DBExpress e ClientDataSet


Com o uso de DBExpress não existe mais a hipótese de campos Lookup para se trazer um determinado campo de uma chave estrangeira, como fazíamos antigamente. Isso porque para criarmos um campo Lookup, precisaríamos deixar a tabela aberta por completo na memória para que os outros datasets pudessem buscar a informação nela através do campo Lookup, e isto sai completamente dos conceitos do ClientDataSet...
    Para resolver este problema, usa-se JOINs em instruções SQL, que "performaticamente" falando, são mais rápidos, porém, um pouco mais trabalhoso, no sentido em que, ao digitarmos os dados para um ClientDataSet, estes ficam na memória e o Join não é executado no BD, até que o dado seja gravado. Dessa maneira, ao informarmos o ID da chave estrangeira não veríamos o campo que desejamos até que este registro seja gravado. Porém, pode-se “forçar” a busca deste campo através de uma função que você pode mais abaixo.

    Um cenário interessante para podermos exemplificar é um cadastro de venda, onde na tabela de vendas existe uma chave estrangeira da tabela de clientes para se gravar o ID do cliente que esta efetuando aquela compra. Se estivermos fazendo uma consulta, automaticamente o JOIN faz o seu papel e retorna o NOME deste cliente, se a SQL for algo do tipo:
SELECT
  V.CODVENDA,
  V.DATA,
  V.CODCLIENTE,
  C.NOME AS NOME_CLIENTE
FROM
  VENDAS V
     INNER JOIN CLIENTES C ON V.CODCLIENTE = C.CODCLIENTE

    Porém, ao inserir um novo registro o JOIN não é executado como já comentamos, pois os dados não estão no banco ainda, somente em Cache. Resolvendo isso, a função:

function GetFieldByID(var SQLConn: TSQLConnection;
                                   Table, ResultField, IDField: String;
                                   ID: integer): string;
var qry: TSQLQuery;
      SQL: String;
begin
  Result := '';
  SQL:='SELECT ' + ResultField + ' FROM ' + Table + ' WHERE '+ 
  IDField + '=' + IntToStr(ID);
  qry := nil;
  try
    SQLConn.Execute(SQL, nil, @qry);
    if Assigned(qry) then
      Result := qry.FieldByName(ResultField).AsString;
  finally
    qry.Free;
  end;
end;

Entendendo a Função

    Passamos para a função qual o SQLConnection (SQLConn) responsável pela execução da qry e também informamos em qual Tabela (Table) ela deverá buscar, através de qual campo (IDField) e qual ela irá trazer (ResultField), além é claro do ID que estamos procurando.
    A função monta então a SQL e a executa no SQLConnection especificado. Este por sua vez, retorna um ResultSet que fica armazenado na variável qry (que é um TSQLQuery). Se o SQLConnection não retornar o ResultSet é porque a SQL não retornou nada caso contrario (if Assigned(qry)), retorna para o Result da função o valor do campo pesquisado com a SQL montada.

    Para usá-la, no evento OnValidate do campo CODCLIENTE basta fazer:

cdsVendas.FieldByName('NOME_CLIENTE').AsString := GetFieldByID(SQLConnection1,'CLIENTES','NOME','CODCLIENTE',Sender.AsInteger);

    Você também pode tratar o campo verificando se a função retornou algum valor, e se não, gerar uma exceção avisando que o código não foi encontrado, por exemplo:

Var str: string;
begin
  str:=GetFieldByID(SQLConnection1,'CLIENTES','NOME','CODCLIENTE',Sender.AsInteger);
  if str<>'' then
    cdsProdutor.FieldByName('RAZAOS').AsString := str
  else
  begin
    MessageDlg('Código Não Encontrado!', mtWarning, [mbOK], 0);
    Abort;
  end;
end;

    A vantagem de se usar esta função, é que muda-se pouco de seu uso para outros campos, por exemplo, se quiséssemos saber o nome do vendedor supondo que este também esteja previsto na SQL através de um JOIN, seria:

cdsVendas.FieldByName('NOME_VENDEDOR').AsString :=  GetFieldByID(SQLConnection1,'VENDEDORES','NOME','CODVENDEDOR',Sender.AsInteger);

Curso de Delphi: 7.Consultas SQL