Atenção: esse artigo tem uma palestra complementar. Clique e assista!

Atenção: esse artigo tem um vídeo complementar. Clique e assista!

Do que trata o artigo

Neste artigo abordaremos diversas dicas sobre o registro e a API do Windows. Mostraremos como personalizar janelas e outros controles através da API, como usar e tratar mensagens do sistema operacional através dessas funções e várias outras coisas que elas podem nos proporcionar. Veremos como criar uma entrada no registro para programas que inicializam junto com o sistema e diversas customizações do Windows pouco divulgadas.

Para que serve

Com o conhecimento necessário sobre a API e o Registro e, sabendo onde obter mais informações, o desenvolvedor poderá criar aplicações com interfaces diferentes das janelas padrão do Windows, além de automatizar processos, fazer com que softwares comuniquem-se entre si, conhecer um pouco mais dos mecanismos e “entranhas” do Windows e saber onde aplicar tudo isso na prática.

Em que situação o tema é útil

As dicas que veremos podem muito bem ser usadas em sistemas de Lan Houses ou Cyber cafés, onde certas configurações devem ser automáticas ou feitas pelo próprio software, para que sejam transparentes ao dono do estabelecimento. Nesse mesmo tipo de sistema algumas ações dos usuários devem ser limitadas e tanto a API como o registro possuem funções para isso. Analogamente ao caso das Lan Houses, temos os programas que rodam em “franquias”, onde muitas opções do Windows podem ser desabilitadas pelo registro. O desenvolvedor pode aprender muito sobre as peculiaridades e funcionamento interno do Windows. Isso facilita a criação de componentes e plugins para o Delphi. Além disso, interfaces mais bonitas ou divertidas de se usar podem ser obtidas com um bom uso da API.

Resumo do DevMan

A API do Windows é um conjunto de arquivos .dll com funções que nos permitem fazer diversas operações. Desde comunicações com outros programas ou com o próprio Windows a interfaces gráficas e auditorias de segurança por exemplo. Existem funções para os mais diversos objetivos e todos os compiladores e IDEs que criam programas nativos para o Windows usam internamente sua API para desenhar janelas e componentes. Os próprios componentes do Delphi usam muito a API do Windows e você poderá aprender como adicionar novas funcionalidades a componentes através da API. Associado a isso temos o registro do Windows, com uma série de opções avançadas que podemos usar para customizar o próprio sistema operacional. Podemos fazer com que nossa própria aplicação faça essas configurações automaticamente.

Nota: O Windows que usaremos no exemplo será a versão XP Professional, porém, versões superiores do Windows serão compatíveis na maioria das chaves do registro.

A API do Windows é um conjunto de arquivos .dll que fazem parte do sistema operacional, porém, expondo várias funções ao meio externo. Nós podemos usar a API do Windows da mesma forma que usamos qualquer outra dll, porém o Delphi contém uma unit que já tem várias das funções mais usadas mapeadas e declaradas.

Durante nossas considerações sobre a API vamos analisar, em cada caso, um cenário onde sua aplicação seria necessária e depois discutiremos como usá-la. Exceto quando o uso da API for muito simples, caso em que inverteremos a ordem: mostraremos primeiro como usar e depois onde seria aplicável. Vamos expor ao longo do artigo diversas dicas do uso de APIs do Windows para a resolução de diversos problemas. Muitas das coisas que conhecemos como dicas nada mais são do que o uso de funções do Windows, que poderiam ser usadas em qualquer linguagem.

O registro do Windows é um conjunto de vários arquivos, dentre eles system.dat e user.dat, mas não se limitando a esses. As informações de configuração do Windows e de diversos outros softwares são armazenadas no registro em forma de árvore, hierarquicamente, de forma muito semelhante a arquivos e diretórios. O formato como esses dados são armazenados e como o arquivo é “feito” não nos interessará porque a Microsoft providenciou na sua API diversas funções para ler e gravar informações do registro. E o Delphi possui uma classe chamada TRegistry que torna o uso dessas funções ainda mais fácil e mais transparente para o desenvolvedor.

Nota do DevMan

Antes de começar a trabalhar com a API do Windows é importante lembrar algumas regras básicas. Por exemplo, como a API foi escrita em C/C++, não existem funções que recebam argumentos por referência, então funções que necessitam modificar valores de variáveis devem receber um ponteiro para essa variável.

O uso de ponteiros nessas funções será muito comum. Será bastante comum também o uso de estruturas, como Records.

Uma outra diferença importante são as strings. Enquanto C e C++ trabalham com vetores de Char terminados em #0 (ou \0), conhecidos por nós como pchars, o Delphi tem dois tipos de strings, as ShortStrings podem ter até 255 caracteres, e são formadas por uma estrutura de “tamanho + 1” bytes, onde o primeiro elemento, de um byte, é um número de 0 a 255 indicando o tamanho da string e o próximo elemento é um vetor de caracteres, podendo ter até 255 elementos. Esse tipo de string não é muito usado no nosso cotidiano. O outro tipo é a string “comum”. Essa string é formada por um ponteiro que aponta para uma estrutura que contém, no início, 4 bytes formando um cardinal e depois deles um array dinâmico de caracteres. O tipo string do Delphi pode armazenar até 4294967295 letras, que é o tamanho de um cardinal.

Ainda falando sobre strings, a maioria das funções da API terão duas versões, uma terminada com A, que trabalha com AnsiStrings, strings padrão ASCII onde cada caractere possui apenas um byte, e as terminadas com W, que funcionam com WideStrings, strings cujos caracteres têm dois bytes cada, também chamados de utf-16. Isso existe para compatibilidade entre Windows 9x e versões NT ou superiores. O Delphi detecta automaticamente o tamanho do char no seu sistema operacional e cria um mapeamento que torna o uso dessas APIs transparente. Então os nomes das funções que você encontrar terão as terminações A e W suprimidas. Uma olhada no fonte da unit Windows.pas tira bastante dúvidas e revela muitos “segredos” tanto do Sistema Operacional como do Delphi.

Desde a versão 2009 o Delphi tem suporte a strings Unicode, padrão utf-8, que suporta uma faixa enorme de caracteres de diversas línguas, como chinês, árabe etc. Esse tipo de string tem caracteres que podem ter até 4 bytes. Como a maioria das funções da API do Windows não usa esse tipo de string, não entraremos em detalhes sobre ela.

Criando janelas dentro de outras janelas

Dentre os vários modelos de disposição de janelas que temos para nossos sistemas, podemos citar o MDI (Multiple Document Interface), onde todas as janelas de um sistema são filhas de uma janela principal, como era a interface dos documentos do Word antes da versão 2007. Uma outra forma é a conhecida SDI (Single Document Interface), onde cada janela abre em um espaço independente. Muito popular hoje é o padrão TDI (Tabbed Document Interface), onde as janelas são dispostas em abas, assim como o Internet Explorer 7 e 8 e o Mozilla Firefox. Imagine um cenário onde você precise, sem usar forms MDI, criar formulários um dentro do outro em vários níveis. Isso é perfeitamente possível com o uso da função SetParent e a propriedade Handle das janelas.

No Windows todo controle gráfico, até mesmo botões e caixas de texto, são considerados janelas, e toda janela tem um Handle, que é um número inteiro positivo que identifica unicamente uma janela. Todas as funções do Windows que usam janelas usam o Handle. Para nós, isso é transparente porque a VCL e outras bibliotecas do Delphi fazem isso automaticamente por nós sem percebermos.

Vamos simular uma aplicação MDI em vários níveis onde não usaremos os forms MDI do Delphi. Além disso, para podermos criar várias instâncias da mesma janela sem usar as variáveis públicas com o nome do formulário e, para evitar vazamentos de memória, vamos projetar um mecanismo no qual poderemos criar quantos formulários quisermos de cada tipo e estes serão destruídos no momento em que os fecharmos clicando no botão X do formulário.

Crie uma aplicação Delphi Win32 e vamos dar o nome de ExemploSetParent a essa aplicação. Vamos criar três formulários, chamados respectivamente de frmPrimeiro, frmSegundo e frmTerceiro e sendo os nomes das units como uFrmPrimeiro, uFrmSegundo e uFrmTerceiro respectivamente. Coloque em cada um dos formulários componentes diferentes, ao seu gosto, como Labels identificando o nome do formulário.

Assegure-se que o primeiro form seja maior que o segundo, e este maior que o terceiro, apenas para, quando colocarmos os formulários um dentro do outro eles não ocultarem controles do próprio formulário. Depois veremos como usar barras de ferramentas e painéis para limitar o espaço que uma janela filha pode tomar. O primeiro formulário será o formulário padrão de nossa aplicação. Os outros dois podemos dizer que seriam um documento e seu detalhe respectivamente.

Vamos colocar no primeiro formulário um botão para abrir o segundo, e no segundo um botão para abrir o terceiro. Seus nomes serão btAbrirSegunda e btAbrirTerceira, respectivamente. O Caption de ambos pode ser simplesmente Abrir. Antes de editarmos o código dos botões vamos editar o código do evento OnClose do segundo e terceiro formulários. No Object Inspector selecione o evento OnClose e digite o seguinte código:


  Action := caFree; 

Faça isso tanto no segundo como no terceiro formulário. Isso fará com que, ao se fechar o formulário, seu método Free seja executado automaticamente, sendo assim liberado da memória. Não usaremos a variável pública da unidade do formulário.

Vá em Project > Options e assegure-se de que apenas a frmPrimeira será criada com a execução do programa, sendo as outras duas criadas sob demanda, conforme a Figura 1.

Figura 1. Ordem de criação dos formulários

A unit Windows provavelmente já está declarada no uso de todas as units desse projeto, então não precisaremos nos preocupar. Usaremos o método SetParent dessa unit, mas para não confundir com o método SetParent do form, que tem o mesmo nome, usaremos Windows.SetParent.

Nota: Como dito anteriormente, a VCL usa internamente a API do Windows, então o método SetParent dos forms e outros componentes usam internamente o SetParent da unit Windows, mas de uma forma a facilitar para o desenvolvedor.

Faça com que o primeiro formulário use o segundo, clicando em File > Use unit> uFrmSegundo. Analogamente faça com que o segundo formulário use o terceiro. Vamos dar um duplo clique no botão btAbrirSegunda, do primeiro form, e digitar o código da Listagem 1.

Listagem 1. Botão abrir do primeiro formulário


  procedure TfrmPrimeira.btAbrirSegundaClick(Sender: TObject);
  begin
    with TfrmSegunda.Create(Self) do
    begin
      Show;
      Windows.SetParent(handle, Self.Handle);
    end;
  end; 

A função SetParent da unit Windows aceita dois parâmetros, o primeiro é o handle da janela filha e o segundo é o handle da janela que será a mãe. Não devemos confundir esse Parent com os conceitos de Herança, da Programação orientada a objetos, e muito menos com o conceito de Owner dos componentes do Delphi. Esse sistema de Parent das janelas do Windows é apenas uma hierarquia visual.

Colocamos Self como Owner do form que acabamos de criar (segunda) por uma razão especial: podemos criar centenas de “segundos formulários” dentro do primeiro. Se fecharmos todos eles, cada um será destruído automaticamente, porém, se fecharmos o primeiro sem fechar os seus filhos, sendo o primeiro formulário Owner (dono) de todos eles, ele mesmo se encarregará de destruir, automaticamente, todos os seus filhos. Esse é o mecanismo de Owner do Delphi. Poderíamos passar Application como Owner, que também funcionaria, com um pequeno problema: só quando a aplicação inteira fosse fechada é que esses formulários seriam destruídos. Embora isso não faça diferença do primeiro para o segundo, fará diferença do segundo para o terceiro. Por isso decidimos colocar a responsabilidade de cada pai “visual” destruir seus filhos.

Se por um acaso tivéssemos passado nil como Owner, criaríamos um Memory Leak enorme, porque não teríamos referência a nenhuma das dezenas de filhos que criamos, então ao fechar o formulário principal os filhos permaneceriam na memória sem serem destruídos. Esse vazamento iria se revelar apenas quando fechássemos o formulário principal com os filhos abertos. Se fechássemos os filhos antes isso não iria ocorrer, porque cada um ia ser destruído no momento do seu fechamento.

Analogamente, o botão btAbrirTerceira no segundo formulário fará a mesma coisa, mas criando um objeto TFrmTerceira. Veja como ficou a ...

Quer ler esse conteúdo completo? Tenha acesso completo