quarta-feira, 26 de outubro de 2016

Descobrindo a chave do Windows 7,8,10

Execute o programa CMD como administrador e cole o comando abaixo para descobrir a chave do windows

wmic path SoftwareLicensingService get OA3xOriginalProductKey

terça-feira, 30 de agosto de 2016

Dez dicas para criar aplicações profissionais Android com Delphi XE5 e Firemonkey

Fonte TDevRocks

# 1. Salve um atalho na tela inicial depois de Instalar

Se você quer que os usuários sejam capazes de encontrar o seu aplicativo novamente depois de instalá-lo, você deve adicionar o ícone à tela inicial do usuário. O usuário pode removê-lo ou ele será automaticamente removido se desinstalar o aplicativo.

# 2. Proteger as conexões de dados do seu aplicativo com SSL

Os usuários estão se conectando a partir de hotspots públicos nos dias de hoje e você precisa proteger essas conexões de dados para manter os dados seguros. Algumas lojas de aplicativos como a Amazon App Store vai negar o seu aplicativo se você não usar conexões seguras para dados de login do seu usuário.

# 3. Criar e implantar ícones para seu aplicativo

Você vai precisar para criar um bom número de diferentes tamanhos de ícones para implantar seu aplicativo e fazer o upload para cada loja de aplicativos de for distribuir. Há um utilitário gratuito disponível, que torna isso mais fácil.

# 4. Manipular eventos de sistema Ativar e Desativar a App

Quando os usuários multitarefa usando o Android , há eventos de sistema específicos que serão disparados. Você deve adicionar esses eventos no seu aplicativo e agir conforme necessário.

# 5. Adicionar um Splash Screen App Carregando…

Apps Android podem demorar alguns segundos para carregar, onde o usuário pode ficar em dúvida sobre o que seu aplicativo está fazendo. Você pode configurar uma SplashScreen para que o usuário veja o seu logotipo ou algo semelhante enquanto carrega o seu app.

# 6. Manter o foco no controle em edição acima do teclado virtual

Quando o teclado virtual aparece por padrão ele irá cobrir a caixa que está sendo editado, se for abaixo da metade inferior da tela. Você pode rolar o formulário para que o campo que está sendo editado permaneça visível.

# 7. Use as caixas de diálogo de progresso para manter seu app responsivo

Se seu aplicativo está fazendo uma tarefa que vai levar algum tempo, como baixar ou salvar um arquivo você deve mostrar uma caixa de diálogo para o usuário com o progresso de modo que a sua aplicação não pareça congelada. Você pode usar qualquer um dos vários recursos que o Delphi te oferece.

# 8. Verifique a conectividade de rede antes de tentar ligar

Você deve verificar se o usuário está conectado à internet antes de chamar funções que necessitem conexão com a internet. Desta forma, você pode notificar o usuário se ele não tiver conectividade e seu aplicativo pode lidar com isso adequadamente.

# 9. Segurar o botão voltar do Android

Dispositivos Android tem um botão de volta o que você deve lidar para que seus usuários tenham um fluxo correto dentro de sua aplicação.

# 10. Salvar configurações entre Sessões

Você pode usar tanto TIniFile e TMemIniFile para salvar as configurações no Android assim como você faria no Windows. Tenha certeza de usar TPath.GetDocumentsPath + PathDelim para obter o local certo para salvar o arquivo INI. Uma segunda maneira de salvar as configurações seria com SQLite.

A TDevRocks fez a tradução das dicas para você, mas caso deseje ler o artigo em inglês, acesse esse link.

segunda-feira, 8 de agosto de 2016

Como copiar um arquivo em Delphi usando a função CopyFile() da API do Windows

Há situações nas quais gostaríamos de copiar um determinado arquivo. Até a versão 2009 as bibliotecas de tempo de execução do Delphi não nos fornecia uma função ou procedure para realizar tal tarefa. No entanto, é possível chamar a função CopyFile() da WinAPI a partir de nossas aplicações sem quaisquer esforços adicionais. Esta função requer o nome e caminho do arquivo a ser copiado, o nome e caminho do novo arquivo e um valor true ou false indicando se a função deve falhar caso o segundo arquivo já exista. O retorno será true se a operação for realizada com sucesso e false em caso contrário.

Veja um trecho de código no qual efetuamos a cópia de um arquivo:

procedure TForm1.Button1Click(Sender: TObject);
var
  arquivo_original, novo_arquivo: string;
begin
  // diretorio e nome do arquivo original
  arquivo_original := 'C:\arquivo de codigos\dados.txt';

  // diretorio e nome do novo arquivo
  novo_arquivo := 'C:\arquivo de codigos\dados2.txt';

  // vamos copiar o arquivo
  if CopyFile(PChar(arquivo_original), PChar(novo_arquivo), true) then
    ShowMessage('O arquivo foi copiado com sucesso')
  else
    ShowMessage('Não foi possível copiar o arquivo');
end;

segunda-feira, 11 de abril de 2016

Aprenda a executar comandos SQL usando o método ExecSQL() da classe TSQLDataSet

O método ExecSQL() da classe TSQLDataSet é usado quando queremos executar comandos SQL que não retornam um conjunto de dados. Este comando pode ser INSERT, UPDATE, DELETE, CREATE TABLE, etc, exceto SELECT.

Veja a assinatura deste método:

function ExecSQL(ExecDirect: Boolean = False): Integer; override;

ExecDirect é usado para indicar que a query não precisa ser preparada antes de ser executada. Este parâmetro pode ser definido como True se o comando não incluir nenhum parâmetro. Se o valor for False, a query será preparada antes de ser executada.

Vamos ver um exemplo? Veja um trecho de código no qual usamos o método ExecSQL() para disparar um comando SQL UPDATE. Note que aqui estamos usando um comando SQL preparado (pré-compilado):

procedure TForm3.Button1Click(Sender: TObject);
var
  nome: string;
  id: integer;
begin
  nome := 'OSMAR J. SILVA';
  id := 2; // id do registro a ser atualizado

  // vamos definir o comando SQL a ser executado
  SQLDataSet1.CommandText := 'UPDATE contatos SET nome = :nome WHERE id = :id';
  SQLDataSet1.Params[0].Name := 'nome';
  SQLDataSet1.Params[0].Value := nome;
  SQLDataSet1.Params[1].Name := 'id';
  SQLDataSet1.Params[1].Value := id;

  // como o comando é preparado, vamos definir o valor
  // False para o parâmetro ExecDirect
  SQLDataSet1.ExecSQL(False);

  // vamos mostrar uma mensagem indicando o sucesso da operação
  ShowMessage('Comando SQL executado com sucesso.');
end;

É importante observar que o método ExecSQL() retorna o número de linhas afetadas pelo comando. O valor retornado se torna o valor da propriedade RowsAffected. Veja como modificar o código anterior para retornar a quantidade de linhas afetadas:

procedure TForm3.Button1Click(Sender: TObject);
var
  nome: string;
  id: integer;
  linhasAfetadas: integer;
begin
  nome := 'OSMAR J. SILVA';
  id := 2; // id do registro a ser atualizado

  // vamos definir o comando SQL a ser executado
  SQLDataSet1.CommandText := 'UPDATE contatos SET nome = :nome WHERE id = :id';
  SQLDataSet1.Params[0].Name := 'nome';
  SQLDataSet1.Params[0].Value := nome;
  SQLDataSet1.Params[1].Name := 'id';
  SQLDataSet1.Params[1].Value := id;

  // como o comando é preparado, vamos definir o valor
  // False para o parâmetro ExecDirect
  linhasAfetadas := SQLDataSet1.ExecSQL(False);

  // vamos mostrar uma mensagem indicando o sucesso da operação
  ShowMessage('Comando SQL executado com sucesso. ' +
    IntToStr(linhasAfetadas) + ' linhas afetadas.');
end;

Lembre-se que não podemos usar ExecSQL() com comandos que retornam dados. Estes comandos incluem todos os comandos do tipo ctTable, queries SELECT e stored procedures que retornam um cursor. Quando o comando retornar dados, devemos usar o método Open ou definir a propriedade Active do SQLDataSet como True.

Como habilitar ou desabilitar o Gerenciador de Tarefas do Windows usando Delphi

O trecho de código nesta dica mostra como podemos usar Delphi e a API do Windows para habilitar ou desabilitar o Gerenciador de Tarefas. O Gerenciador de Tarefas é invocado quando usamos a combinação Ctrl+Alt+Del e permite ao usuário visualizar os aplicativos e processos sendo executados no momento. Além disso, o usuário pode selecionar um determinado processo e finalizá-lo.

Em algumas situações gostaríamos que o usuário não tivesse acesso ao Gerenciador de Tarefas. Veja como podemos desabilitá-lo no evento Click de um botão:

procedure TForm3.Button5Click(Sender: TObject);
var
  reg: TRegistry;
begin
  // uses Registry

  // vamos criar uma instância da classe TRegistry
  reg := TRegistry.Create;

  // vamos abrir a chave que contém a entrada DisableTaskMgr
  reg.OpenKey('Software\Microsoft\Windows\CurrentVersion\Policies\system',
    True);

  // vamos desabilitar o Gerenciador de Tarefas
  // se o valor fornecido for 1, o gerenciador será desabilitado
  reg.WriteString('DisableTaskMgr', '1');
  reg.CloseKey;

  // para habilitar basta excluir o valor da entrada
  //reg.DeleteValue('DisableTaskMgr');
  //reg.CloseKey;

  // vamos liberar o registro 
  reg.Free;
end;

Agora, quando o usuário pressionar Ctrl+Alt+Del ele verá a mensagem "O 'Gerenciador de tarefas' foi desabilitado pelo administrador". É importante observar que, no Windows XP, o usuário pode habilitar ou desabilitar o Gerenciador de tarefas simplesmente abrindo uma janela de terminal e digitando "gpedit.msc". Este comando abrirá o Editor de Diretivas de Grupo e permitirá ao usuário habilitar ou desabilitar o gerenciador de tarefas novamente.

Note que para habilitar o Gerenciador de Tarefas novamente só precisamos remover os comentários do trecho:

// para habilitar basta excluir o valor da entrada
//reg.DeleteValue('DisableTaskMgr');
//reg.CloseKey;

segunda-feira, 7 de março de 2016

Criando sua primeira DLL usando Delphi

Em dicas anteriores você aprendeu o que é um DLL e sua importância para a programação no ambiente Windows. Nesta dica mostrarei como criar uma DLL bem simples e usá-la a partir de um programa Delphi. Para isso siga atentamente os passos abaixo:

1) Vá em File -> New -> Other;

2) Selecione Delphi Projects e escolha DLL Wizard;

3) Neste momento o DLL Wizard criará o esqueleto do código fonte da DLL. Note que o código desta unit é bem parecido com aquele da unit de uma aplicação. A diferença principal é o uso da palavra-chave library em vez de program:

library Project2;

{ Important note about DLL memory management: ShareMem must be the
  first unit in your library's USES clause AND your project's (select
  Project-View Source) USES clause if your DLL exports any procedures or
  functions that pass strings as parameters or function results. This
  applies to all strings passed to and from your DLL--even those that
  are nested in records and classes. ShareMem is the interface unit to
  the BORLNDMM.DLL shared memory manager, which must be deployed along
  with your DLL. To avoid using BORLNDMM.DLL, pass string information
  using PChar or ShortString parameters. }

uses
  SysUtils,
  Classes;

{$R *.res}

begin
end.

Salve esta unit como MinhaDLL.dproj em um diretório de sua preferência. Em seguida vamos criar uma rotina que receberá dois valores inteiros e retornará sua soma. Veja a modificação no código:

library MinhaDLL;

function Somar(a, b: Integer): Integer; stdcall;
begin
  Result := a + b; // retorna a soma
end;

{ vamos exportar (export) a função Somar }
exports
  Somar;

begin
end.

Nossa DLL está pronta. Agora vá no menu Project -> Build MinhaDLL (ou pressione Shift+F9). Se tudo correu bem você terá um arquivo chamado MinhaDLL.dll no diretório no qual você salvou o projeto. O próximo passo é aprender a carregar esta DLL a partir de seus programas Delphi. Veja mais dicas nesta seção para aprender como isso é feito.

sábado, 5 de março de 2016

Carregando uma DLL a partir de seus programas Delphi usando Dynamic loading (carregamento dinâmico)

Por carregamento dinâmico (Dynamic loading) entendemos a técnica de carregar e liberar uma DLL (Dynamic Link Library) sempre que quisermos sem a necessidade da criação de uma unit de importação (como fazemos com o carregamento estático).

O processo de carregar uma DLL dinâmicamente envolve os seguintes passos:

a) Devemos declarar uma variável ou tipo procedure ou function que descreve a rotina que queremos chamar na DLL. Se a DLL foi escrita em outra linguagem diferente de Delphi, teremos que traduzir os tipos de dados. Você pode verificar como esta tradução é feita olhando as declarações de importações na unit Windows.pas e comparando com as mesmas funções na API do Windows;

b) Usamos a função LoadLibrary() da API do Windows para carregar a DLL;

c) Efetuamos uma chamada à função GetProcAddress() para obter um ponteiro para o endereço da rotina na DLL;

d) Chamamos a rotina desejada;

e) Chamamos a função FreeLibrary() da API do Windows para liberar a DLL, devolvendo ao sistema a memória alocada durante a sua execução.

Para esta demonstração faremos uso da DLL MinhaDLL.dll, criada em uma das dicas nesta mesma seção.

O primeiro passo é saber a assinatura da função contida na DLL e que pretendemos usar em nosso programa Delphi. A DLL MinhaDLL.dll contém uma função Somar() que recebe dois Integer e retorna a soma como um Integer:

function Somar(a, b: Integer): Integer; stdcall;
begin
  Result := a + b; // retorna a soma
end;

Uma vez que já sabemos a assinatura da função que queremos chamar na DLL, podemos escrever o seguinte código Delphi:

procedure TForm3.Button1Click(Sender: TObject);
type
  // vamos declarar um tipo function
  TSomarFuncao = function(a, b: Integer): Integer; stdcall;
var
  Somar: TSomarFuncao; // uma variável que representará a função
  DLLHandle: THandle; // este é o handle para a DLL
begin
  // vamos carregar a DLL
  DLLHandle := LoadLibrary('MinhaDLL.dll');
  try
    // vamos obter o endereço da função na DLL
    Somar := GetProcAddress(DLLHandle, 'Somar');

    // vamos chamar a função agora
    if Assigned(Somar) then
      ShowMessage(IntToStr(Somar(4, 3)))
    else
      ShowMessage('Não foi possível chamar a rotina desejada');
  finally
    FreeLibrary(DLLHandle); // vamos liberar a DLL
  end;
end;

Note que, no carregamento dinâmico, se a DLL não puder ser encontrada nada acontecerá, a menos que você remova o bloco try...finally do código, o que geraria um erro de acesso à memória. Dentro da IDE do Delphi veríamos a mensagem:

Debugger Exception Notification

Project Project2.exe raised exception class EAccessViolation with
message 'Access violation at address 00000000. Write of address 00000000'.

Fora da IDE a mensagem de erro é bem parecida. Lembre-se de que o os aplicativos procuram as DLLs primeiro no diretório local e em seguida nos diretórios Windows, System e System32.

Os índices das matrizes em Delphi sempre começam em 0, como no Java e C/C++?

Uma das dúvidas mais frequentes de programadores vindos de linguagens tais como Java, C/C++, C#, etc, é em relação ao índice inicial de uma matriz em Delphi. Em outras linguagens, é comum o índice de uma matriz começar sempre em 0. Felizmente, o Delphi nos oferece uma flexibilidade maior.

Em Delphi nós podemos definir o índice inicial da matriz para o valor que desejarmos. Veja alguns exemplos:

procedure TForm3.Button1Click(Sender: TObject);
var
  valores: array[5..10] of Integer;
  codigos: array[5000..10000] of Integer;
  letras: array[Ord('a')..Ord('z')] of Char;
begin
  // alguma implementação aqui
end;

Note que é possível termos o mesmo valor para o índice inicial e final da matriz. Mas, não é possível definir o índice inicial maior que o índice final. Veja:

procedure TForm3.Button1Click(Sender: TObject);
var
  valores: array[7..5] of Integer;
begin
  // alguma implementação aqui
end;

Ao tentarmos compilar este trecho de código teremos a seguinte mensagem de erro:

[DCC Error] Unit3.pas(90): E2011 Low bound exceeds high bound.

Como passar uma matriz de Integer para uma procedure ou function

Em algumas situações precisamos passar uma matriz para uma procedure ou function (função). Nesta dica eu mostro como isso pode ser feito. Veja que a procedure exibirMatriz(matriz: array of Integer) possui uma matriz aberta (open array) como parâmetro. Esta é a forma correta de declarar matrizes em parâmetros de métodos, pois permite que passemos matrizes de tamanhos arbitrários. Veja o código:

procedure TForm3.exibirMatriz(matriz: array of Integer);
var
  i: Integer;
begin
  for i := Low(matriz) to High(matriz) do
    begin
      Memo1.Lines.Add(IntToStr(matriz[i]));
    end;
end;

procedure TForm3.Button1Click(Sender: TObject);
var
  // vamos declarar uma matriz de 10 inteiros
  valores: array[1..10] of Integer;
  i: Integer;
begin
  // vamos inicializar a matriz com os números pares de 2 até 20
  for i := Low(valores) to High(valores) do
    begin
      valores[i] := i * 2;
    end;

  // vamos fornecer a matriz para uma procedure que exibirá seus valores
  exibirMatriz(valores);
end;


No evento Click do botão nós criamos uma matriz de 10 inteiros, inicializamos os valores dos elementos e a fornecemos à procedure exibirMatriz() para que esta exiba os valores em um TMemo.

Importante: Quando passamos uma matriz para uma função ou procedure, alterações nos valores dos elementos da matriz por parte da procedure ou função que a recebe não afetam a matriz original. Para que isso aconteça devemos usar a palavra-chave Var antes do parâmetro para indicar que estamos passando a matriz por referência. Veja:

procedure TForm3.exibirMatriz(var matriz: array of Integer);

sexta-feira, 4 de março de 2016

Como obter o índice inicial e final de uma matriz em Delphi

Como os índices das matrizes em Delphi, diferente de outras linguagens, nem sempre começam em 0, não podemos deduzir que Length() - 1 retornará o índice final da matriz. Desta forma, podemos usar as funções Low() e High(), ambas presentes na unit System.

A função Low() retorna o índice inicial da matriz, enquanto High() retorna o índice final. Veja:

procedure TForm3.Button1Click(Sender: TObject);
var
  valores: array[11..20] of Integer;
  inicio, fim: Integer;
begin
  // vamos obter o índice inicial da matriz
  inicio := Low(valores);

  // vamos obter o índice final da matriz
  fim := High(valores);

  // vamos exibir o resultado
  ShowMessage('O índice inicial da matriz é: ' + IntToStr(inicio));
  ShowMessage('O índice final da matriz é: ' + IntToStr(fim));
  ShowMessage('O tamanho da matriz é: ' + IntToStr((fim - inicio) + 1));
end;


Lembre-se desta dica quando precisar percorrer os elementos de uma matriz usando o laço For.

Usando a função ForceDirectories() para criar um diretório juntamente com um ou mais subdiretórios

Em algumas situações precisamos, de uma só vez, criar um diretório e, dentro deste diretório, criar outro subdiretório. Para isso, o Delphi nos fornece a função ForceDirectories(), presente na unit SysUtils. Esta função recebe uma string contendo o diretório e subdiretórios a serem criados e returna true se a operação foi efetuada com sucesso e false em caso contrário.

Veja um trecho de código no qual criamos um diretório contendo um subdiretório que, por sua vez, contém outro subdiretório:

procedure TForm1.Button1Click(Sender: TObject);
var
  diretorio: string;
begin
  // diretório e subdiretorios que queremos criar
  diretorio := 'C:\\arquivo de codigos\\delphi\\exemplos';

  // vamos criar o diretório e subdiretórios
  if ForceDirectories(diretorio) then
    ShowMessage('O diretório e subdiretórios foram criados.')
  else
    ShowMessage('O diretório e subdiretórios NÃO foram criados.')
end;


Note que a unit FileCtrl também contém uma função chamada ForceDirectories(). Porém, esta função foi considerada ultrapassada. Dê preferência àquela da unit SysUtils.

Obtendo o tamanho de um arquivo em bytes, KB, MB ou GB

Em algumas situações precisamos obter o tamanho de um arquivo em bytes e, se necessário, converter este valor em bytes para KB, MB ou GB. Esta dica mostra como isso pode ser feito. Aqui nós temos duas funções. A função TamanhoArquivoBytes() recebe o caminho e nome de um arquivo e retorna seu tamanho em bytes. Já a função TamanhoArquivoFormatado() recebe um valor em bytes e devolve uma string formatada.

Veja o exemplo completo:

// permite formatar o tamanho de um arquivo em bytes em
// Kb, Mb ou Gb
function TamanhoArquivoFormatado(const bytes: Longint): string;
const
  b = 1; // byte
  kb = 1024 * b; // kilobyte
  mb = 1024 * kb; // megabyte
  gb = 1024 * mb; // gigabyte
begin
  if bytes > gb then
    Result := FormatFloat('#.## GB', bytes / gb)
  else if bytes > mb then
    Result := FormatFloat('#.## MB', bytes / mb)
  else if bytes > kb then
    Result := FormatFloat('#.## KB', bytes / kb)
  else
    Result := FormatFloat('#.## bytes', bytes);
end;

// função que permite obter o tamanho de um arquivo em bytes
function TamanhoArquivoBytes(arquivo: string): Int64;
var
  search_rec: TSearchRec;
begin
  if FindFirst(arquivo, faAnyFile, search_rec) = 0 then
    Result := Int64(search_rec.FindData.nFileSizeHigh) shl Int64(32)
      + Int64(search_rec.FindData.nFileSizeLow)
  else
    Result := -1;

  FindClose(search_rec);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  arquivo: string;
begin
  // nome do arquivo que queremos obter o tamanho
  arquivo := 'C:\\estudos_delphi\\programa_vcl\\arquivo.txt';

  // exibe o resultado
  ShowMessage('O tamanho do arquivo é: ' +
    TamanhoArquivoFormatado(TamanhoArquivoBytes(arquivo)));
end;


Note que aqui nós usamos o campo FindData da estrutura TSearchRec. Este campo contém informações adicionais sobre um arquivo, tais como a data e hora de criação do arquivo, data e hora do último acesso, etc. Porém, este campo é específico à plataforma Windows, o que pode resultar em um warning do tipo:

[DCC Warning] Unit1.pas(54): W1002 Symbol 'FindData' is specific 
to a platform

Listando todos os arquivos de um diretório

Em algumas situações precisamos listar todos os arquivos contidos em um determinado diretório. Para isso podemos usar a função abaixo. Esta função recebe o caminho e nome de um diretório e uma coleção de strings TStrings. Como resultado de sua execução, a função insere em TStrings todos os nomes dos diretórios encontrados. Para facilitar o entendimento do exemplo eu incluí uma chamada à função a partir do evento Click de um botão. Tenha a certeza de colocar uma TListBox no formulário antes de executar o código:

procedure listarArquivosDir(diretorio: string; lista: TStrings);
var
  search_rec: TSearchRec;
begin
  if FindFirst(diretorio + '*.*', faAnyFile, search_rec) = 0 then
    begin
      repeat
        if search_rec.Attr <> faDirectory then
          lista.Add(search_rec.Name);
      until FindNext(search_rec) <> 0;

      FindClose(search_rec);
    end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  listarArquivosDir('c:\\', ListBox1.Items);
end;

quarta-feira, 2 de março de 2016

Como usar a função BlockWrite() para escrever um ou mais bytes em um arquivo

A função BlockWrite pode ser usada quando precisamos escrever um ou mais bytes em um arquivo não tipado, ou seja, um arquivo de tipo arbitrário não texto. Veja sua assinatura:

a) procedure BlockWrite(var F: File; var Buf; Count: Integer); overload;

b) procedure BlockWrite(var F: File; var Buf; Count: Integer; var AmtTransferred: Integer); overload;

Aqui F é um arquivo não tipado no qual os dados serão escritos, Buf é qualquer variável contendo os dados que serão escritos no arquivo, Count é uma expressão do tipo Integer e representa a quantidade de registros a serem escritos e AmtTransferred é uma variável opcional do tipo Integer que representa a quantidade de registros realmente escritos.

Vamos ver um exemplo do uso desta função? Considere o seguinte trecho de código:

procedure TForm3.Button2Click(Sender: TObject);
var
  arquivo: File;
  valor: Byte;
begin
  // vamos abrir o arquivo para escrita
  AssignFile(arquivo, 'dados.dat');
  Rewrite(arquivo, 1);  // tamanho do registro = 1 byte

  valor := 90; // vamos escrever o valor 90

  BlockWrite(arquivo, valor, 1);
end;

Aqui nós escrevemos o byte 90 no arquivo "dados.dat". Note que, ao chamar a função Rewrite() nós informamos que o arquivo sendo criado terá o tamanho de 1 byte por registro. Vejamos agora como escrever quatro bytes (agrupados em um Integer):

procedure TForm3.Button2Click(Sender: TObject);
var
  arquivo: File;
  valor: Integer;
begin
  // vamos abrir o arquivo para escrita
  AssignFile(arquivo, 'dados.dat');
  Rewrite(arquivo, 4);  // tamanho de cada registro = 4 bytes

  valor := 2290; // vamos escrever o valor 2290
  BlockWrite(arquivo, valor, 1);
end;

Desta vez nós definimos que cada registro no arquivo terá 4 bytes, ou seja, um inteiro (Integer). Para finalizar, veja como podemos escrever um array (matriz) de bytes:

procedure TForm3.Button2Click(Sender: TObject);
var
  arquivo: File;
  valores: array[0..3] of Byte; // uma matriz de 4 bytes
  i: integer;
begin
  // vamos abrir o arquivo para escrita
  AssignFile(arquivo, 'dados.dat');
  Rewrite(arquivo, 1);  // tamanho de cada registro = 1 byte

  // vamos atribuir valores aos bytes
  for i := 0 to 3 do
    begin
      valores[i] := (i + 1);
    end;

  // vamos escrever um bloco de 4 registros de 1 byte cada
  BlockWrite(arquivo, valores, 4);
end;

Como obter o diretório atual usando a função GetCurrentDir()

Em algumas situações precisamos obter o diretório atual, ou seja, aquele a partir do qual nosso programa está sendo executado. Em Delphi isso pode ser feito com o auxílio da função GetCurrentDir() da unit SysUtils. Esta função não exige nenhum argumento e retorna o drive e nome do diretório atual como uma string. Veja o exemplo:

procedure TForm1.Button1Click(Sender: TObject);
var
  diretorio_atual: string;
begin
  // vamos obter o diretório atual
  diretorio_atual := GetCurrentDir;

  // vamos exibir o resultado
  ShowMessage('O diretório atual é: ' +
    diretorio_atual);
end;

Como obter apenas a letra do drive do diretório atual

A unit System do Delphi nos fornece a procedure GetDir(), usada para retornar o diretório atual. Porém, há situações nas quais precisamos obter apenas a letra do drive atual. Para esta finalidade escrevi a função obterLetraDriveAtual(), que não requer nenhum argumento e retorna a letra do drive atual. Note que incluí uma chamada a ela no evento Click de um botão para tornar o exemplo completo:

// função que permite obter a letra do drive
// atual
function obterLetraDriveAtual: Char;
var
  s: string;
  letra: Char;
begin
  GetDir(0, s);
  letra := s[1];
  Result := letra;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage('A letra do drive atual é: ' +
    obterLetraDriveAtual);
end;

Como excluir diretórios usando Delphi

Em algumas situações nossas aplicações Delphi precisam remover diretórios. Isso pode ser feito com o auxílio da função RemoveDir() da unit SysUtils. Esta função recebe uma string representando o diretório a ser excluído e retorna um valor true se o diretório foi excluído com sucesso e false em caso contrário. Para que o diretório seja excluído com sucesso ele deverá estar vazio.

Veja um trecho de código no qual usamos a função RemoveDir() para remover um diretório:

procedure TForm1.Button1Click(Sender: TObject);
var
  diretorio: String;
begin
  // vamos excluir o diretório abaixo
  diretorio := 'C:\\estudos_delphi\\arquivo';

  if RemoveDir(diretorio) then
    ShowMessage('Diretório excluído com sucesso.')
  else
    ShowMessage('Não foi possível excluir o diretório.');
end;

Como escrever em um arquivo texto usando Delphi

Não há como fugir. Cedo ou tarde precisaremos escrever em um arquivo texto usando Delphi. E esta dica foi escrita com o propósito de facilitar esta tarefa.

O primeiro passo para se escrever em um arquivo texto usando Delphi é declarar uma variável do tipo TextFile. Em seguida usamos a procedure AssignFile() para associar a variável TextFile ao arquivo em disco.

Como queremos escrever conteúdo novo no arquivo, a função Rewrite() pode ser usada. Esta função cria o arquivo em disco se este ainda não existir. Se o mesmo existir, ele é excluído e a função cria outro com o mesmo nome. E, para escrever conteúdo no arquivo texto, usamos as funções Write() e WriteLn(). A primeira escreve no arquivo enquanto a segunda escreve no arquivo e adiciona o marcador de quebra de linha.

Veja o exemplo para um melhor entendimento:

procedure TForm1.Button1Click(Sender: TObject);
var
  arquivo: TextFile;
begin
  // vamos fazer uma ligação entre a variável arquivo e o
  // arquivo que queremos manipular
  AssignFile(arquivo, 'C:\\arquivo de codigos\\dados.txt');

  // vamos abrir o arquivo ou criar um novo
  Rewrite(arquivo);

  // vamos escrever no arquivo agora
  WriteLn(arquivo, 'Sou a primeira linha.');
  Write(arquivo, 'Sou a segunda linha.');
  WriteLn(arquivo, ' E lá vem...');
  WriteLn(arquivo, 'A terceira linha');

  // hora de fechar o arquivo
  CloseFile(arquivo);

  // fim
  ShowMessage('Operação realizada com sucesso.');
end;

Como copiar um arquivo em Delphi usando a função CopyFile() da API do Windows

Há situações nas quais gostaríamos de copiar um determinado arquivo. Até a versão 2009 as bibliotecas de tempo de execução do Delphi não nos fornecia uma função ou procedure para realizar tal tarefa. No entanto, é possível chamar a função CopyFile() da WinAPI a partir de nossas aplicações sem quaisquer esforços adicionais. Esta função requer o nome e caminho do arquivo a ser copiado, o nome e caminho do novo arquivo e um valor true ou false indicando se a função deve falhar caso o segundo arquivo já exista. O retorno será true se a operação for realizada com sucesso e false em caso contrário.

Veja um trecho de código no qual efetuamos a cópia de um arquivo:

procedure TForm1.Button1Click(Sender: TObject);
var
  arquivo_original, novo_arquivo: string;
begin
  // diretorio e nome do arquivo original
  arquivo_original := 'C:\\arquivo de codigos\\dados.txt';

  // diretorio e nome do novo arquivo
  novo_arquivo := 'C:\\arquivo de codigos\\dados2.txt';

  // vamos copiar o arquivo
  if CopyFile(PChar(arquivo_original), PChar(novo_arquivo), true) then
    ShowMessage('O arquivo foi copiado com sucesso')
  else
    ShowMessage('Não foi possível copiar o arquivo');
end;

terça-feira, 1 de março de 2016

Como usar o evento OnDrawCell para controlar o desenho das células em um TStringGrid

O evento OnDrawCell, definido originalmente na classe TCustomDrawGrid, é disparado quando uma determinada célula do TStringGrid precisa ser desenhada. Este evento possui a seguinte assinatura:

property OnDrawCell: TDrawCellEvent;

O tipo Grids.TDrawCellEvent apresenta, no Delphi 2009, a seguinte lista de parâmetros:

TDrawCellEvent = procedure(Sender: TObject; ACol, ARow: Longint; 
  Rect: TRect; State: TGridDrawState) of object;

Vamos ver cada um destes parâmetros separadamente:

Sender - Representa a grid na qual a célula está sendo desenhada;

ACol, ARow - Índices da coluna e linha na qual a célula está sendo desenhada;

Rect - Localização da célula na área de desenho (canvas);

State - Um objeto Grids.TGridDrawState que indica se a célula possui o foco (gdFocused), está selecionada (gdSelected) e se a mesma é uma célula fixa (gdFixed). Células fixas permanecem vísiveis quando as barras de rolagem são acionadas.

Veja um trecho de código no qual usamos o evento OnDrawCell para colorir de amarelo o fundo de uma determinada célula do TStringGrid:

procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
  Rect: TRect; State: TGridDrawState);
var
  conteudo: String;
begin
  // vamos obter o conteúdo da célula
  conteudo := StringGrid1.Cells[ACol, ARow];

  // vamos colorir a célula na segunda linha e terceira
  // coluna com o fundo amarelo
  if (ACol = 2) and (ARow = 1) then
    begin
      StringGrid1.Canvas.Brush.Color := clYellow;
      StringGrid1.Canvas.FillRect(Rect);
      StringGrid1.Canvas.TextRect(Rect, Rect.Left, Rect.Top,
        conteudo);
    end;
end;


Para este exemplo deixei o valor da propriedade DefaultDrawing do TStringGrid como true. Isso faz com que o fundo da célula seja pintado antes que o evento DrawCell seja chamado e o efeito 3D das células fixas seja exibido ou o retângulo de foco ao redor da célula que possui o foco no momento seja desenhado após o evento. Experimente executar o exemplo com o valor false para a propriedade DefaultDrawing para ver o resultado.

Veja agora um trecho de código no qual definimos a cor vermelha para o texto das células cujo valor inteiro seja menor que 10:

procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
  Rect: TRect; State: TGridDrawState);
var
  conteudo: String;
begin
  // vamos obter o conteúdo da célula
  conteudo := StringGrid1.Cells[ACol, ARow];

  // vamos definir a cor vermelha para o texto das células
  // contendo valores menores que 10
  if (conteudo <> '') and (StrToInt(conteudo) < 10) then
    begin
      StringGrid1.Canvas.Font.Color := clRed;
      StringGrid1.Canvas.FillRect(Rect);
      StringGrid1.Canvas.TextRect(Rect, Rect.Left, Rect.Top,
        conteudo);
    end;
end;


Tenha cuidado para que o valor da célula possa ser convertido para inteiro por meio do uso da função StrToInt(). Caso a conversão não for possível, uma exceção do tipo EConvertError será lançada.