quarta-feira, 7 de setembro de 2011

Arredondamento financeiro

É muito comum encontrar programadores Delphi que têm dúvidas sobre como arredondar um valor real para "n" casas após o separador decimal. A princípio parece um problema simples, pois o próprio Delphi já possui uma função que arredonda para o inteiro mais próximo, a qual poderia facilmente ser utilizada para arredondar para qualquer quantidade de casas decimais. Exemplo:

{ x receberá o valor de y arredondado para 2 casas após o separador. }
x := Round(y * 100) / 100;

{ z receberá o valor de y arredondado para 3 casas após o separador. }
z := Round(y * 1000) / 1000;
No entanto dois problemas poderão aparecer com os exemplos acima:
  • O arredondamento feito pelo Delphi difere daquele feito pelas calculadores financeiras, bem como bancos de dados como InterBase e FireBird.
  • poderão ocorrer pequenos arredondamentos devido ao modo como o Delphi trata números reais, tais como aparecer 3.9999999... em vez de 4.
A função abaixo resolve estes dois problemas.
{ Esta função faz arredondamento de valores reais para "n" casas
  decimais após o separador decimal, seguindo os critérios das
  calculadoras financeiras e dos bancos de dados InterBase e FireBird.
}
function TBRound(Value: Extended; Decimals: integer): Extended;
var
  Factor, Fraction: Extended;
begin
  Factor := IntPower(10, Decimals);
  { A conversão para string e depois para float evita
    erros de arredondamentos indesejáveis. }
  Value := StrToFloat(FloatToStr(Value * Factor));
  Result := Int(Value);
  Fraction := Frac(Value);
  if Fraction >= 0.5 then
    Result := Result + 1
  else if Fraction <= -0.5 then
    Result := Result - 1;
  Result := Result / Factor;
end;

Consultar por mês de um campo data

Problema:

Tenho um cadastro de clientes com Codigo, Nome, DataNasc, etc.
Preciso fazer uma consulta onde apareceão apenas os clientes
que fazem aniversário em determinado mês. Como fazer?

Solução:

Use uma Query como abaixo:
- Coloque no form os seguintes componentes:
  * TQuery
  * TDataSource
  * TDBGrid
  * TEdit
  * TButton

- Altere as propriedades dos componentes como abaixo:
  * Query1.DatabaseName = (alias do BDE)
  * DataSource1.DataSet = Query1
  * DBGrid1.DataSource = DataSource1
  
- Coloque o código abaixo no evento OnClick de Button1:

  Query1.Close;
  Query1.SQL.Clear;
  Query1.SQL.Add('select * from dCli');
  Query1.SQL.Add('where extract(month from DataNasc) = :Mes');
  Query1.ParamByName('Mes').AsInteger := StrToInt(Edit1.Text);
  Query1.Open;

- Execute. Digite um número de 1 a 12 no Edit e clique no 
  botão.

Observações

Os números de 1 a 12 representam, respectivamente, os meses de Janeiro a Dezembro. Este exemplo foi testado com Delphi4, BDE5 e tabela Paradox7.

DBGrid zebrado

Programe o evento OnDrawColumnCell do DBGrid como abaixo:

procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject;
  const Rect: TRect; DataCol: Integer; Column: TColumn;
  State: TGridDrawState);
begin
  if State = [] then
  begin
    if Table1.RecNo mod 2 = 1 then
      DBGrid1.Canvas.Brush.Color := clAqua
    else
      DBGrid1.Canvas.Brush.Color := clWhite;
  end;
  DBGrid1.DefaultDrawColumnCell(Rect, DataCol, Column, State);
end;

Observação:
O objeto Table1 é da classe TTable (relativa ao BDE), mas esta dica poderá ser usada com outros DataSet's, tais como IBDataSet, ClientDataSet, etc.

domingo, 4 de setembro de 2011

Usando funções no MySQL


Vou tentar passar aqui, um pequeno exemplo de como usar funções do MySQL em nossas instruções SQL.
Em primeiro lugar, certifique-se que você possua instalado uma versão a partir da 5.x do MySQL em seu micro.
No nosso exemplo, criaremos e utilizaremos uma função que retornará a idade do cliente
Crie uma pequena tabela com a seguinte estrutura:

CREATE TABLE `clientes` (
`ID` int(11) NOT NULL auto_increment,
`NOME` varchar(45) default NULL,
`DATANASC` date default NULL,
PRIMARY KEY (`ID`),
UNIQUE KEY `ID` (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;



Inclua as linhas abaixo na tabela, ou digite suas próprias informações:


INSERT I NTO `clientes` (`ID`, `NOME`, `DATANASC`) VALUES
(1, 'SILVIO SANTOS', '1971-09-18'),
(2, 'SILLAS SANTOS', '1993-04-27');


Agora, crie a função conforme o exemplo abaixo:


CREATE FUNCTION `idade`(pDATA DATE)
RETURNS varchar(15) CHARSET latin1
NOT DETERMINISTIC CONTAINS SQL SQL SECURITY DEFINER
COMMENT ''
Begin
DECLARE vIDADE varchar(30);
IF DATE_FORMAT(NOW( ),'00-%m-%d') >= DATE_FORMAT(pDATA,'00-%m-%d') THEN
SET @idade=DATE_FORMAT(NOW( ),'%Y')-DATE_FORMAT(pDATA,'%Y');
ELSE
SET @idade=DATE_FORMAT(NOW( ),'%Y')-DATE_FORMAT(pDATA,'%Y')-1;
END IF;
RETURN(concat(@idade,' anos'));
end;


Esta função, retornará a idade da pessoa de acordo com o campo DATANASC, da tabela ‘clientes’.
Veja que a função receberá um valor (pDATA, que no nosso exemplo será o campo ‘DATANASC’ de nossa tabela) e fará a subtração da “dat a corrente” pela data do campo DATANASC.
Dentro da função, criamos uma variável (vIDADE), onde armazenaremos o valor a ser retornado pela função. Note também que a função possui retorno do tipo String (RETURNS varchar(30)), que deverá ter o tamanho suficiente para comportar o resultado retornado. Ou seja, se o valor retornado tiver 15 caracteres, o ‘RETURNS’ da função precisa ter tamanho de 15 ou mais caracteres, senão, pode acontecer de não retornar nenhum valor.

Após ter criado a tabela, inserido as linhas na mesma e criado a função, execute a seguinte instrução:


SELECT NOME, idade(DATANASC) IDADE FROM clientes;


No SQL acima, a consulta irá retornar o Nome, e a idade assim: “XX anos”, para cada cliente.

Como você viu, utilizar funções dentro do banco de dados por ser muito mais prático do que fazê-lo dentro da aplicação (me refiro a funções para retorno de dados, claro).

Lembrando que este exemplo é apenas uma base, pois podemos criar funções para qualquer necessdiade, como: calculo de preços, juros de mora, datas, horas, concatenação de campos complexos, junções de tabelas (como nos inner, left e right joins (desde que precise retornar apenas um valor), e muito mais.

JSON e Generics - Serializando Objetos


Desta vez vou demonstrar duas novas tecnologias que estão sendo muito melhoradas no Delphi desde a versão 2009 até a ultima versão o Delphi XE. Generics é uma forma de poder

...passar parâmetro de tipos para classes e métodos, possibilitando assim reutilização de código, por exemplo e, JSON (JavaScript Object Notation) é uma forma de troca de dados entre aplicações, semelhante ao XML, porém com uma sintaxe bem mais simples e leve. O Delphi XE, como algumas versões anteriores, dá suporte a implementação usando JSON, e em nosso exemplo vamos criar um método totalmente genêrico que recebe um objeto e um tipo e usaremos o JSON para formatar este objeto e gravar no disco usando a nova classe de IO (input/output) do Delphi, e um método de leitura deste arquivo no formato JSON para retornar o mesmo a um Objeto, novamente passando o tipo do objeto e o caminho onde se encontra o arquivo.

Primeiro vamos criar uma nova classe do tipo TPessoa:

type
  TPessoa = class
  private
    { Private declarations }
    FPessIden: Integer;
    FPessNome: string;

  public
    { Published declarations }
    property PessIden: Integer read FPessIden write FPessIden;
    property PessNome: string read FPessNome write FPessNome;

  end;

Para conseguirmos desenvolver nossos métodos, temos antes que adicionar ao uses as unit’s citadas abaixo. Acima de cada unit tem a descrição de sua função no projeto:

uses
  SysUtils,

  { Unit que contém os novos métodos de I/O }
  IOUtils,

  { Unit's necessárioas para usar JSON }
  DBXJSONReflect, DBXJSON,

  { Unit necessária para usar Generic }
  Generics.Collections;

Agora vamos criar nossa classe que irá conter os métos necessários para carregar e salvar os dados no disco. O class antes da function indica que estes métodos serão estáticos, não sendo necessário instânciar a classe TJSONTools para usar estes métodos, e a descrição T : class, indica que este método recebe um tipo e que a variável obj passada por parâmetro é deste tipo e, na outra função que ela retorna o tipo especificado.

type
  TJSONTools = class
    public
    { Public declarations }
    class function saveFileJSON(obj: T; const filePath: string): Boolean;
    class function loadFileJSON(const filePath: string): T;
  end;

Agora nosso método que irá gravar no disco o objeto da classe descrita acima.:

class function TJSONTools.saveFileJSON(obj: T; const filePath: string): Boolean;
var
  Marshal: TJSONMarshal;

begin
  Marshal := TJSONMarshal.Create(TJSONConverter.Create());
  try
    try
      TFile.WriteAllText(filePath, (Marshal.Marshal(obj) as TObject).ToString);

      Result := True;
    except
      Result := False;
    end;
  finally
    FreeAndNil(Marshal);
  end;
end;

Agora o método responsável por ler o arquivo e carregar novamente os dados para um objeto:

class function TJSONTools.loadFileJSON(const filePath: string): T;
var
  Unmarshal: TJSONUnMarshal;
  obj: TJSONObject;

begin
  Unmarshal := TJSONUnMarshal.Create();
  try
    try
      if not(FileExists(filePath)) then
        Exit(nil);

      obj := TJSONObject.ParseJSONValue(
        TEncoding.ASCII.GetBytes(TFile.ReadAllText(filePath)),
        0) as TJSONObject;

      Result := T(Unmarshal.Unmarshal(obj));
    except
      Exit(nil);
    end;
  finally
    FreeAndNil(Unmarshal);
  end;
end;

Agora uma forma de poder usar nossas classes:

var
  objPessoa: TPessoa;

begin
  objPessoa:= TPessoa.Create();
  objPessoa.PessIden := 1;
  objPessoa.PessNome := 'Active Delphi';

  if (TJSONTools.saveFileJSON&ltTPessoa&gt(objPessoa, 'C:\pessoa.txt')) then
    ShowMessage('Criou o arquivo no caminho: ' + #13 + ' C:\pessoa.txt');

  objPessoa := TJSONTools.loadFileJSON&ltTPessoa&lt('C:\pessoa.txt');
  if (Assigned(objPessoa)) then
  begin
    ShowMessage(
      'PessIden: ' + IntToStr(objPessoa.PessIden) + #13 +
      'PessNome: ' + objPessoa.PessNome
    );
  end
  else
    ShowMessage('Não foi possivel des-serializar o objeto');
end;

Este foi apenas um exemplo didático usando JSON e Generics no Delphi. Estes métodos ainda podem ser refatorados e até criado validações para tratar possíveis erros de parâmetros, que podem ser identificados criando testes unitários sobres estes métodos.

Obrigado pela leitura e até a próxima!

Clique aqui para fazer o download do exemplo, feito com Delphi XE

domingo, 14 de agosto de 2011

Dicas para melhorar a performance do servidor Firebird


Defina índices para todas as colunas que vão ser usadas para join’s ou ordenações. Os índices são estruturas de dados de árvores balanceadas que fornecem um grande aumento na velocidade da procura dos valores nas colunas em que os índices são definidos. Isto é especialmente útil quando ordenamos por uma coluna em particular ou juntamos duastabelas por uma coluna que as duas têm em comum.Lembre que um índice Firebird é específico para uma direção de ordenação, ascendente ou descendente (ASCENDING x DESCENDING), e a direção de um índice deve ser igual à direção escolhida quando se faz a ordenação. Se você for usar as duas direções, ascendente ou descendente, crie dois índices para as chaves de ordenação. As inclusões e alterações usam índices para serem gravadas, o que pode prejudicar a performance durante um INSERT ou UPDATE, mas alguns custos impostos durante a entrada de dados podem resultar em um grande ganho de performance mais tarde na pesquisa destes dados. Para minimizar a perda de performance durante um INSERT, considere em desabilitar os índices durante um grande volume de INSERTs. Isto irá ‘desligar’ os índices, tornando-os indisponíveis para ajudar a acelerar as pesquisas, mas também não vão ser atualizados durante os INSERTs.Então, reabilite-os depois de terminados os INSERTs, e os índices serão atualizados e balanceados uma vez para todos os dados incluídos.Periodicamente reconstrua os índices, desativando e ativando novamente (ALTER INDEX nome INACTIVE; seguido de ALTER INDEX nome ACTIVE;). Recalcule o índice seletivamente (SET STATISTICS INDEX nome;) se uma mudança na tabela afeta a distribuição regular dos valores e dados. Os índices também são reconstruídos do mesmo modo durante o restore.

Otimize as pesquisas 


O Firebird irá analisar uma pesquisa (query) e o otimizador fará a melhor estimativa de quais índices deverão ser empregados para acelerar a pesquisa. Mas nenhum otimizador automático é infalível. Para queries complexas que serão usadas freqüentemente, ou que serão aplicadas a um número elevado de dados, construa a pesquisa cuidadosamente. Usea opção SET PLAN da ferramenta ISQL para testar queries, ver sua performance e ver qual PLAN o otimizador escolheu. No Interbase v4.0, v4.1 e v4.2 existem alguns problemas conhecidos em tais queries usando a sintaxe JOIN na linguagem ANSI SQL-92, que não são analisados e otimizados corretamente. Evite esta sintaxe ou, se tiver que usá-la, especifique seu próprio PLAN.  


Ajuste o cache do Firebird 


O Firebird mantém seu próprio cache de páginas do banco de dados correntemente em uso na RAM do servidor. O tamanho padrão do cache na versão superserver (Windows) é 2048 páginas de banco de dados (isto é compartilhado por todos os clientes conectados a um dado banco de dados). Para um banco de dados com 1Kb de páginas, isto ainda ésomente 2048Kb. A maioria dos servidores tem muita memória, então ponha em uso! Experimente ajustar o cache de 256 até 10.000 páginas. Em algum ponto verá diminuir os retornos, mas alguma experimentação irá revelar este ponto. 


Faça Backup / Restore 


Existem vários benefícios em fazer periodicamente um backup/restorede um banco de dados Firebird usando o utilitário GBAK:-reconstruir índices;-eliminar versões obsoletas de registros (‘garbage’);-defragmentar páginas de dados;-regravar tabelas de dados contiguamente; e

-fazer uma cópia dos dados!

Use processamento Cliente-servidor 


Usando funções externas (UDFs), Triggers, Stored Procedures e Select Procedures fazem com que o banco de dados faça muito processamento no servidor, que provavelmente é um computador muito mais potente que a estação de trabalho. Esta técnica evita também tráfego de rede desnecessário.  


Use o cache de disco do Sistema Operacional 


A maioria dos sistemas operacionais oferece sistema de cache de disco para leitura/gravação. Com isto os programas podem fazer suas gravações na RAM do servidor, e o sistema operacional irá salvar este conteúdo fisicamente no disco periodicamente. Fazendo sempre pequenas gravações ou fazer de uma só vez numa operação maior podeaumentar bastante a performance de leitura/gravação de dados. Usar este sistema de cache de disco é algumas vezes chamado de leitura/gravação assíncrona (asynchronous I/O) ou gravação em cache (cached writes). Forçando a gravação direta dos dados no disco,sobrepondo o sistema de cache, é chamado de leitura/gravação síncrona (synchronous I/O) ou gravação forçada (forced writes). O sistema de leitura/gravação assíncrona tem o benefício de aumentar a performance, mas como a RAM é um sistema volátil de armazenamento, pode perder dados se houver uma queda de energia ou uma falha no servidor ou ainda uma reinicialização do servidor quando o cache ainda não tiver sidodescarregado no disco. A leitura/gravação síncrona garante que nada será perdido por estar no cache, mas ler ou gravar diretamente no disco é bem mais lento que fazê-lo na RAM.O Firebird permite fazer a leitura/gravação assíncrona ou síncrona. O comando GFIX –WRITE SYNC .GDB faz o banco de dados usar a gravação forçada (forced writes), e o comando GFIX –WRITE ASYNC .GDB faz o banco usar a gravação em cache (cached writes).A gravação em cache pode ganhar muito em performance no Firebird, mas devem ser tomadas medidas de proteção contra perda de dados. Por exemplo, usando espelhamento de disco para manter uma duplicação dos dados. Usando também no-break e estabilizadores para garantir uma energia estável e ininterrupta para o servidor. Com medidas como estas,a leitura/gravação assíncrona pode trazer benefícios sem sacrificar a segurança. 


Administre o protocolo de rede 


O protocolo TCP/IP é muito mais rápido que o SPX ou NetBEUI. Conectar sua rede usando TCP/IP resultará em uma melhor performance. O serviço NetBEUI tem um prioridade baixa em servidores NT, mas existe um meio no sistema operacional para aumentar a prioridade deste serviço. Alguns usuários dizem ter aumentado a performance depois de ter feito isto. Veja na documentação do Windows NT as instruções paraajustar a prioridade dos serviços NetBEUI. NOTA: A partir do Windows NT 4.0, a performance da implementação do Microsoft NetBEUI foi aumentada. É agora pelo menos tão boa quanto a implementação do Microsoft TCP/IP, entretanto, em uma rede ocupada, o NetBEUI é ainda limitado por ser um protocolo independente de conecção. Isto significa que cada host na rede deve checar cada pacote para ver seu conteúdo. Com mais hosts transmitindo na rede, cada host deve manipular uma crescente leitura de "números errados". 


Escolha o sistema operacional 


Sistemas operacionais basados no UNIX/Linux são sempre mais rápidos em multiprocessamento que sistemas Windows em um mesmo equipamento. Se velocidade é algo importante para você considere o uso desta plataforma. Pense sempre em utilizar o Firebird como servidor dedicado, que não funcione como servidor de arquivos, pois ele poderá ficar sentado no "banco de trás" nas questões de prioridade de processamento.No Windows NT o servidor é configurado para dar prioridade ao compartilhamento de arquivos. Você pode modificar esta configuração no servidor: Painel de Controle -> Rede -> Software de Rede. Modifique para "balancear" ou "servidor de banco de dados". Esta modificação irá resultar em uma melhora dramática na performance do Firebird.O uso de sistemas operacionais não específicos para uso de Servidores como os Windows 9x, Me ou XP Home podem prejudicar sua performance, além das fragilidades conhecidas. 


Considere a limpeza do lixo (garbage collection) 


O Firebird tem por padrão a função de automaticamente limpar as antigas versões dos registros quando estas se tornam muito numerosas. O problema deste método é que se um processo cliente que for sem sorte suficiente para iniciar uma transação que coincida com o início da limpeza automática, vai ter que esperar o trabalho de limpeza. O processo clienteatrasa enquanto o processo de limpeza é feito. Desabilite a limpeza automática (automatic garbage collection), usando GFIX –h 0, em favor da limpeza programada (scheduled database sweep), usando GFIX –s. Isto irá eliminar a perda na performance do cliente.Fazer um backup e restore efetivamente faz a mesma coisa do que uma limpeza completa no banco de dados. O backup somente copia a versão mais recente de cada registro, nunca copia versões anteriores, assim, quando os dados são restaurados, existe somente uma versão de cada registro. Existem também outros benefícios de fazer backup/restore,descritos na seção própria deste artigo. Falando em limpeza do lixo, muitos programadores não se dão conta da necessidade de iniciar e terminar (START e COMMIT) suas transaçõescom o banco de dados. Abrir transações impede que a varredura periódica complete a limpeza do lixo, e a performance irá sofrer progressivamente com o tempo. 


Normalize seu banco de dados 


Projete seu banco de dados com uma normalização adequada dos dados. Registros que têm muitos grupos de campos repetidos são maiores que deveriam ser, podem aumentar o custo de ordenação e ainda podem causar a ocupação de mais páginas do que o necessário,resultando em uma maior fragmentação de páginas e um banco de dados enorme sem necessidade.


Pense nas transações 


Projete sua aplicação para que resolva o mais rápido possível as transações depois de abertas. Freqüentemente os programadores de aplicação abrem uma transação e apresentam uma tela de entrada de dados para o usuário. Ao invés disso, pegue os dados com o usuárioantes, abra uma transação para dar o INSERT e então dê o COMMIT na transação imediatamente. O propósito disto é diminuir o tempo de duração das transações, que diminui a probabilidade de duas aplicações concorrentes conflitarem em um bloqueio de registro. Isto também ajuda a evitar muitos ROLLBACKs e tráfego de rede, já que a sua aplicação decide quando submeter os dados ao banco ou descartá-los.  


Programe com a API do Firebird 


Um programa escrito em C com SQL embutido responde melhor que uma aplicação cliente Firebird escrita em Paradox, Delphi ou outra ferramenta visual. A idéia é programar diretamente na API do Firebird ao invés de adicionar camadas intermediárias com mais um protocolo de rede sobre ele.

domingo, 31 de julho de 2011

Migrando aplicativos do Delphi 5 para o 6 e 7


Neste artigo, será demonstrado como solucionar alguns problemas comumente encontrados por desenvolvedores Delphi quando fazem migração de aplicativos e componentes de versões anteriores ao Delphi 6 (mais precisamente o Delphi 5) para as versões 6 e/ou 7. O que funcionar para o Delphi 6 provavelmente funcionará para o Delphi 7, já que as mudanças mais expressivas nas bibliotecas padrões para a criação de componentes foram realizadas no Delphi6, por motivos de compatibilidade.
A Borland realizou diversas mudanças em métodos e classes nas antigas bibliotecas (VCL) em decorrência da necessidade de compatibilizar o máximo possível o Delphi 6 com o Kylix, ou em outras palavras, a biblioteca de componentes VCL (Visual Component Library) com a biblioteca inter-plataforma CLX (Component Library for Cross-Plataform), utilizada mais especificamente no Kylix (apesar da mesma ser utilizada no Delphi também).

Serão listadas a seguir as principais mudanças que conheço e que devem ajudar o leitor a migrar seus aplicativos e componentes para as versões 6 e 7 do Delphi. São estas:

* A implementação de Classes e Métodos da unit "Dsgnintf.pas" foram divididos em duas units (pelo menos até onde conheço): a DesignIntf.pas e a DesignEditors.pas. Ambas estão localizadas em ($Delphi)\Source\ToolsAPI, onde ($Delphi) é o diretório onde o Delphi está instalado. Ao invés de incluir o caminho citado em seu "Enviroment Options\Library\LibraryPath" para utilizar as units, você pode optar por colocar na seção requires (e realmente acho preferível isto se estiver trabalhando com pacotes) o pacote "designide.dcp", localizado na pasta ($Delphi)\Lib. Ele já inclui estas bibliotecas e outras comumente utilizadas além de uma nova interface, a IComponentDesigner, contida em ComponentDesigner.pas.

* A procedure EditProperty da classe TDefaultEditor teve seus parâmetros alterados; no Delphi 5, a mesma era declarada desta forma:

  EditProperty(PropertyEditor: TPropertyEditor; var Continue, FreeEditor: Boolean); virtual;

e agora a mesma é declarada assim:

  procedure EditProperty(const Prop: IProperty; var Continue: Boolean); virtual;

Observe que o parâmetro PropertyEditor do tipo classe TPropertyEditor passou agora a se chamar Prop e ser do tipo interface IProperty e, o parâmetro FreeEditor deixou de existir. Essa nova implementação é realizada na unit "DesignEditors.pas".

* As rotinas de manipulação de objetos ou variáveis do tipo Variant passaram da unit System.pas para uma unit própria, a Variants.pas.

* Interfaces como a IFormDesigner , IDesignerSelection e IdesignerSelection sofreram alterações. Quanto as duas primeiras, as mudanças são respectivamente estas: mudança de nome para "IDesigner" e a adição de uma função GET que retorna um TPersistentequando indicado o índice do seu membro. Exemplo:

Nas versões anteriores ao Delphi 6, se você quisesse usar o TPersistent de um objeto, deveria escrever:

  var p: TPersistent;
  ...
  P := Selections[i] as TPersistent;

Agora basta escrever:

  var p: TPersistent;
  ...
  P := Selections.get[i];

* A função CollectionsEqual implementada na Unit Classes.pas teve seus parâmetros alterados; no Delphi 5, a mesma era declarada desta forma:

  function CollectionsEqual(C1, C2: Tcollection): Boolean;

e agora a mesma é declarada assim:

  function CollectionsEqual(C1, C2: TCollection; Owner1, Owner2: TComponent): Boolean;

* As constantes que estavam na unidade DesignConsts.pas, foram colocadas juntamente com os menus em DesignMenus.pas.

Dicas

Para aproveitar um mesmo código-fonte em diferentes versões do Delphi, você pode utilizar a diretiva de compilação {$IFDEF name}, que especifica que um determinado bloco de código compreendido pela cláusula só será compilado se o parâmetro "name" estiver definido.

Parâmetros de versão

  Delphi3 - VER80;
  Delphi5 - VER130;
  Delphi6 - VER140;
  Delphi7 - Ver150;

Como utilizar

Exemplo1:
No caso de um código que referencie a DsgnIntf.pas e seja utilizado no Delphi 6 ou posterior, você pode fazer o seguinte:

uses
  Windows, Messages, SysUtils, Classes, Graphics,
  {$IFDEF VER130} // se for Delphi5
    DsgnIntf
  {$ELSE}
    {$IFDEF VER140} // se for Delphi6
      DesignIntf, DesignEditors
    {$ELSE}
      {$IFDEF VER150} // se for Delphi7
        DesignIntf, DesignEditors
      {$ENDIF}
    {$ENDIF}
  {$ENDIF}

Observação: No lugar de você colocará o caractere adequado, "," para continuar a declaração de Units ou ";" para fechamento de declaração.

Exemplo 2:
No caso de um código que referencie a System.pas e seja utilizado no Delphi 6 ou posterior, você pode fazer o seguinte:

uses
  Windows, Messages, SysUtils, Classes, Graphics, System,
  {$IFDEF VER140} // se for Delphi6
    Variants
  {$ELSE}
    {$IFDEF VER150} // se for Delphi7
       Variants
    {$ENDIF}
  {$ENDIF}

Bem, desejo que isto possa ajudar todos aqueles que trabalhem com Delphi, seja usuário iniciante, intermediário ou avançado.

Curso de Delphi: 7.Consultas SQL