Entrada e Saída em Arquivos – Parte Final

Eliminando Registros

O modelo lógico desse aplicativo é eficiente e relativamente simples de entender. Porém, essa simplicidade tem o seu preço: você não tem uma forma simples de eliminar registros.

Suponha que o arquivo de banco de dados DONINHAS.DAT contenha quatro registros, para as doninhas chamadas hipoteticamente de A, B, C e D, conforme mostrado na Figura 10.21. A criação desses registros produziu um arquivo que contém 280 Bytes (4 x 70).

Figura 10.21 Um arquivo com quatro registros para o banco de dados Doninhas.

Não é possível simplesmente remover bytes do meio do arquivo e compô-lo novamente. Por exemplo, se deseja eliminar o registro da doninha B, você tem apenas duas opções: copiar todos os registros, exceto aquele a eliminar, para um novo arquivo ou então sobrepor o registro B com algum dado novo. Se você fizer a cópia, poderá então remover o arquivo original e renomear o arquivo copiado com o nome do arquivo original. Essa solução está ilustrada na Figura 10.22.

 
Figura 10.22 Copiando registros e renomeando o arquivo.

Se você sobrepuser o registro B com outros dados – por exemplo, um registro contendo apenas espaços em branco -, a caixa de lista eventualmente conterá uma série de entradas em branco. Você pode pensar em fazer com que a rotina de inicialização simplesmente omita qualquer registro com o nome da doninha em branco. Porém, caso isso seja feito, esse aplicativo não será capaz de compatibilizar as entradas da caixa de lista com os registros correspondentes no arquivo. A Figura 10.23 ilustra esse problema.

Esse aplicativo, como está atualmente, requer uma associação de um para um entre os nomes na caixa de lista e os registros no arquivo. (Acidentalmente, esse requisito significa que a propriedade Sorted da caixa de lista não pode ser configurada para True, uma vez que a classificação destruiria o relacionamento entre os valores de índices da caixa de lista e a posição dos registros no arquivo.) É possível contornar esse problema criando-se um mapeamento separado.entre o índice da caixa de lista e as posições no arquivo. A Figura 10.24 contém um exemplo.

 
Figura 10.23 Sobrepondo um registro com espaços brancos.

 


Figura 10.24 Mapeando os índices da caixa de lista.

Qual é a melhor solução? Se você sabe que os arquivos de dados para o seu aplicativo serão sempre pequenos, o método de copiar e renomear pode ser melhor. Ele pode ser implementado sem qualquer modificação no código que já foi escrito, e o aplicativo permanecerá simples e fácil de entender. Entretanto, em se tratando de arquivos volumosos, a cópia demanda tempo e considerável espaço em disco. É preciso ter em mente que tanto a cópia quanto o arquivo original terão de existir ao mesmo tempo, e se o espaço em disco não for suficiente, o programa poderá falhar. A criação de um mapeamento permite que o seu programa seja executado mais eficientemente, algo muito apreciado pelos usuários finais. No entanto, essa solução envolve complexidade adicional (e possivelmente bugs) e a reescrita de algumas partes do código existente.

Como programador, você será constantemente chamado a tomar decisões desse tipo. É muito importante compreender a tarefa do seu programa e a natureza dos dados antes de tomar uma decisão. Não importa o quão livre de bugs esteja o seu código, ele será inútil se os usuários não puderem conviver com as decisões que você toma durante a fase de projeto.

Vejamos mais de perto as alternativas de eliminação de registros do banco de dados Doninhas. A solução copiar e renomear não requer modificações no código existente. Basta incluir o procedimento mostrado na Figura 10.25.

Figura 10.25 O procedimento btnEliminar_Click para o aplicativo Doninha.

O procedimento btnEliminar_Click abre um novo arquivo e copia todos os registros, menos o atual, para ele. Os dois arquivos são então fechados. A instrução Kill recebe o nome original do arquivo como um parâmetro e este arquivo é excluído. A instrução Name renomeia o arquivo criado por meio da cópia. Quando acaba o tratamento dos arquivos, a caixa de lista é limpa e o programa chama o procedimento DoninhaInicia novamente, para recarregar os nomes na caixa de lista.

A segunda alternativa, usando uma matriz como um mapa, é mais complexa, pois envolve modificações no código existente. A Figura 10.26 contém apenas as rotinas que devem ser modificadas para produzir a nova versão; linhas que foram adicionadas ou alteradas encontram-se em destaque.

 
Figura 10.26A  A parte revisada do código do aplicativo Doninha para implementar a matriz de mapeamento.

 


Figura 10.26B A parte revisada do código do aplicativo Doninha para implementar a matriz de mapeamento. (Continuação)

O programa alterado usa a propriedade chamada ItemData, que é uma característica interna de caixas de lista. ItemData é uma matriz de elementos do tipo Long Integer, com uma entrada para cada item da caixa de lista. Essa matriz é usada para armazenar o mapeamento, isto é, ela conterá o número de registro no arquivo para cada entrada da lista. Na versão original, a variável DoninhaAtualIx refere-se apenas ao índice na caixa de lista; a posição do registro no arquivo deve ser obtida a partir da expressão cboNome.ltemData(DoninhaAtuaIIx).

As alterações feitas nos procedimentos NovaDoninha, MostrarDoninha e AtualizarDoninhaAtual são triviais, simplesmente asseguram que o mapeamento seja usado e atualizado corretamente. As modificações em DoninhaInicia são relativamente diretas: à medida que cada registro é lido, verifica-se se a primeira posição do nome está em branco; se estiver, significa que o registro deve ser ignorado. Além disso, durante a leitura dos registros, é armazenado o valor apropriado na matriz ItemData. A propriedade NewIndex contém o valor do índice da lista para o item incluído mais recentemente. Uma vez que não é mais necessária uma relação biunívoca entre os registros no disco e na caixa de lista, você pode configurar a propriedade Sorted da caixa de lista para True.

Finalmente, o procedimento btnEliminar_Click é completamente diferente na nova versão. Para eliminar o registro atual, o programa irá sobrepô-lo com um registro em branco. Depois, o nome é removido da caixa de lista.

Permanece um pequeno problema nessa nova versão: os registros eliminados ocupam espaço e os novos registros são incluídos sempre no final do arquivo. Seria ideal se o procedimento NovaDoninha reutilizasse o espaço dos registros excluídos, em vez de estender automaticamente o arquivo. Essa modificação impediria que o arquivo crescesse indefinidamente. A implementação desse exercício é deixada a cargo do leitor. (Eu sempre desejei dizer algo semelhante a isso.)

Configuração

Freqüentemente, você pode desejar salvar um ou dois valores importantes de uma seção do aplicativo para outra – talvez uma configuração tal como o modo analógico ou digital para o seu programa de relógio. As variáveis padrões não funcionam, pois o valor armazenado nelas se perde quando o programa acaba. Você poderia criar um arquivo de dados, porém isso exigiria um grande esforço para quantidades mínimas de informações. A solução padrão adotada por aplicativos para o Windows 95 e NT é armazenar os valores no Registro do Sistema. O Registro do Sistema é um conjunto de arquivos que o Windows utiliza para armazenar informações sobre hardware, software do sistema e configuração de aplicações. Cada aplicativo pode criar informações e armazená-las no Registro do Sistema. Você não precisa se preocupar em como estas informações estão organizadas internamente no Registro, basta saber que o Registro do Sistema está organizado na forma de uma árvore de itens onde cada item constitui o que chamamos uma chave do Registro. Nas chaves ficam armazenadas as informações. O Visual Basic possui funções que lhe dão acesso ao Registro para criação, leitura, gravação e exclusão de informações.

O Visual Basic provê uma localização padrão no Registro para o armazenamento de informações de aplicações criadas em Visual Basic:
HKEY_CURRENT_USER\Software\VB and VBA Program Settings\NomeAplicação\seção\chave

Observe que NomeAplicação, seção e chave são subchaves de HKEY_CURRENT_USER\Software\VB and VBA Program Settings. Sua aplicação poderá utilizar estas três subchaves para armazenar informações de configuração que permaneçam de uma execução para outra.

As funções de manipulação das informações armazenadas pela sua aplicação no registro são:

 


Figura 10.27 Funções de acesso ao Registro

Nota Para examinar entradas no registro, use a aplicação Regedit, que acompanha o Windows 95 e Windows NT.

Criando e Salvando Configurações da Aplicação

Você pode usar a instrução SaveSetting para salvar um novo valor para uma chave do registro armazenada na localização do registro destinada à sua aplicação. Por exemplo, você poderia adicionar código ao evento Form_Unload no formulário principal da aplicação no sentido de preservar as configurações no momento de encerramento, ou no evento de Form_Unload de uma caixa de diálogo de Opções para atualizar as preferências do usuário.

Use a seguinte sintaxe para a instrução SaveSetting:

SaveSetting NomeAplicação, seção, chave, valor

O código seguinte salva novos valores para as chaves Backup e LastEntry na seção Setup do registro para uma aplicação chamada “RegCust”. Este código assume que as variáveis strDate e intLastEntry contêm os novos valores.
Private Sub Form_Unload(Cancel As Integer)

SaveSetting “RegCust”, “Startup”, “Backup”, strDate

SaveSetting “RegCust”, “Startup”, “LastEntry”, _

intLastEntry

End Sub
Se uma entrada para a aplicação “RegCust” ou alguma dessas seções ou chaves não existir na seção Software\Microsoft do registro, este código irá criá-la.

Para mais informações veja “SaveSetting Statement” no Language Reference do Books Online.

Recuperando Configurações da Aplicação

Você pode usar as funções GetSetting e GetAllSettings para obter os valores armazenados no localização do registro usada pela sua aplicação. Por exemplo, sua aplicação pode obter as informações do registro para recriar as condições presentes no momento do seu último encerramento.

Uma Informação por Vez

Para recuperar uma única informação do registro, use a seguinte sintaxe para a função GetSetting:

GetSetting(NomeAplicação, seção, chave, [padrão])

O código seguinte recupera o valor da chave LastEntry na seção Startup da aplicação “RegCust” e exibe o valor na janela Immediate.
Private Sub Form_Load()

Dim intLastEntry As Integer

intLastEntry = GetSetting(“RegCust”, “Startup”, _

“LastEntry”, “0”)

Debug.Print intLastEntry

End Sub
Note que você pode usar um parâmetro opcional, padrão, para determinar o valor retornado pelo Visual Basic quando nenhum valor estiver presente no registro para a chave especificada.

Múltiplas Informações

Para retornar uma lista das chaves e seus valores, use a seguinte sintaxe para a função GetAllSettings:

GetAllSettings(NomeAplicação, seção)

O código seguinte recupera uma lista de duas colunas de chaves do registro e seus valores na seção Startup da aplicação “RegCust”, e exibe os resultados na janela Immediate.
Private Sub Form_Load()

Dim avntSettings As Variant

Dim intX As Integer

avntSettings = GetAllSettings(“RegCust”, “Startup”)

For intX = 0 To UBound(avntSettings, 1)

Debug.Print avntSettings(intX, 0), _

avntSettings(intX, 1)

Next intX

End Sub
Para mais informações veja “GetSetting Function” e “GetAllSettings Function” no Language Reference do Books Online.

Excluíndo Configurações da Aplicação

Você pode usar a instrução DeleteSetting para excluir uma chave , seção ou localização de aplicação do registro. Por exemplo, você pode querer excluir todas as informações do registro para uma aplicação quando ela for desinstalada.

Use a sintaxe seguinte para a instrução DeleteSetting:
DeleteSetting(NomeAplicação, seção, chave)
O código seguinte exclui a chave LastEntry na seção Startup da aplicação “RegCust”.
Private Sub cmdDelKey_Click()

DeleteSetting “RegCust”, “StartUp”, “LastEntry”

End Sub
O código seguinte exclui totalmente do registro a seção Startup da aplicação “RegCust”.
Private Sub cmdDelSection_Click()

DeleteSetting “RegCust”, “StartUp”

End Sub
O código seguinte exclui por inteiro do registro a localização para a aplicação “RegCust”.
Private Sub cmdUnInstall_Click()

DeleteSetting “RegCust”

End Subm
Para mais informações veja “DeleteSetting Statement” no Language Reference do Books Online.

Bibliotecas de Ligação Dinâmica

Você pode acessar funções internas do Windows ou internas aos aplicativos desenvolvidos para Windows se as funções fizerem parte de uma biblioteca de ligação dinâmica (Dynamic-link library, uma DLL). Se você pesquisar o diretório SYSTEM do Windows, verá arquivos com a extensão DLL no nome. Alguns desses arquivos acompanham o Windows; outros (tais como VBRUN500.DLL) vêm com os aplicativos que você comprou. Se você souber a definição das funções contidas nesses arquivos, poderá chamar quase todas elas em seus aplicativos Visual Basic.

Para chamar essas funções a partir do Visual Basic, é necessário conhecer algumas informações técnicas a respeito de tipos de dados, passagem de parâmetros e assim por diante. Para ter uma visão detalhada sobre isto consulte no Books Online “Acessing DLL’s and the Windows API”. Informações especificas sobre as funções que podem ser chamados em cada DLL devem ser buscadas nos manuais de programação dos produtos que contêm as DLL’s.

Resumidamente, a instrução Declare possui a seguinte sintaxe:

Sintaxe 1

[Public | Private] Declare Sub Nome Lib “NomeBiblioteca” [Alias “NomeApelido”] [([Lista de argumentos])]

Sintaxe 2

[Public | Private] Declare Function Nome Lib “NomeBiblioteca” [Alias “NomeApelido”] [([Lista de argumentos]
)] [As TipoRetorno]

Public ou Private: declara se o procedimento pode ser chamado em todos os módulos (Public) ou apenas no módulo onde esta instrução aparece (Private).

Explicações

  • Function ou Sub: se retorna valor, Function; caso contrário, Sub.
  • Nome: o nome dado ao procedimento que está sendo declarado para referenciá-lo dentro do Visual Basic.
  • NomeBiblioteca: o nome da biblioteca de ligação dinâmica onde reside o código do procedimento.
  • NomeApelido: o nome do procedimento como é reconhecido dentro da DLL que o contém. Usado quando, por alguma razão, não seja conveniente ou possível usar o nome do procedimento como é reconhecido internamente à DLL.
  • Lista de argumentos: os argumentos a serem passados para o procedimento.
  • TipoRetorno: o tipo dos dados retornados pelo procedimento.

Aviso: A instrução Declare é muito poderosa. Ela lhe permite acessar qualquer função do sistema Windows. Entretanto, você deve usá-la cuidadosamente. Por fazer chamadas diretas ao sistema, você não desfrutará dos benefícios de verificação de erros do Visual Basic. Há o perigo potencial de você provocar uma queda do sistema, se for cometido um engano na declaração ou chamar uma função do sistema com valores inválidos.

Um Exemplo de Uso das Funções em DLLs do Windows

Uma tarefa comum no uso de caixas de lista e caixas de combinação é verificar a existência de um item na lista. Usando somente os recursos do Visual Basic, você teria que criar um loop para verificar item por item até localizar o item procurado. No entanto, o Windows disponibiliza uma função chamada SendMessage, que pode ser usada para solicitar diretamente essa pesquisa ao objeto caixa de lista ou caixa combinada. O trabalho de codificação diminui e o desempenho é melhor. Antes que você possa usar SendMessage em uma aplicação, no entanto, é preciso declará-la no seu programa. Abaixo está a instrução de declaração de SendMessage.

Declare Function SendMessage Lib “user32” Alias “SendMessageA” (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

Esta função envia uma mensagem à janela identificada pelo argumento hwnd solicitando alguma informação ou ação. A mensagem é identificada pelo argumento wMsg, uma constante, que também precisaremos declarar antes de fazer a chamada a SendMessage. Os dois últimos argumentos têm significados variados dependendo da mensagem que está sendo enviada. No exemplo que daremos a seguir o parâmetro lParam será usado para conter uma string pesquisada numa caixa de lista. Mudemos, portanto, o seu tipo de dados para String na instrução Declare.

Além da declaração de SendMessage, devemos declarar também a constante que identifica a mensagem para a caixa de lista ou caixa combinada. No caso de a mensagem que solicita a pesquisa de uma string ser destinada a uma caixa de lista a declaração da constante é:

Public Const LB_FINDSTRINGEXACT = &H1A2

Declararemos também uma constante usada para verificação do valor de retorno da função: LB_ERR. Caso LB_ERR seja retornado, é sinal de que nenhum item com o texto exatamente igual ao pesquisado existe na lista.

Public Const LB_ERR = (-1)

Uma vez que as declarações estejam feitas, o código abaixo exemplifica a chamada a SendMessage para pesquisar pelo item “Paulo” a partir do inicio da lista de lstNomes. No caso do item ser localizado é dada uma mensagem que exibe o seu índice na lista; caso nada seja encontrado, é dada uma mensagem de aviso.

 
Figura 10.28 Exemplo de uso de SendMessage para pesquisar listas

Neste exemplo, o terceiro argumento é -1, o que significa, para esta mensagem, que a pesquisa deve começar pelo primeiro item da lista.

Repare no prefixo LB no nome da constante que identifica a mensagem. Este prefixo é abreviatura de ListBox (Caixa de Lista). Se a mensagem fosse para uma caixa combinada, a constante seria declarada como abaixo:

Public Const CB_FINDSTRINGEXACT = &H158

No restante tudo seria feito da mesma forma.

Agora veja abaixo duas outras constantes identificadoras de mensagens para pesquisas em caixas de lista e caixas combinadas:

Public Const LB_FINDSTRING = &H18F

Public Const CB_FINDSTRING = &H14C

Estas constantes identificam mensagens que também fazem pesquisa em listas, mas com uma diferença: retornam o primeiro item que inicie com o texto pesquisado. O modo de chamar é o mesmo que o usado em LB_FINDSTRINGEXACT e CB_FINDSTRINGEXACT.

Há duas formas de você obter as informações necessárias para criar estas declarações sempre que precisar usar as funções das DLLs do Windows. Uma é consultar alguma documentação do Windows sobre o procedimento e fazer por sua própria conta as conversões de tipos de dados da linguagem “C” para os seus correlatos no Visual Basic. Outra é carregar a aplicação API Text Viewer, que acompanha o Visual Basic, e abrir o arquivo de texto Win32API.TXT que contém as declarações prontas para você copiar e colar no seu código. Foi o que fizemos para obter as declarações acima.

Nota: o conjunto de funções que o Windows coloca à disposição dos programadores para serem chamadas de dentro de qualquer aplicação para Windows é conhecido como Windows’ Application Programming Interface ou Windows’ API. Uma excelente fonte de informação detalhada sobre a API do Windows é o livro “Visual Basic 5.0 – Programmer’s Guide to the Win32 API” de Dan Appleman.

Artigo gentilmente cedido por CodeLines

Compatilhe esse artigo!

Leave a Reply

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

This site uses Akismet to reduce spam. Learn how your comment data is processed.