Funções Recursivas
Recursão é um método de programação no qual uma função pode chamar a si mesma. O termo é usado de maneira mais geral para descrever o processo de repetição de um objecto de um jeito similar ao que já fora mostrado. Um bom exemplo disso são as imagens repetidas que aparecem quando dois espelhos são apontados um para o outro. (Fonte Wikipédia)
As funções recursivas são muito utilizadas nas diversas linguagens de programação. São funções que se chamam a si mesmo, permitindo simplificar código e executar tarefas que seriam bastante complicadas sem a sua utilização. Por exemplo, o preenchimento de uma Treeview ou mesmo o sistema utilizado na construção de fóruns, em que uma questão tem sub-questões e essa sub-questão pode ter mais sub-questões, e por aí fora.
Outro dos exemplos de aplicação deste método é a procura de controlos dentro de um Form. Através de um ciclo nos controlos do Form conseguem-se encontrar todos os seus controlos, mas os Containers (controlos que conseguem alojar outros controlos, como é o caso de um Panel ou GroupBox) podem por sua vez ter mais controlos. Um método recursivo resolve o problema.
No seguinte exemplo serão procuradas todas as CheckBoxes dentro do Form (incluindo dentro dos Containers) e seleccionar ou não seleccionar todas, de acordo com a escolha efectuada.
' Definição do tipo de acção
Enum SelectOption
Unselect = 0
[Select] = 1
End Enum
''' <summary>
''' Função recursiva que irá percorrer todos os controlos dentro
''' de um controlo principal, que será indicado
''' </summary>
''' <param name="ctrl">Controlo a </param>
''' <param name="option">Acção a efectuar</param>
Private Sub SelectCheckBoxes(ByVal ctrl As Control, ByVal [option] As SelectOption)
' Ciclo em todos os controlos
For Each c As Control In ctrl.Controls
' Se o control for do tipo CheckBox converte para
' o tipo CheckBox e executa a opção indicada
If TypeOf c Is CheckBox Then
Dim cb As CheckBox = DirectCast(c, CheckBox)
cb.Checked = [option]
End If
' Caso o controlo tenha sub-controlos, chama novamente
' a função actual (recursivamente) até não existirem mais.
If c.HasChildren Then
SelectCheckBoxes(c, [option])
End If
Next
End Sub
Finalmente para utilizar o Sub criado é apenas necessário:
' Selecciona todos os controlos
Private Sub btnAll_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAll.Click
SelectCheckBoxes(Me, SelectOption.Select)
End Sub
' Retira a selecção de todos os controlos
Private Sub btnNone_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnNone.Click
SelectCheckBoxes(Me, SelectOption.Unselect)
End Sub
Este é um exemplo de como utilizar funções recursivas para seleccionar CheckBoxes mas poderá ser utilizado para procurar um controlo, contar o número de CheckBoxes seleccionadas, etc, etc.
Copiar dados do Excel para uma DataGridView
A DataGridView já tem a possibilidade de permitir copiar os seus dados e colar em outra aplicação. A propriedade que permite isto é a ClipboardCopyMode, que por defeito está seleccionada para EnableWithAutoHeaderText. No entanto o inverso não está previsto.
Para se adicionar está possibilidade à DataGridView é necessário ler a classe Clipboard através da sua função GetText(). Deste modo consegue-se ler o que está no Clipboard e colocar na DataGridView.
Este exemplo, que mostra como implementar esta funcionalidade, utiliza uma DataGridView sem estar vinculada a dados e utiliza a combinação de teclas Ctrl+V para colar os dados. Verifica ainda se o número de colunas copiadas é igual ao número de colunas existentes.
' No evento KeyDown da DataGridView
Private Sub DataGridView1_KeyDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles DataGridView1.KeyDown
' Caso as teclas pressinadas sejam CTRL+V
If e.Control AndAlso e.KeyCode = Keys.V Then
Try
' Ciclo nas linhas copiadas
For Each line As String In Clipboard.GetText.Split(vbNewLine)
' Separa as colunas referentes à linha actual
Dim item() As String = line.Trim.Split(vbTab)
' Se o número de colunas for diferente mostra uma mensagem de erro
If item.Length <> Me.DataGridView1.ColumnCount Then
Dim str As String = "O número de colunas copiadas é diferente do número de colunas da DataGridView"
Throw New Exception(str)
End If
' Adicionar a linha a DataGridView
Me.DataGridView1.Rows.Add(item)
Next
Catch ex As Exception
' Mensagem de erro caso exista
MessageBox.Show(ex.Message, My.Application.Info.Title, MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End If
End Sub
PS: Como sempre, qualquer dúvida, comentário ou correcção ao artigo é sempre bem vinda!
Os Windows Services, normalmente conhecidos por Serviços NT, permite criar aplicações executáveis que correm junto dos serviços do sistema operativo do Windows. Estes serviços arrancam com o sistema operativo, independentemente do utilizador activo, e não são visíveis (correm em background), sendo portanto indicados para operações em servidores ou operações que não interferem com o utilizador. Antes do lançamento do.Net este processo era apenas conseguido por aplicações em C++, complicadas e não acessíveis a todos.
Como os Windows Services correm como serviço, não é possível utilizar controlos, mostrar mensagens ao utilizador através de msgbox’s, etc. Também não é possível validar o código criado, sendo aconselhável criar um programa normal “Windows Project”, testar e só depois colocar o código no serviço.
Para um exemplo de como criar e instalar um Windows Service, será utilizado outro componente do .Net Framework, o FileSystemWatcher.
O FileSystemWatcher é um componente no namespace System.IO que permite observar uma directoria específica. Ele verifica alterações nos ficheiros e sub-directorias de acordo com um determinado filtro e detecta se um ficheiro foi apagado, criado, modificado, etc, sendo uma excelente ferramenta de monitorização.
Neste exemplo será criado um serviço que irá monitorizar numa directoria os ficheiros mp3 e gravar num ficheiro log todas as alterações efectuadas.
Para criar o serviço é necessário executar os seguintes passo:
1 - Criar um novo projecto e seleccionar um “Windows Service”. Alterar o nome do projecto para myWindowsService. Irá ser criado um novo serviço com o nome Service1.vb
2 – Criar com o botão direito do rato sobre a área criada e seleccionar “Add Installer”. O Visual Studio irá criar dois itens: ServiceInstaller1 e ServiceProcessInstaller1.
O ServiceInstaller não faz parte do serviço em si, servindo apenas para registar o serviço quando este for instalado. É através dele que se especifica que tipo de arranque o serviço tem (Manual, Automatic ou Disabled), qual o nome visível, qual a descrição geral do serviço, entre outras definições.
O ServiceProcessInstaller é chamado pelo aplicativo de instalação e serve para escrever no registry do Windows informações sobre o serviço que se vai instalar.
Em conjunto estas duas classes permitem instalar um serviço de uma forma bastante simples.
3 – Seleccionado o ServiceInstaller1 alterar alguns dados, como a Description e DisplayName na janela Properties. Seleccionar o ServiceProcessInstaller1 e alterar a propriedade Account para LocalService.
4 – Seleccionar o Service1.vb e mudar para o código. É aqui que se desenvolve o código que o serviço irá executar. Utilizar o seguinte código.
Nota: Criar a directoria “c:\mp3\” ou alterar a localização no código do serviço
Imports System.IO
Public Class Service1
' Indicação do nome do ficheiro log
Private Const logFileName As String = "c:\mp3\history.log"
Private watcher As FileSystemWatcher
' No arranque do serviço
Protected Overrides Sub OnStart(ByVal args() As String)
' Cria uma nova instância do FileSystemWatcher
watcher = New FileSystemWatcher
' Atribui eventos ao FileSystemWatcher
AddHandler watcher.Deleted, AddressOf OnChanged
AddHandler watcher.Created, AddressOf OnChanged
AddHandler watcher.Renamed, AddressOf OnRenamed
' Indicação do filtro, caminho e activação
With watcher
.NotifyFilter = NotifyFilters.FileName
.Path = "c:\mp3"
.Filter = "*.mp3"
.EnableRaisingEvents = True
End With
' Mensagem inicial de arranque do serviço
Dim logMsg As String = "Serviço iniciado em " & DateTime.Now.ToString
My.Computer.FileSystem.WriteAllText(logFileName, logMsg & vbNewLine, True)
End Sub
' Quando o serviço for parado
Protected Overrides Sub OnStop()
Dim logMsg As String = "Serviço parado em " & DateTime.Now.ToString
My.Computer.FileSystem.WriteAllText(logFileName, logMsg & vbNewLine, True)
End Sub
' Quando um ficheiro for renomeado
Sub OnRenamed(ByVal sender As Object, ByVal e As System.IO.RenamedEventArgs)
Try
' Mensagem com o nome anterior e o novo nome do ficheiro
Dim logMsg As String = _
String.Format("Ficheiro renomeado de ""{0}"" para ""{1}"" em {2}", _
e.OldName, e.Name, DateTime.Now.ToString)
' Escreve no ficheiro log
My.Computer.FileSystem.WriteAllText(logFileName, logMsg & vbNewLine, True)
Catch ex As Exception
' Em caso de erro, escreve a mensagem geral com o erro
Dim logError As String = ex.Message.ToString & " - " & DateTime.Now.ToString
My.Computer.FileSystem.WriteAllText(logFileName, logError & vbNewLine, True)
End Try
End Sub
' Caso algum ficheiro seja apagado ou criado
Private Sub OnChanged(ByVal source As Object, ByVal e As FileSystemEventArgs)
Try
Dim logMsg As String = String.Empty
' Verifica qual a acção e cria a mensagem
Select Case e.ChangeType
Case WatcherChangeTypes.Created
logMsg = String.Format("Ficheiro ""{0}"" criado em {1}", e.Name, DateTime.Now.ToString)
Case WatcherChangeTypes.Deleted
logMsg = String.Format("Ficheiro ""{0}"" apagado em {1}", e.Name, DateTime.Now.ToString)
End Select
' Escreve no ficheiro log
My.Computer.FileSystem.WriteAllText(logFileName, logMsg & vbNewLine, True)
Catch ex As Exception
' Em caso de erro, escreve a mensagem geral com o erro
Dim logError As String = ex.Message.ToString & " - " & DateTime.Now.ToString
My.Computer.FileSystem.WriteAllText(logFileName, logError & vbNewLine, True)
End Try
End Sub
End Class
5 – Compilar o programa (build) e irá ser criado o myWindowsService.exe
Com o serviço já criado é apenas necessário instalá-lo. Para instalar um serviço é necessário utilizar um utilitário disponível com o .Net Framework e que corre em ambiente DOS - o InstallUtil.exe. Este ficheiro encontra-se na directoria de instalação da plataforma .Net Framework, como por exemplo para a versão 2.0 em C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\
6 – Ir ao menu do Windows Start – Run e escrever cmd para abrir uma janela do DOC
7 – Indicar a localização do ficheiro e do serviço criado para proceder à instalação. Exemplo:
Instalar:
<localização do ficheiro>\InstallUtil.exe <localização do serviço>\myWindowsService.exe
Desinstalar:
<localização do ficheiro>\InstallUtil.exe /u <localização do serviço>\myWindowsService.exe
Nota: Uma solução mais simples é copiar o ficheiro executável do serviço e o ficheiro InstallUtil.exe para o mesmo directório/pasta simplificando o comando no DOS.
8 – Para terminar ir ao Control Panel – Administrative Tools – Services e iniciar o serviço criado.
Para testar o serviço é só copiar ficheiros mp3 para dentro da directoria c:\mp3\, apaga-los, alterar os nomes e verificar o registo no ficheiro log. Caso seja necessário alterar algum código ao serviço é preciso desinstala-lo (ver ponto 7), efectuar as alterações, compilar o projecto e instalar novamente.
Este é um exemplo de como criar Windows Services e como são úteis para diversas aplicações. O FileSystemWatcher é utilizado no exemplo mas pode, obviamente, ser utilizado numa aplicação normal para o Windows.
PS: Como sempre, qualquer dúvida, comentário ou correcção ao artigo é sempre bem vinda!
O Microsoft Scripting Runtime está incluído em diversos programas do Windows, como é o caso do Office e do próprio sistema operativo, e permite um fácil acesso a ficheiros e pastas, bem como simplifica o acesso de leitura e escrita de ficheiros de texto.
Por ser uma ferramenta disponível no sistema operativo pode ser usada por o Excel (VBA) mas também por outras aplicações que utilizem VBA (Access, Word, Outlook, etc) ou pelo Visual Basic 6.
O Microsoft Scripting Runtime tem 6 objectos que podem ser usados com bastante simplicidades: Dictionary (criação de colecções); Drive (acesso às drives do sistema); File (acesso a ficheiros); FileSystemObject (objecto base para acesso a drives, pastas e ficheiros); Folder (acesso a pastas) e TextStream (leitura e escrita em ficheiros de texto).
No seguinte exemplo serão utilizados apenas alguns objectos para mostrar como aceder a todos os ficheiros, com base em uma pasta inicial e suas sub-pastas, e listar seus nomes e alguns dados adicionais.
Para implementar o exemplo é apenas necessário adicionar referência no VBE (Visual Basic Editor) ao Microsoft Scripting Runtime através do menu Tools - References e utilizar o seguinte código:
Option Explicit
' Cria uma nova instância do FileSystemObject
Private fso As New FileSystemObject
' Declaração de variáveis
Private sFolder As Scripting.Folder
Private myFile As Scripting.File
Private myFolder As Scripting.Folder
Private pos As Long
' --------------------------------------------------------------------
' Sub auxiliar que lista os ficheiros nas sub-pastas
' --------------------------------------------------------------------
Sub ShowSubFolderFiles(ByVal folderName As String)
' Ignora erros em pastas protegidas, pastas de sistema, etc
On Error Resume Next
Set sFolder = fso.GetFolder(folderName)
' Ciclo em todas as pastas
For Each myFolder In sFolder.SubFolders
‘ Ciclo em todos os ficheiros
For Each myFile In myFolder.Files
Cells(pos, 1).Value = myFile.Path
Cells(pos, 2).Value = myFile.DateCreated
Cells(pos, 3).Value = myFile.Size & " bytes"
Cells(pos, 4).Value = myFile.Type
Cells(pos, 5).Value = myFile.DateLastAccessed
pos = pos + 1
Next
' Recursividade: Caso a pasta tenha sub-pastas chama novamente
' o mesmo código - Sub ShowSubFolderFiles()
If myFolder.SubFolders.Count > 0 Then
ShowSubFolderFiles myFolder.Path
End If
Next
End Sub
' --------------------------------------------------------------------
' Sub principal que inicia o processo de listagem de
' todos os ficheiros, com base numa pasta inicial
' --------------------------------------------------------------------
Sub ShowFolderFiles()
Dim initialFolder As String
' Ignora erros em pastas protegidas, pastas de sistema, etc
On Error Resume Next
' Pasta inicial e linha da folha de cálculo onde será
' iniciada a escrita dos ficheiros encontrados
initialFolder = "C:\"
pos = 1
' Verifica se a pasta indicada existe
If Not fso.FolderExists(initialFolder) Then
MsgBox "A pasta " & initialFolder & " não existe!", vbCritical
Exit Sub
End If
' Define a pasta inicial
Set sFolder = fso.GetFolder(initialFolder)
' Mostra os ficheiros na pasta inicial
' incrementado a posição (linha) onde escreve
For Each myFile In sFolder.Files
Cells(pos, 1).Value = myFile.Path
Cells(pos, 2).Value = myFile.DateCreated
Cells(pos, 3).Value = myFile.Size & " bytes"
Cells(pos, 4).Value = myFile.Type
Cells(pos, 5).Value = myFile.DateLastAccessed
pos = pos + 1
Next
' Mostra os ficheiros nas sub-pastas
Call ShowSubFolderFiles(sFolder.Path)
' Limpa as variáveis da memória
Set fso = Nothing
Set myFolder = Nothing
Set myFile = Nothing
End Sub
Finalmente para testar o nosso código basta ir ao menu Tools – Macro – Macros (ALT+F8) e escolher a macro ShowFolderFiles(). Poderá, obviamente, ser utilizada através de um botão, menu, etc.
O seguinte exemplo mostra como listar ficheiros numa folha de cálculo mas o objectivo é mostrar as potencialidades desta biblioteca e como se podem fazer algumas operações.
Podem ser criados pastas, eliminados ficheiros, verificar se determinado ficheiros existe, escrever em ficheiros de texto, etc etc, de uma forma muito mais simples e sem o recurso a API’s ou códigos demasiado complicados.
PS: Como sempre, qualquer dúvida, comentário ou correcção ao artigo é sempre bem vinda!
Os programadores que utilizam VB6 e .Net certamente já disseram, quando estavam a programar em VB6, “Se isto fosse em .NET … era muito mais fácil!”. A verdade é que o .Net simplificou muitos processos, graças à plataforma .Net Framework e às suas classes.
Então se temos o .NET Framework instalado porque não a utilizamos?
É bastante simples de o fazer construindo classes COM Interop. Deste modo podemos utilizar no VB6 as potencialidades do .Net Framework, como por exemplo, enviar emails, encriptação, gestão de ficheiros, XML, compactação de ficheiros, etc, etc.
Para mostrar como utilizar as classes COM Interop no VB6 irá ser mostrado um pequeno exemplo utilizando o Namespace IO do VB.Net. Este exemplo irá apagar todos os ficheiros num directório indicado, com uma determinada máscara. No VB6 este processo é muito mais complicado sendo necessário percorrer todos os directórios e verificar quais os ficheiros que cumprem os requisitos para serem eliminados.
Para construir o nossa classe COM Interop fazer então o seguinte:
VB.NET
1 – Criar um novo projecto e seleccionar Class Library e alterar o nome para myComProject
2 – Eliminar a classe automaticamente criada “Class1.vb”
3 – Adicionar um novo item do tipo COM Class e alterar o nome para myComClass
Após isto o Visual Studio faz algumas configurações no nosso projecto e cria algum código na classe.
4 – Adicionar o Sub DeleteFiles (apenas adicionar este novo Sub e manter o restante). No final ficará com este aspecto:
<ComClass(myComClass.ClassId, myComClass.InterfaceId, myComClass.EventsId)> _
Public Class myComClass
#Region "COM GUIDs"
' These GUIDs provide the COM identity for this class
' and its COM interfaces. If you change them, existing
' clients will no longer be able to access the class.
Public Const ClassId As String = "0c6277bf-5e39-4682-8027-18b62c82ee6f"
Public Const InterfaceId As String = "154cdcef-f13f-4af2-82c8-fc6de1e74a7d"
Public Const EventsId As String = "07420108-f1c9-4b36-adbc-1373035429f7"
#End Region
' A creatable COM class must have a Public Sub New()
' with no parameters, otherwise, the class will not be
' registered in the COM registry and cannot be created
' via CreateObject.
Public Sub New()
MyBase.New()
End Sub
''' <summary>
''' Apaga todos os ficheiros numa determinada localização
''' </summary>
''' <param name="filePath">Directório a procurar</param>
''' <param name="filePattern">Máscara a eliminar</param>
''' <remarks></remarks>
Public Sub DeleteFiles(ByVal filePath As String, ByVal filePattern As String)
Try
' Verifica se o directório existe
If Not IO.Directory.Exists(filePath) Then
Throw New ArgumentException("A caminho indicado não é válido")
Exit Sub
End If
' Elimina os ficheiros de acordo com a máscara seleccionada
Dim files() As String = IO.Directory.GetFiles(filePath, filePattern)
For Each myfile As String In files
IO.File.Delete(myfile)
Next
Catch ex As Exception
Throw New ArgumentException(ex.Message)
End Try
End Sub
End Class
5 – Gravar e compilar o Projecto. Irá então ser criado um novo DLL.
VB6
6 – Abrir o VB6 e criar um novo projecto "Standard EXE"
7 – Ir ao menu Project – References e adicionar o *.tlb, que se encontra na pasta Bin\Release\, da nossa classe COM criada. Atenção: o ficheiro myComProject.tlb e não o myComProject.dll
8 – Adicionar um botão o Form e no evento click colocar o seguinte código:
Private Sub Command1_Click()
' Cria uma nova instância da nossa classe
Dim cls As New myComClass
' Apaga todos os ficheiros no directório d:\test que tenham a extensão txt
cls.DeleteFiles "d:\test", "*.txt"
End Sub
Como podem ver após este exemplo, é utilizar as potencialidades da plataforma .NET no Visual Basic 6. Esta funcionalidade permite, além de simplificar muito código, utilizar funções que só se conseguiam através do VB.NET.