Existe um componente COM disponível no Windows chamado FileSystemObject. O VB pode manipular este componente para ter acesso ao sistema de disco da máquina.
Vamos fazer um mecanismo de busca de arquivos em disco.
O usuário poderá digitar na txtProcurar o nome do arquivo que ele deseja localizar, podendo usar máscaras (*.txt) por exemplo. Ao clicar no botão cmdencontrar será feita a busca do arquivo em todo o HD e o resultado será mostrado na lstarquivos. O label servirá para mostrar o diretório que está sendo pesquisado no momento.
Temos aqui um excelente uso para a recursividade. Cada diretório poderá ter subdiretórios e estes mais subdiretórios e assim por diante. Assim deveremos criar uma sub que receba um objeto Folder do filesystemobject. Primeiramente ela verificará os arquivos contidos na própria pasta, caso atendam ao critério de busca ela exibirá os arquivos na listbox.
Tendo verificado todos os arquivos da folder, a função deverá fazer uma verificação em cada uma das subpastas existentes. Desta forma a função irá chamar ela mesma passando como parâmetro uma das subpastas, então ela chamará ela mesma tantas vezes quanto forem as subpastas da pasta atual.
O mesmo se repetirá continuamente para as subpastas, as subsubpastas e assim por diante. A técnica de fazer com que uma função chame ela mesma é chamada de recursividade.
Precisaremos declarar o objeto FileSystemObject. Teremos que fazer um references (Project -> References) para a biblioteca do FileSystemObject (Microsoft Scripting Runtime) e definir uma variável deste tipo.
O botão, por sua vez, precisará chamar a função de localização (vamos chama-la de AchaArquivos) transmitindo a pasta inicial (vamos considerar como sendo C:\)
Veja como fica:
Dim fso As New FileSystemObject Private Sub cmdEncontrar_Click() txtProcurar.Text = UCase(txtProcurar.Text) AchaArquivos fso.GetFolder("c:\") End Sub
Observe que o Ucase na textbox foi aplicado já neste ponto para que desta forma seja feito uma única vez, ganhando-se performance.
O método GetFolder do objeto FileSystemObject recupera um objeto Folder (pertence à biblioteca do FileSystemObject). Assim sendo estamos transmitindo para a função AchaArquivos o objeto Folder relativo a pasta C:\, veja:
Sub AchaArquivos(f As Folder) End Sub
Precisaremos então fazer a verificação de todos os arquivos contidos na pasta. O objeto Folder possui uma propriedade chamada Files que contém tais arquivos. Podemos fazer um For/each, mas para isso precisaremos de uma variável do tipo File. Veja como fica:
Sub AchaArquivos(f As Folder) Dim a As File For Each a In f.Files Next End Sub
Dentro do for/Each precisaremos fazer a comparação do nome do arquivo com a máscara que foi digitada na textbox. Se o nome do arquivo estiver de acordo com a máscara iremos inseri-lo na listbox. Para esta comparação podemos usar o operador LIKE do VB (sim, o VB tem um operador LIKE, vide dica 80). Veja:
Sub AchaArquivos(f As Folder) Dim a As File For Each a In f.Files If UCase(a.Name) Like txtProcurar.Text Then lstArquivos.AddItem a.Name End If Next End Sub
Lembre-se que já haviamos aplicado o Ucase na textbox, ganhando performance com isso.
Feito isso os arquivos desta pasta que atendem ao critério já estarão dentro do ListBox. Torna-se necessário agora fazermos a busca em cada uma das subpastas de F.
O objeto Folder possui uma coleção chamada SubFolders que contém todas as subpastas existentes na pasta em questão. Podemos fazer um loop na coleção SubFolders e, para cada item de subFolder chamarmos a própria sub AchaArquivos novamente, dai a utilização de recursividade. Veja como fica:
Sub AchaArquivos(f As Folder) Dim a As File Dim sf As Folder For Each a In f.Files If UCase(a.Name) Like txtProcurar.Text Then lstArquivos.AddItem a.Name End If Next For Each sf In f.SubFolders AchaArquivos sf Next End Sub
É interessante observar no algorítimo a forma como a recursividade se aprofundará na estrutura de diretórios do disco e, ao atingir os últimos níveis de diretório, retornará.
Podemos agora embelezar um pouco a busca, adicionando um label no formulário (lblProcurar) e exibindo no label o diretório no qual estamos realizando a busca no momento. Mas como a exibição será feita dentro do loop, este não permitirá nem que o label nem que a listbox se atualizem, então não conseguiremos ver o conteúdo do label e da listbox enquanto a busca não terminar.
Para evitar esse problema precisaremos utilizar a instrução DoEvents. A instrução DoEvents verifica se existe algum evento da interface gráfica solicitando resposta e o atende antes de continuar a execução do código. Deve-se lembrar que estamos trabalhando com um sistema que usa multitarefa preemptiva, portanto essa questão só afeta a nossa própria aplicação, não impede outras aplicações de realizarem suas tarefas.
Veja como fica o código:
Sub AchaArquivos(f As Folder) Dim a As File Dim sf As Folder lblProcurar.Caption = "Procurando em " & f.Path DoEvents txtProcurar.Text = UCase(txtProcurar.Text) For Each a In f.Files If UCase(a.Name) Like txtProcurar.Text Then lstArquivos.AddItem a.Name Doevents End If Next For Each sf In f.SubFolders AchaArquivos sf Next End Sub
Para incrementar um pouco mais nossa pequena aplicação podemos fazer com que ao clicar em um arquivo sejam exibidas informações adicionais sobre ele, por exemplo: Tamanho, data de criação e data da última modificação. Precisaremos de mais labels para isso: lbltamanho,lblcriacao e lblultmodificacao.
Para podermos acessar esses dados após os arquivos já terem sido inseridos na listbox precisaremos guardar os objetos arquivo (File) referente a cada arquivo. Vamos criar uma Collection para guarda-los e adicionar na sub AchaArquivos o código para inseri-los na collection. Veja como fica:
Sub AchaArquivos(f As Folder) Dim a As File Dim sf As Folder lblProcurar.Caption = "Procurando em " & f.Path DoEvents For Each a In f.Files If UCase(a.Name) Like txtProcurar.Text Then lstArquivos.AddItem a.Name col.add a Doevents End If Next For Each sf In f.SubFolders AchaArquivos sf Next End Sub
Então vamos programar o click da listbox para preencher os labels com os dados dos arquivos. Como inserimos o objeto File de cada um na collection teremos que usar o ListIndex da listbox para indexar a collection e assim acessarmos o objeto File do arquivo correto. Não podemos esquecer que o listindex começa em 0 enquanto que o objeto collection começa em 1, dai teremos que corrigir a diferença no código, veja:
Private Sub lstArquivos_Click() lblTamanho.Caption = col(lstArquivos.ListIndex - 1).Size lblCriacao.Caption = col(lstArquivos.ListIndex - 1).DateCreated lblUltModificacao.Caption = col(lstArquivos.ListIndex - 1).DateLastModified End Sub
Mas não vamos parar por ai, vamos incrementar um pouco mais nossa aplicação: Podemos mostrar o conteúdo do arquivo. Vamos criar um segundo form (chamaremos de frmconteudo) e uma textbox (txtconteudo) com multiline como true.
O melhor evento para realizarmos esta exibição é o duplo clique da listbox. Assim sendo, quando o usuário der um duplo clique sobre o nome de um arquivo abriremos o conteúdo do arquivo para ele examinar.
O objeto File possui um método chamado OpenAsTextStream que abre o arquivo e devolve como resposta um objeto TextStream. O objeto TextStream nos permite navegar através do conteúdo do arquivo. Não precisaremos de tanto em nosso exemplo, apenas obter o conteúdo, mesmo assim precisaremos de uma variável TextStream.
Veja como fica o código:
Private Sub lstArquivos_DblClick() Dim t As TextStream Set t = col(lstArquivos.ListIndex + 1).OpenAsTextStream frmConteudo.txtConteudo.Text = t.ReadAll t.Close frmConteudo.Show 1 End Sub
Para completar falta apenas o toque final: Deixar que o usuário selecione a pasta a partir da qual ira ser realizada a busca. O Windows já possui uma caixa "Selecionar Pasta" que permite que o usuário selecione uma determinada pasta do sistema e não um arquivo. Para podermos aproveitar esse recurso do windows devemos utilizar uma chamada a uma função da API SHBrowserForFolder, comentada na dica 202.
Precisaremos de uma textbox na qual ficará a pasta escolhida (chamaremos de txtpasta) e um botão que abrirá a caixa de busca, cmdBuscaPasta. Veja como ficam as declarações para podermos realizar a chamada à API:
Const BIF_RETURNONLYFSDIRS = 1 Const BIF_DONTGOBELOWDOMAIN = 2 Const MAX_PATH = 260 Private Declare Function SHBrowseForFolder Lib "shell32" ( _ lpbi As BrowseInfo) As Long Private Declare Function SHGetPathFromIDList Lib "shell32" ( _ ByVal pidList As Long, _ ByVal lpBuffer As String) As Long Private Declare Function lstrcat Lib "kernel32" Alias "lstrcatA" ( _ ByVal lpString1 As String, _ ByVal lpString2 As String ) As Long 'Tipo para def Private Type BrowseInfo hWndOwner As Long pIDLRoot As Long pszDisplayName As Long lpszTitle As Long ulFlags As Long lpfnCallback As Long lParam As Long iImage As Long End Type
No botão cmdBuscaPasta precisaremos configurar uma variável do tipo BrowseInfo através da qual passaremos parâmetros para a função SHBrowseForFolder e, enfim, chamar a função. Após chamarmos a função utilizaremos a função SHGetPathFromIdList para transformar o ID que é retornado pela SHBrowseForFolder no caminho da pasta em si e inseri-lo dentro da textbox.
Veja como fica o código:
Private Sub cmdBuscaPasta_Click() Dim lpIDList As Long Dim sBuffer As String Dim szTitle As String Dim tBrowseInfo As BrowseInfo 'Personaliza a procura szTitle = "Indique a pasta a partir da qual será feita a busca: " With tBrowseInfo .hWndOwner = Me.hWnd .lpszTitle = lstrcat(szTitle, "") .ulFlags = BIF_RETURNONLYFSDIRS + BIF_DONTGOBELOWDOMAIN End With 'Abre a janela de procura 'E retorna o caminho da pasta selecionada lpIDList = SHBrowseForFolder(tBrowseInfo) 'Se existir alguma pasta selecionada If (lpIDList) Then sBuffer = Space(MAX_PATH) 'Esta função é chamada para converter o ID retornado no caminho da pasta SHGetPathFromIDList lpIDList, sBuffer sBuffer = Left(sBuffer, InStr(sBuffer, vbNullChar) - 1) 'Atribuimos o caminho na textbox txtPasta.Text = sBuffer End If End Sub
Por fim os retoques finais. O botão cmdEncontrar só pode estar habilitado se as duas caixas (txtpasta e txtprocurar) estiverem preenchidas. Deveremos então atribuir enabled=false em design e fazer o seguinte código:
Private Sub txtPasta_Change() cmdEncontrar.Enabled = (Trim(txtPasta.Text) <> "" And Trim(txtProcurar.Text) <> "") End Sub Private Sub txtProcurar_Change() cmdEncontrar.Enabled = (Trim(txtPasta.Text) <> "" And Trim(txtProcurar.Text) <> "") End Sub
Desta forma o botão será habilitado apenas quando houver conteúdo nas duas pastas. Por fim, detalhes de código fazem os últimos ajustes na aplicação:
- O label lblprocurar só deverá estar visível durante o processo de busca, portanto deverá ser exibido no inicio (visible=true) e escondido no final;
- Os labels com informações sobre o arquivo só deverão ser exibidos quando o usuário clicar em um arquivo pela primeira vez e deverão ser novamente escondidos quando uma nova busca for realizada;
- A collection (COL) precisa ser esvaziada quando uma nova busca estiver se iniciando.