segunda-feira, 28 de fevereiro de 2011

Mudar a letra da unidade usando WMI e Delphi

Fonte: www.activedelphi.com.br

Veja nesta dica uma aplicação console com o código fonte de exemplo para se alterar a letra de unidade de disco (volume) através do WMI. A chave é usar a classe Win32_Volume e alterar a propriedade DriverLetter. Esta propriedade é de leitura e escrita, sendo assim, podemos atualiza-la diretamente e então chamar o método Put_ do objeto SWbemObject.
Para rodar o programa e fazer os testes, solicite no Delphi uma nova "Console Application", apague todo o seu conteúdo e coloque o código abaixo:

program ChangeVolumeLetter_WMI;
 
{$APPTYPE CONSOLE}
 
uses
  SysUtils,
  ActiveX,
  ComObj;
 
procedure  ChangeDriveLetter(OldDrive, NewDrive:Char);
var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;
  FWbemObjectSet: OLEVariant;
  FWbemObject   : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;
begin;
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  FWbemObjectSet:= FWMIService.ExecQuery(
    Format('SELECT * FROM Win32_Volume Where DriveLetter=%s',
           [QuotedStr(OldDrive+':')]),
    'WQL',0);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  if oEnum.Next(1, FWbemObject, iValue) = 0 then
  begin
    //Define a nova letra
    FWbemObject.DriveLetter:=NewDrive+':';
    //Aplica as mudanças
    FWbemObject.Put_();
  end;
end;
 
begin
 try
    CoInitialize(nil);
    try
      //Mudará a letra da unidade E para Z
      ChangeDriveLetter('E','Z');
      Readln;
    finally
      CoUninitialize;
    end;
 except
    on E:Exception do
    begin
        Writeln(E.Classname, ':', E.Message);
        Readln;
    end;
  end;
end.
Aí é só rodar e conferir a letra da unidade modificada, neste exemplo, alterando a unidade E para Z.

Algumas explicações na internet (em inglês):

WMI - Windows Management Instrumentation
Win32_Volume
Método Put_
SWbemObject

sexta-feira, 25 de fevereiro de 2011

Relatório em PDF usando o Delphi 2006


Olá pessoal! Essa dica é pra quem quer exportar seus relatório feitos no QuickReport para arquivos PDF, usando o Delphi 2006 ou superior.
Segue abaixo o código utilizado:
procedure TQRStandardPreview.btnExportarClick(Sender: TObject);
var
  PDFFilt : TQRPDFDocumentFilter;
  FileExt, dir : string;
  I : integer;
begin
  dir := ExtractFilePath( Application.ExeName );
  FileExt := QRPreview.QRPrinter.Title;
  FileExt := FileExt + '.pdf';
  PDFFilt := TQRPDFDocumentFilter.Create( FileExt );
  try
    PDFFilt.AddFontMap( 'WebDings:ZapfDingBats' );
    PDFFilt.TextOnTop := true;
    PDFFilt.LeftMargin := 0;
    PDFFilt.TopMargin := 0;
    PDFFilt.CompressionOn := False;
    PDFFilt.Concatenating := True;
    QRPreview.QRPrinter.ExportToFilter( PDFFilt );
    PDFFilt.EndConcat;
  finally
    PDFFilt.Free;
  end;
end;
Lembrando que devemos acrenscentar na seção uses a unit "QRPDFFilt"

quarta-feira, 23 de fevereiro de 2011

Validando endereços de e-mail


Veja nesta dica uma função que apresenta como validar um endereço de e-mail, evitando que sejam informados dados incorretos nos cadastros.

Segue a implementação:

function ValidaEmail(sEmail: string): boolean;
const
  // Caracteres válidos
  ATOM_CHARS = [#33..#255] - ['(', ')', '<', '>', \@\, ',', ';', ':',
                              '\', '/', '"', '.', '[', ']', #127];

  // Caracteres válidos em uma cadeia
  QUOTED_STRING_CHARS = [#0..#255] - ['"', #13, '\'];

  // Caracteres válidos em um subdominio
  LETTERS = ['A'..'Z', 'a'..'z'];
  LETTERS_DIGITS = ['0'..'9', 'A'..'Z', 'a'..'z'];
  SUBDOMAIN_CHARS = ['-', '0'..'9', 'A'..'Z', 'a'..'z'];

type
  States = (STATE_BEGIN, STATE_ATOM, STATE_QTEXT, STATE_QCHAR,
    STATE_QUOTE, STATE_LOCAL_PERIOD, STATE_EXPECTING_SUBDOMAIN,
    STATE_SUBDOMAIN, STATE_HYPHEN);
var
  State: States;
  i, n, iSubdomains: integer;
  c: char;
begin
  State := STATE_BEGIN;
  n := Length(sEmail);
  i := 1;
  iSubdomains := 1;
  while (i <= n) do
  begin
    c := sEmail[i];
    case State of
      STATE_BEGIN:
        if c in atom_chars then
          State := STATE_ATOM
        else if c = '"' then
          State := STATE_QTEXT
        else
          break;
      STATE_ATOM:
        if c = \@\ then
          State := STATE_EXPECTING_SUBDOMAIN
        else if c = '.' then
          State := STATE_LOCAL_PERIOD
        else if not (c in atom_chars) then
          break;
      STATE_QTEXT:
        if c = '\' then
          State := STATE_QCHAR
        else if c = '"' then
          State := STATE_QUOTE
        else if not (c in quoted_string_chars) then
          break;
      STATE_QCHAR:
        State := STATE_QTEXT;
      STATE_QUOTE:
        if c = \@\ then
          State := STATE_EXPECTING_SUBDOMAIN
        else if c = '.' then
          State := STATE_LOCAL_PERIOD
        else
          break;
      STATE_LOCAL_PERIOD:
        if c in atom_chars then
          State := STATE_ATOM
        else if c = '"' then
          State := STATE_QTEXT
        else
          break;
      STATE_EXPECTING_SUBDOMAIN:
        if c in letters then
          State := STATE_SUBDOMAIN
        else
          break;
      STATE_SUBDOMAIN:
        if c = '.' then
        begin
          Inc(iSubdomains);
          State := STATE_EXPECTING_SUBDOMAIN
        end
        else if c = '-' then
          State := STATE_HYPHEN
        else if not (c in letters_digits) then
          break;
      STATE_HYPHEN:
        if c in letters_digits then
          State := STATE_SUBDOMAIN
        else if c <> '-' then
          break;
    end;
    Inc(i);
  end;

  if i <= n then
    Result := False
  else
    Result := (State = STATE_SUBDOMAIN) and (iSubdomains >= 2);

  //se sEmail esta vazio retorna true
  if sEmail = '' then
    Result := true;
end;

Para testar, adicione a um novo formulário um Edit e um Button, programando no evento onClick deste último:

procedure TForm1.Button1Click(Sender: TObject);
begin
  if ValidaEmail(Edit1.Text) then
    ShowMessage('Ok! E-mail válido!')
  else
    ShowMessage('E-mail inválido!');
end;

Agora rode o programa e faça os testes digitando vários endereços de e-mail do Edit1 e clicando sobre o botão.

terça-feira, 22 de fevereiro de 2011

Automação de Queries

Fonte: www.activedelphi.com.br

Desenvolvendo um sistema para uma clínica escola de psicologia, identifiquei que, apesar de o sistema estar bem avançado, estava cheio de códigos de manipulação de query repetidos, e por isso resolvi mudar esta situação. A solução, você confere abaixo
Os códigos eram todos parecidos, como abaixo:

  with dm.query do
  begin
    close;
    sql.Clear;
    sql.Add('select * from tabela where campo = valor');
    open;
  end;

Então fiz uma procedure que com uma linha faz tudo isso:

class procedure Tdm.qrcon(componente: Tadoquery; tabela, campo, valor: string; 
  operacao: integer);
begin
  case operacao of
    //seleciona tudo
    1: with componente do
      begin
        close;
        sql.Clear;
        sql.Add('select * from ' + tabela);
        open;
      end;
    2: with componente do
      begin
        close;
        sql.Clear;
        sql.Add('select * from ' + tabela + ' where ' + campo + 
                ' = ' + quotedstr(valor));
        open;
      end;
    3: with componente do
       begin
        close;
        sql.Clear;
        sql.Add('delete from ' + tabela + ' where ' + campo + ' = ' + valor);
        execsql;
       end;
  else
    MessageBox(0, 'Erro de parametro de consulta.' + #13#10 + 
                  '          Contate o CPD.', 
                  'Erro de parametro de consulta.', MB_ICONSTOP or MB_OK);
  end;
end;

Converter Minutos Para Horas

Fonte: www.activedelphi.com.br


Veja nesta dica uma simples função que retorna a quantidade de horas a partir de uma quantidade de minutos.

function MinutosEmHoras(Minutos: Integer): String;
var
  HoraDecimal, HH, MM: String;
begin
  if Minutos > 1440  then
    Minutos := Minutos - 1440;
  HoraDecimal := FormatFloat( '00.00', Minutos / 60 );
  HH := Copy( HoraDecimal, 1 , 2 );
  if Copy( HoraDecimal, 4, 5 )[1] = '0' then
    MM := '0' + IntToStr( Round( ( StrToInt( Copy( HoraDecimal, 4, 5 ) ) * 60 ) /100 ) )
  else
    MM := IntToStr( Round( ( StrToInt( Copy( HoraDecimal, 4, 5 ) ) * 60 ) /100 ) );
  Result :=  HH+ ':' + MM ;
end;

Para utilizá-la, faça:

  ShowMessage( MinutosEmHoras(480) );
  //retornará: '08:00'

segunda-feira, 21 de fevereiro de 2011

Abrindo arquivos com os aplicativos associados e tratando exceções

Fonte: www.activedelphi.com.br


Você já deve ter ouvido falar no comando ShellExecute da unit ShellApi, certo? Se não ouviu, conheça-o agora. Para quem já conhece e o utiliza, costuma tratar os possíveis retornos desta função? Não? Então veja nesta dica como fazer o tratamento!


// Esta procedure requer a unit ShellApi declarada na cláusula Uses da unit.
// Declare a procedure na cláusula private da unit e coloque-a após a cláusula
// implementation, assim: procedure Tform1.ExecFile(F: String);
// use-a assim: ExecFile('c:\windows\Ladrilhos.bmp')
procedure ExecFile(F: String);
var
  r: String;
begin
  case ShellExecute(Handle, nil, PChar(F), nil, nil, SW_SHOWNORMAL) of
    ERROR_FILE_NOT_FOUND:   r := 'O arquivo especificado não foi encontrado ' +
                                 'ou não existe!';
    ERROR_PATH_NOT_FOUND:   r := 'O Caminho é inválido ou não existe!';

    ERROR_BAD_FORMAT:       r := 'O Aplicativo está corrompido ou não é um ' +
                                 'Aplicativo Win32 valido!';
    SE_ERR_ACCESSDENIED:    r := 'O sistema negou acesso a este arquivo por ' +
                                 'algum motivo desconhecido!';
    SE_ERR_ASSOCINCOMPLETE: r := 'Este arquivo tem uma associação inválida ' +
                                 'ou incompleta a ele!';
    SE_ERR_DDEBUSY:         r := 'A transação DDE não pode ser efetuada por ' +
                                 'já haver outra Transação DDE em andamento';
    SE_ERR_DDEFAIL:         r := 'Não foi possível efetuar a transação DDE!';

    SE_ERR_DDETIMEOUT:      r := 'A transação DDE não pode ser efetuada ' +
                                 'porque o tempo requerido expirou!';
    SE_ERR_DLLNOTFOUND:     r := 'Uma Biblioteca DLL necessária ao ' +
                                 'aplicativo associado não foi encontrada!';
    SE_ERR_NOASSOC:         r := 'Este arquivo não tem nenhum aplicativo ' +
                                 'associado à ele!';
    SE_ERR_OOM:             r := 'memória insuficiente para prosseguir com' +
                                 'esta operação!';
    SE_ERR_SHARE:           r := 'Ocorreu uma violação de compartilhamento ' +
                                 'ao efetuar esta operação!';
  else
    exit;
  end;
 
  ShowMessage(r);
end;
Este comando também pode ser utilizado para abrir páginas da Web com o navegador padrão, passando no lugar do caminho do arquivo, um endereço de um site iniciado com "http://", ex:


  ExecFile('http://www.activedelphi.com.br')

Convertendo BMP em JPG

Fonte: www.activedelphi.com.br

Esta dica mostra uma função que converte um arquivo BMP em um arquivo JPG, mostrando também como fazer a compacatação do arquivo ajustando o nível de qualidade da imagem. Primeiro, vamos adicionar a unit JPEG ao uses do formulário.
Agora, façamos a seguinte função:

function BmpToJpg(cImage: String): Boolean;
var
  MyJPEG: TJPEGImage;
  MyBMP : TBitmap;
begin
  Result := False;
  if fileExists(cImage + '.bmp') then
  begin
    MyBMP := TBitmap.Create;
    with MyBMP do
      try
        LoadFromFile(cImage + '.bmp');
        MyJPEG := TJPEGImage.Create;
        with MyJPEG do
        begin
          Assign(MyBMP);
          //"Descomente" e ajuste as linhas abaixo para compactar a imagem, o que
          //poderá perder qualidade mas ajudará a diminuir o tamanho do arquivo
          //CompressionQuality := 75; //min. 1 - max. 100
          //Compress;
          SaveToFile(cImage + '.jpeg');
          Free;
          Result := True;
        end;
      finally
        Free;
      end;
  end;
end;
E para testar, supondo que haja um arquivo chamado ImagemTeste.BMP no diretório raiz C:\, basta fazer:

procedure TForm1.Button1Click(Sender: TObject);
begin
  BmpToJpg('C:\ImagemTeste');
end;

sexta-feira, 18 de fevereiro de 2011

Desenvolvendo um Navegador

Fonte: www.activedelphi.com.br

istemas que não permitem ao usuário utilizar outros programas do computador enquanto estão sendo usados, e este precisa consultar algo na web, por exemplo
Obs.: É bom lembrar que sempre vale a pena salvar o projeto várias vezes durante o seu desenvolvimento.

Primeiro vamos criar a interface do navegador.

Inicie um novo projeto no delphi e altere as seguintes propriedades do formulário:
Caption: Navegador
Name: frmNavega
Width: 770

Insira um panel presente na guia Standard, alterando as propriedades:
Caption: apague todo o seu conteúdo
Align: AlTop

Insira no panel dois BitBtn presente na guia Addtional, e da mesma forma altere as suas seguintes propriedades:
BitBtn 1
Caption: apague todo o seu conteúdo
Name: btVoltar
Height: 25
Left: 8
Top: 8
Width: 33
Glyph: esta propriedade define a imagem de exibição do bitbtn. Geralmente ao instalar o delphi, ele copia uma serie de imagens para utilizarmos em nossas aplicações. Provavelmente elas estão instaladas no diretório: C:\Arquivos de programas\Arquivos comuns\Borland Shared\Images\Buttons. Neste exemplo, utilizaremos duas imagens presente nesta pasta, para isto selecione o botão [...]. Será exibida a caixa Picture Editor. Nela, clique em load e acesse a pasta Buttons do diretório acima. Nesta pasta selecione o item ARROW1L.

BitBtn 2
Caption: apague todo o seu conteúdo
Name: btAvancar (na propriedade name, não use caracteres de pontuação como o ‘ç’, o ‘´’)
Height: 25
Left: 48
Top: 8
Width: 33
Glyph: na mesma pasta usada para o botão anterior, selecione o item ARROW1R.

Insira no panel uma edit
Name: Endereco
Height: 21
Left: 88
Top: 10
Width: 505
Text: apague o seu conteúdo.

Insira mais dois BitBtn:
BitBtn 3
Caption: Parar
Name: btParar
Height: 25
Left: 600
Top: 8
Width: 75
Kind: esta propriedade tem uma função parecida com a Glyph, porém possui imagens pré-definidas. Selecione o item bkAbort.

BitBtn 4
Caption: Atualizar
Name: btAtualizar
Height: 25
Left: 680
Top: 8
Width: 75
Kind: bkRetry

Obs.: Ao utilizar a propriedade kind, o Delphi automaticamente altera o caption do BitBtn para o valor padrão do kind.

Por ultimo insira no form um componente WebBrowser, presente na guia Internet:
Align: alClient
Name: WebAgora vamos começar a codificar nosso navegador.

Primeiro, dê um duplo clique sobre o botão voltar e insira o seguinte código no evento:

  Web.GoBack;
Faça o mesmo para o botão avançar, só que agora com o código:

  Web.GoForward;
Para o botão parar:

  Web.Stop;
E para o botão atualizar:

  Web.Refresh;
No evento onTitleChange do componente web, insira o código:

  Caption := Text + ' - Navegador';
Esta linha define que, toda vez que o título da página for alterado, a propriedade caption do formulário será igual à propriedade text do componente web, mais ' - Navegador'.

No evento onDownloadComplete do componente web insira o código:

  Endereco.Text := web.LocationURL;
Este código faz com que o valor da edit endereco assuma o endereço da página atual. Caso não for usado, ao utilizar os botões voltar e avançar o endereço continuaria o mesmo do site que estava aberto.

No evento onkeyPress da edit endereco insira o código:

  if Key = #13 then
  begin
    Key := #0;
    web.Navigate(Endereco.Text);
  end;
Neste bloco de comandos verificamos se a variável Key possui valor igual a “#13” (código do ENTER). Em caso afirmativo, mudamos seu valor para “#0” a fim de evitar um bip desagradável e informamos à aplicação que o endereço de navegação está na propriedade text da edit endereco.

Obs.: O componente WebBrowser utiliza a mesma estrutura do internet explorer.

Retornando o Idioma Padrão do Sistema Operacional

Fonte: www.activedelphi.com.br

Você já precisou saber qual o idioma padrão do Windows em que seu programa está sendo executado? Se ainda não precisou, provavelmente precisará e esta dica vai lhe ajudar

function IdiomaPadrao: string;
var
  WinLanguage: array [0..50] of char;
begin
  VerLanguageName(GetSystemDefaultLangID, WinLanguage, 50);
  Result := StrPas(WinLanguage);
end;

Esta simples função faz chamada à respectiva função na API do Windows que retorna o idioma padrão configurado no computador. Para testá-la, basta colocar esta função na sua unit e no evento onClick de um botão, por exemplo, fazer:

procedure TForm1.Button1Click(Sender: TObject);
begin
  Showmessage( IdiomaPadrao );
end;

quinta-feira, 17 de fevereiro de 2011

Login Integrado ao Active Directory


O Active Directory é um serviço da Microsoft, lançado no Windows 2000, que utiliza o protocolo LDAP para armazenar informações de usuários em uma determinada rede. Com ele, o administrador de redes tem todas as informações necessárias para controlar cada usuário da rede, como: nome, login e senha, bem como a criação de políticas de grupos de usuários.

Atualmente, há uma grande necessidade de se ter um login unificado dentro de uma organização, para que o usuário não tenha que ficar "decorando" login e senha de cada sistema e para que os desenvolvedores e o suporte técnico ganhem tempo, uma vez que não haverá mais tantos cadastros espalhados pela empresa. Sendo assim, vamos demonstrar neste artigo como integrar o login de sua aplicação no Active Directory.


Configure os componentes da seguinte forma:

Componente Nome Valor
TLabel lblLogin LOGIN
TLabel lblUsuario Usuário:
TLabel lblSenha Senha:
TEdit edtUsuario
TEdit edtSenha
TButton btnLogin Entrar

Primeiramente, vamos criar um novo projeto: File/New/VCL Forms Application - Delphi. Neste exemplo, criei uma tela básica de Login, como segue:

Configure os componentes da seguinte forma:

O Active Directory Service Interfaces (ADSI) nos dá uma interface COM para interagirmos com o Active Directory, portanto, vamos adicionar a unit ActiveX na uses list para utilizarmos esta interface.

Para que possamos validar o login e a senha do usuário, necessitamos utilizar uma interface requerida em objetos ADSI para capturar algumas propriedades e um método para comparar estas informações com os objetos do Active Directory. Para tanto, adicionemos em nossa aplicação as units ActiveDs_Tlb e Adshlp, as quais se encontram no final deste artigo, juntamente com o código fonte deste projeto-exemplo, não se esquecendo de adicioná-las também à aplicação.

Configure o evento onClick do btnLogin, como segue:

procedure TfrmLogin.btnLoginClick(Sender: TObject);
var
  adObject: IADs;
begin
  ///Inicialização do COM
  CoInitialize(nil);
  try
    ADsOpenObject('://',
                  LowerCase(edtUsuario.Text),
                  edtSenha.Text,
                  ADS_SECURE_AUTHENTICATION,
                  IADs,
                  adObject);
    ShowMessage('Login válido!');
  except
    on e: EOleException do
    begin
      if Pos('Falha de logon', e.Message) > 0 then
        ShowMessage('Login inválido!')
      else
        ShowMessage(e.Message);
    end;
  end;
  CoUninitialize;
end;
Como podemos ver, inicializamos o COM e depois utilizamos um método para comparação de nosso login e senha com os objetos do Active Directory. Neste método, passamos o provider WinNT ou LDAP, o nome do domínio da rede que estamos conectados, o login e senha que o usuário digitou, a forma de autenticação ao Active Directory, a interface e um objeto IADs que criamos localmente. Se houver erro nesta comparação, significa que o login e senha digitados não foram encontrados em nenhum objeto do Active Directory ou há algum problema na comunicação ou conectividade da rede.



Clique aqui para baixar o código fonte do exemplo (323 KB)

quarta-feira, 16 de fevereiro de 2011

Executar o Windows Explorer em uma pasta específica


Veja nesta dica um código simples, capaz de abrir via programação o Windows Explorer, iniciando-o em uma pasta específica. Para compilar, é necessário acrescentar ao uses a unit ShellApi, e codificar a seguinte função:

function ExecExplorer(OpenAtPath: string; OpenWithExplorer,
  OpenAsRoot: Boolean): Boolean;
var
  s: string;
begin
  if OpenWithExplorer then
  begin
    if OpenAsRoot then
      s := ' /e,/root,"' + OpenAtPath + '"'
    else
      s := ' /e,"' + OpenAtPath + '"';
  end
  else
    s := '"' + OpenAtPath + '"';

  result := ShellExecute(
    Application.Handle,
    PChar('open'),
    PChar('explorer.exe'),
    PChar(s),
    nil,
    SW_NORMAL) > 32;
end;
Para testar, podemos fazer, por exemplo:

  ExecExplorer('C:\Temp', True, True);

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);

domingo, 6 de fevereiro de 2011

Chega de Showmessage!!!

Se você pensa..."Que Mensagem para validação de campos é muito chato!". Vai encontrar uma maneira bem simples de fugir deste terror.
O que veremos neste artigo é ao invés de mandarmos uma mensagem para o usuário, que é muito chato, você poder fazer o usuário se interagir com o sistema, veja...
Para este Artigo vamos precisar criamor uma Procedure.
Dei o Nome desta Procedure de TrocaCor com a sintaxe abaixo

Procedure TrocaCor(Campo : TWinControl ; CorTrocada , CorMeio, CorFinal : TColor ; Tempo : Cardinal );

O intuito desta rotina é o Seguinte:

- no lugar de mandarmos uma mensagem, faremos com que o campo "pisque", e que receba o foco depois deste "pisca-pisca"

o Parâmetro Campo, é do Tipo WinControl , pois poderá ser um TEdit, TComboBox , TListBox e Etc...(que recebam foco).

Os Parâmetro de Cores:
-CorTrocada
-CorMeio
-CorFinal

São do Tipo TColor, pois são elas as responsáveis de fornecedor o efeito de "piscar".

E a última, Tempo, serve para executar em qual período de Tempo;

vamos Lá Entao:

Implemente esta procedure com CTRL + SHIFT + C, e vamos aos códigos:

if (Campo is TEdit) Then
Begin
  (Campo as TEdit ).Color := CorTrocada;
  (Campo as TEdit).Update;
  Sleep(Tempo);
  (Campo as TEdit).Color := CorMeio;
  (Campo as TEdit).Update;
  Sleep(Tempo);
  (Campo as TEdit).Color := CorTrocada;
  (Campo as TEdit).Update;
  Sleep(Tempo);
  (Campo as TEdit).Color := CorFinal;
  (Campo as TEdit).SetFocus;
End //TEdit
else if (Campo is TComboBox) Then
Begin
  (Campo as TComboBox ).Color := CorTrocada;
  (Campo as TComboBox).Update;
  Sleep(Tempo);
  (Campo as TComboBox).Color := CorMeio;
  (Campo as TComboBox).Update;
  Sleep(Tempo);
  (Campo as TComboBox).Color := CorTrocada;
  (Campo as TComboBox).Update;
  Sleep(Tempo);
  (Campo as TComboBox).Color := CorFinal;
  (Campo as TComboBox).SetFocus;
  End;//TComboBox
End;

Bom, no caso acima eu imaginei que os campos fossem somente de dois tipos, TEdit e TComboBox, mas você poderá ir além disso, ou mais além ainda. Criar uma variável onde armazene a classe e economizar nos códigos...;)

Vamos usá-la Agora:
Coloque um botão no Formulário, um Edit e um ComboBox

E no envento OnClick do Botão Digite:
if Edit1.Text = 'Edit1' Then
Begin
  TrocaCor(Edit1 , clRed, clBlue, clBlack , 50);
  Exit;
End;

Entendendo, o código acima testará se o conteúdo do Edit1 for = Edit1
se Sim ele Executa nossa procedure e sai da Rotina. num intervalo de 50 milisegundos

Lembrando que quanto menor o número mais rápido será a piscagem....

Questões de segurança em banco de dados


Uma das principais preocupações nos dias de hoje é com a segurança das informações que estão armazenadas nos bancos de dados dos sistemas dos nossos clientes.
Para tanto, os desenvolvedores gastam muito tempo com o desenvolvimento de mecanismos que protejam estas informações, como senhas de acesso, níveis de segurança nos programas e políticas de usuários nos bancos de dados.

O que muitos analistas ou desenvolvedores não sabem ou não levam em consideração, é a segurança, não do sistema, não do banco de dados como um servidor, mas dos ARQUIVOS que compões este banco de dados.

Em muitas vezes, enquanto enumerava os benefícios que meu sistema traria a empresa, caso ele substituísse o sistema em uso, eu mostrava esta fragilidade, disfarçada em fortes esquemas de proteção via software.

No início, eram as tabelas PARADOX. Elas, aparentemente, podiam proteger os dados utilizando uma senha "embutida" diretamente nas tabelas. O sistema, contendo entrincados meios de autenticar os usuários, "abriam" estas tabelas com a senha pré-definida.

A principal falha deste método reside no fato de que a senha está em algum ponto no executável, e com um editor de arquivos que mostre hexadecimal, poderá ser encontrada.

Os programadores, motivados pela perspicácia dos invasores, resolveram codificar a senha, mas a necessidade de se obter informações sempre é mais forte, e os invasores começaram a ver que:

"A corrente parte sempre em seu elo mais fraco"

Como os programas estavam cada vez mais protegidos, as atenções se  voltaram para os bancos de dados. Afinal, o objetivo é roubar a informação e não invadir o programa.

Recuperar dados de um sistema não é a coisa mais difícil do mundo quando o programador se apoia somente na segurança do sistema operacional e do servidor de banco de dados. No caso do PARADOX, eu lembro quando recebi um desafio de pegar os dados de uma pessoa em um sistema de cadastro que possuía vários métodos de segurança.
O sistema realmente era bem montado, com senha e políticas de segurança, e o banco continha senha.
Um pouco de tempo na internet e eu consegui acessar todos os dados, com um programa de quebra senhas de tabelas PARADOX, o que mostrou que este tipo de banco de dados não é muito seguro se não for bem estruturado.

Para efeito de didática, eu disponibilizo o programinha em meu site.
Clique aqui para baixar...

Por favor, não o use em bancos de dados de terceiros.

Na empresa em que trabalhei, os programadores (e analistas) tinham o péssimo hábito de trabalhar com o Interbase da pior forma possível:

Eles colocavam uma máquina para ser o servidor do Interbase, até aí tudo bem.
Depois, acreditem, compartilhavam a pasta onde estava o banco de dados GDB usando o compartilhamento de pastas do Windows. Em seguida, as estações que rodavam o programa, acessavam o banco de dados através do mapeamento de pastas da Microsoft, criando um drive para a pasta compartilhada do servidor e acessando o GDB usando o protocolo local.

Eu considero isto a maior heresia de todos os tempos em se tratando de Interbase...
Me perdoem os programadores que lerem este artigo e adotam esta doutrina.

Não fossem todos os problemas com o banco de dados que esta prática causa, ela afeta diretamente a segurança do banco, visto que o Interbase protege os dados a nível de servidor, ou seja, toda a segurança do interbase se baseia na pemissa de que o cliente não acessará o arquivo diretamente, mas sim através da engine do servidor,
que aplicará os métodos de segurança elaborados pelo DBA (users e roles).

Tudo isso significa que, o banco de dados, na forma de arquivo, não possui capacidade para se proteger. Caso o arquivo GDB seja copiado para outro servidor, o SYSDBA poderá acessar os dados da mesma forma (ou usuários com os mesmos nomes). Então, basta instalar um servidor Interbase com a senha padrão do SYSDBA e colocar lá o GDB que se quer invadir.

Obviamente, se o GDB está em uma pasta compartilhada, não será necessário colocar senhas nos programas nem no banco de dados, pois serão inúteis...

Uma outra prática muito usada pelos programadores, talvez por desconhecer o uso correto da conexão IB, é usar o formato \\\Pasta\Arquivo.GDB como DatabaseName do IBDataBase.
Este formato também força o cliente a conectar-se ao banco em modo LOCAL, visto que esta é uma convenção de nomes de rede para compartilhamento de pastas, o que indica que o servidor não estará sendo usado.
Neste caso, o formato correto seria: ::\Caminho\Arquivo.GDB
Sendo "DRIVE" e "caminho" fazendo referência ao sistema de arquivos do SERVIDOR.
Este método é muito eficiente e permite que o IB seja conectado até através da internet, pois dispensa o uso do NetBios.

A primeira vista, soluções simples poderão ser adotadas para proteger os dados e dar um pouco mais de trabalho para a pessoa que desejar roubar informações.

A principal premissa a se assumir é:
"Meu servidor sempre estará exposto de alguma forma"

No caso do PARADOX, eu desaconselho o uso. Nada contra o banco de dados, mas eu não acho que ele tenha sido criado para sistemas que estão em produção. Comparando com os servidores SQL atuais de baixo custo (e até grátis), ele não é a melhor solução.

No caso do Interbase, é obvio que o GDB jamais poderá cair em mãos erradas.
Para evitar, ou diminuir o risco, o ideal é, acima de tudo, utilizar o protocolo de rede para acessar o servidor.

Uma aproximação mais eficiente seria utilizar camadas de acesso ao servidor.
Este método só traria vantagens para o usuário e para o programador.
Para melhorar ainda mais a segurança, a rede em que está localizado o servidor poderia ser diferente da rede onde estão os usuários (não fisicamente, mas logicamente).

Um computador com duas placas de rede (e dois endereços IP) poderia conter a camada de acesso, que é visível aos usuários. Esta camada receberia os usuários por uma interface de rede e se comunicaria com o servidor pela outra. Esta abordagem tornaria o servidor simplesmente inacessível diretamente aos usuários, pois ele estaria em uma faixa de IP incompatível com a rede deles.

Caso o programador não tenha acesso aos métodos descritos, seria ideal utilizar a criptografia para proteger os dados. Escrevi alguns artigos na revista (foram matéria de capa das edições 14 e 20) e alguns artigos no site que descrevem como melhorar a segurança dos dados sem confiar esta tarefa ao servidor ou a máquina onde o servidor foi instalado.

Seguem os links destes artigos:

http://www.activedelphi.com.br/modules.php?op=modload&name=News&file=article&sid=236 
http://www.activedelphi.com.br/modules.php?op=modload&name=News&file=article&sid=145
http://www.activedelphi.com.br/modules.php?op=modload&name=News&file=article&sid=127
http://www.activedelphi.com.br/modules.php?op=modload&name=News&file=article&sid=123

Uma outra saída, e talvez a mais eficiente, seria manter o nível de paranóia acima do normal (indicado como seguro pelos médicos) e criptografar tudo o que ver pela frente.
Isto inclui todos os dados do banco nas tabelas e também o tráfego na rede!
Eu já ultrapassei a barreira da sanidade ao fazer isso, como um bom gestor de segurança da informação...

Caso o leitor queira chegar a estas fronteiras, faça o download do servidor de SQL que estou aprimorando (sim, é um derivado do FlashFiler da TurboPower). Este servidor de SQL tem todos os componentes para o Delphi e com a vantagem de criptografar tudo o que é armazenado nas tabelas e também criptografar o que é transmitido entre o servidor e os clientes.
A desvantagem é que será uma má notícia para o DBA quando ele descobrir que não lembra da senha do banco ao retornar de férias...

quarta-feira, 2 de fevereiro de 2011

Gaste tempo e depois ganhe mais ainda ao ler este artigo


Algumas idéias citadas abaixo ajudarão profissionais que trabalham com banco de dados a serem mais produtivos e atingir metas firmadas no acordo de nível de serviços (SLAs)
Não paramos e não podemos parar, desde o momento em que acordamos até a hora de se deitar. O mundo está cada vez mais competitivo, lutando por sobrevivência, e podemos afirmar que certamente neste momento há alguém estudando e aprendendo novas tecnologias ou tentando inventar um novo “Google”, por exemplo. Ninguém tem tempo a perder, ao ler este artigo você está gastando ele, mas espero que após a leitura lhe ajude a ganhar outros tantos que lhe dará tempo e uma vida mais calma.

A rotina de quem trabalha com dados é incessante, a infinidade de lugares, formatos e tamanhos, no qual é obrigado a controlar para manter simples conjuntos de bytes em algo compreensível é importante, faz com que se gaste muito tempo criando e executando rotinas estressantes que exigem muita habilidade e conhecimentos técnicos.


Hoje em dia os Databases Administrators(DBAs) são obrigados a gerenciar ambientes complexos que podem envolver diferentes plataformas de banco de dados, nas quais dependem de uma série de tarefas e rotinas, que precisam ser aprendidas em horas e dominadas em dias para serem aplicadas em todas as instâncias. Além disso tem a necessidade de aprender a utilizar a ferramenta de gestão de cada fabricante, que aplicam conceitos diferentes e tornam o aprendizado ainda mais demorado.


Uma forma de resolver estes problemas e também é uma dica que ofereço, é escolher uma ferramenta que permita gerenciar de maneira igual diferentes tipos de banco de dados, permitindo que o DBA domine uma única ferramenta e o seu aprendizado já seja inserido e replicado em qualquer plataforma de banco de dados, direcionando a preocupação do DBA em resolver o problema rápido, sem intervenção das peculiaridades de uma plataforma.


Ao pensarmos em modelagem, muitos a utilizam na fase de criação da estrutura do banco de dados e após a produção da aplicação abandonam o modelo, deixando obsoleto e sem manutenção. Desta maneira começam a surgir as famosas “gambiarras” para manter o sistema em funcionamento, mas de forma “sofrível”.


A modelagem não deve ser apenas utilizada na fase de criação da aplicação, ao integrar a ferramenta de modelagem no banco de dados, possibilitará ainda controle de todo o ciclo de vida e das mudanças, que forem realizadas e propagadas de maneira mais rápida, contribuindo também para uma documentação atualizada e sempre disponível de forma automática.


Outro conselho é escolher uma ferramenta de modelagem que propicie a empresa trabalhar de forma articulada e centralizada, permitindo que analistas de negócios façam o levantamento de requisitos e necessidades de seus usuários, reduzindo o mal entendimento e possibilitando que áreas especialistas em captação de informação possam trabalhar de maneira integrada e automatizada com DBAs e arquitetos de dados. Isso tudo porque ao realizar o modelo conceitual que foi gerado a partir de uma necessidade de negócio será permitido a geração do modelo do banco de dados lógico e físico já direcionado ao usuário, evitando desperdício de esforço aplicado em retrabalho.


Ao termos um modelo atualizado e em uso podemos, ir mais além e, oferecer aos desenvolvedores acesso ao dicionário de dados padronizado seguindo templates pré-fabricados para serem reutilizados pelos desenvolvedores, evitando que eles “reinventem a roda”. Ao sugerir e criar alternativas ou até mesmo novos objetos para armazenar dados, o desenvolvimento de software tem conseguido ser satisfatório seguindo implementações como Gang of Four (GoF) e UML patterns ,do mesmo modo a construção de um banco de dados não precisa ser feito de maneira diferente.


Arquitetos podem disponibilizar para os desenvolvedores estruturas e associações que necessitarão ser reutilizadas com frequência como dados cadastrais de clientes ou produtos, indicando os campos obrigatórios e tipos de dados utilizados, o ganho de tempo estará na constante reutilização e na transformação dos dados para serem movidos ou carregados via ETL.


Boas práticas de mercado são ótimas para serem lidas, nos ajudam a entender como o mercado trabalha e para onde estamos caminhando. Entretanto sabemos que muitas vezes a cultura e até mesmo a dificuldade em adotar uma ou outra técnica acabam dependendo de diversos fatores, mas ao mesmo tempo em que você ou sua empresa não adota ou não se adapta ao mundo de hoje, serão obrigados a viver o mundo de ontem, no qual a tecnologia e seus concorrentes não fazem questão de lembrar.

Função FormatDateTime


FormatDateTime é uma função que retorna uma string de acordo com o formato passado como 1º parametro, tendo como base o 2º parametro que é a data. Veja nesta dica todos os detalhes e exemplos de uso desta função
Ela está presente na unit SysUtils (Delphi 7), com a seguinte implementação:
function FormatDateTime(const Format: string; DateTime: TDateTime): string;
begin 
  DateTimeToString(Result, Format, DateTime); 
end;
Segue um exemplo de uso, para ser colocado no evento onClick de um button, por exemplo:
begin 
  ShowMessage(FormatDateTime('dd/mm/yyyy', (Now + 1))); 
end;
Se hoje for 17/12/2010, este código resultará em uma mensagem contendo:
‘18/12/2010'.
Uma das grandes facilidade que a função nos traz é durante a interação com um banco de dados. Exemplo para o mySQL:

begin 
  MyQuery.SQL.Text := Format( 
    'SELECT * FROM `log` WHERE `date` BETWEEN ''%s'' AND ''%s''', 
    [ 
      // Data inicial 
      FormatDateTime('yyyy-mm-dd', (Now)),
      // Data final 
      FormatDateTime('yyyy-mm-dd', (Now + 1)) 
    ]); 
end;
Veja agora a tabela com os formatos possíveis e seus respectivos resultados, considerando a data e hora "02/09/2008 07:04:01":
Sintaxe Finalidade Resultado
FormatDateTime('c', Now) Resulta na Data e hora com o formato ShortDateFormat + LongTimeFormat. (Se for exatamente meia-noite não irá mostrar a hora) 02/09/2008 07:04:01
FormatDateTime('d', Now) Resulta no valor número que representa o Dia do mês ( sem 0 na frente ) 2
FormatDateTime('dd', Now) Resulta no valor número que representa o Dia do mês ( com 0 na frente [00] ) 02
FormatDateTime('ddd', Now) Resulta na abraviação do nome do dia da semana ter
FormatDateTime('dddd', Now) Resulta no nome do dia da semana terça-feira
FormatDateTime('ddddd', Now) Resulta na data formatada usando ShortDateFormat 02/09/2008
FormatDateTime('dddddd', Now) Resulta na data formatada usando LongDateFormat terça-feira, 2 de setembro de 2008
FormatDateTime('m', Now) Resulta no valor número que representa o mês ( sem 0 na frente ) 9
FormatDateTime('mm', Now) Resulta no valor número que representa o mês ( com 0 na frente [00] ) 09
FormatDateTime('mmm', Now) Resulta na abreviação do nome do mês set
FormatDateTime('mmm', Now) Resulta no nome do mês setembro
FormatDateTime('y', Now) Resulta no ano em dois digitos 08
FormatDateTime('yyyy', Now) Resulta no ano em quatro digitos 2008
FormatDateTime('h', Now) Resulta nas horas ( sem 0 na frente ) 7
FormatDateTime('hh', Now) Resulta nas horas ( com 0 na frente [00] ) 07
FormatDateTime('n', Now) Resulta nos minutos ( sem 0 na frente ) 4
FormatDateTime('nn', Now) Resulta nos minutos ( com 0 na frente [00] ) 04
FormatDateTime('s', Now) Resulta nos segundos ( sem 0 na frente ) 1
FormatDateTime('ss', Now) Resulta nos segundos ( com 0 na frente [00] ) 01
FormatDateTime('z', Now) Resulta nos milisegundos ( sem 0 na frente ) 31
FormatDateTime('zzz', Now) Resulta nos milisegundos ( com 0 na frente [000] ) 031
FormatDateTime('t', Now) Resulta na hora formatada usando ShortTimeFormat 07:04
FormatDateTime('tt', Now) Resulta na hora formatada usando LongTimeFormat 07:04:01
FormatDateTime('am/pm', Now) Reajusta a hora resultada para horários de 12 horas (manha e tarde)



segunda-feira, 31 de janeiro de 2011

Programa - Simulação de Lucro Com Rendimento Mensal


Galera Delphiana! Bom, esta dica que escrevo é muito simples e se trata de um simulador de rendimento de aplicação financeira mensalmente.
O arquivo do projeto encontra-se compactado e disponivel para download aqui (7,64 KB).
Dentro dele, estão todos os códigos desenvolvidos, que não são nada complexos.

Apagando itens selecionados de um ListBox

 
Esta dica mostra como apagar vários itens de uma só vez de um ListBox, não importando se eles são selecionados em sequência ou aleatoriamente. Lembrando que para selecionarmos vários itens de um ListBox, sua propriedade MultiSelect deve estar ativada. Segue o código:
//A procedure tem que ser declarada na sessão "Public" ou "Private" da unit
procedure TForm1.ApagaVarios (var ListBox: TListBox);
var
  i: integer;
  lista: TStringList;
begin
  for i:=0 to ListBox.Items.Count-1 do
    if ListBox.Selected[i] then
      ListBox.Items[i] := '';

  lista:= TStringList.Create;
         
  for i:=0 to ListBox.Items.Count-1 do
    if ListBox.Items[i] <> '' then
      lista.Add(ListBox.Items[i]);

  Listbox.Clear;
  ListBox.Items.Assign(lista);  
  lista.Free;
end;
Para usá-la, coloque um componente ListBox e um componente Button em seu form. Adicione alguns itens no ListBox e ative a sua propriedade MultiSelect, conforme comentado no início e, no onClick do botão, faça o código que chama a procedure para apagar os itens selecionados:

procedure TForm1.Button1Click(Sender: TObject);
begin
  ApagaVarios(ListBox1);
end;
Por: David
Contato: sapodiou2@bol.com.br



Dica do editor

Segue abaixo uma outra forma de se fazer o mesmo procedimento, de maneira mais otimizada:

procedure TForm1.ApagaVarios2(var ListBox: TListBox);
var
  i: integer;
begin
  i := 0;
  while i <= ListBox.Items.Count-1 do
  begin
    if ListBox.Selected[i] then
      ListBox.Items.Delete(i)
    else
      i := i + 1;
  end;
end;