How to double check if folder is empty`? - vb.net

I can't find a solution for my problem. My Code is deleting empty folders and in general working fine, but there is one exception. It goes through every path one time, but if there was a Folder (A) that only has empty Folder (B) in it, then it would only delete Folder(B), since Folder (A) was not at empty at the time. How can I make it, so that it understands that Folder (A) is gonna be empty, once Folder (B) is deleted?
I thought DeleteEmptyFolder(folder.FullName) would solve the problem, but it is not working, since it doesn't repeat the same path it already went through. Removing directory.GetDirectories.Count = 0 doesn't work either, since it would delete any folder that doesn't have a file in it (even if there is another folder with files in it)
Private Sub DeleteEmptyFolder(ByVal sDirectoryPath As String)
If IO.Directory.Exists(sDirectoryPath) Then
Dim directory As New IO.DirectoryInfo(sDirectoryPath)
If directory.GetDirectories.Count = 0 AndAlso directory.GetFiles.Count = 0 Then
directory.Delete(True)
Return
End If
For Each folder As IO.DirectoryInfo In directory.GetDirectories()
DeleteEmptyFolder(folder.FullName)
Next
End If
End Sub
I am fairly new to VB.Net, so pardon if it is an obvious answer that I don't see.

Here's how the code ought to look:
Private Sub DeleteEmptyFolder(folderPath As String)
If Directory.Exists(folderPath) Then
For Each subFolderPath In Directory.EnumerateDirectories(folderPath)
DeleteEmptyFolder(subFolderPath)
Next
If Directory.EnumerateFiles(folderPath).Any() OrElse
Directory.EnumerateDirectories(folderPath).Any() Then
Return
End If
Directory.Delete(folderPath)
End If
End Sub
There's no point using DirectoryInfo if you need no other information about files and folders other than path. You should use EnumerateFiles and EnumerateDirectories over GetFiles and GetDirectories unless you specifically need to get an array of entries up front. In this case, you definitely don't. Let's say that you had a folder with 1000 files in it. This:
directory.GetFiles.Count = 0
would create an array containing an element for all 1000 files first, then check the number of elements in it. On the other hand, this:
Directory.EnumerateFiles(folderPath).Any()
would return True as soon as it encountered the first file, ignoring the other 999. You only care whether there's any files in the folder, not how many there are.

Please try this:
Private Sub deleteEmptyFolders(ByRef folder As String)
'Does exist such a path?
If IO.Directory.Exists(folder) Then 'yes
'Loop over all directories
For Each subFolder In IO.Directory.GetDirectories(folder)
'Delete all empty folders
deleteEmptyFolders(subFolder)
Next
'Delete folder if nothing remained in it
Try
My.Computer.FileSystem.DeleteDirectory(folder, FileIO.DeleteDirectoryOption.ThrowIfDirectoryNonEmpty)
Catch ex As Exception
End Try
End If
End Sub
I think this does what you want in a simple way.

I solved the problem by moving some of the code around.
Private Sub DeleteEmptyFolder(ByVal sDirectoryPath As String)
If IO.Directory.Exists(sDirectoryPath) Then
Dim directory As New IO.DirectoryInfo(sDirectoryPath)
For Each folder As IO.DirectoryInfo In directory.GetDirectories()
DeleteEmptyFolder(folder.FullName)
Next
If directory.GetDirectories.Count = 0 AndAlso directory.GetFiles.Count = 0 Then
directory.Delete(True)
Return
End If
End If
End If

Related

How to iterate through files and skip the ones to which I don't have access

I want to get all files in a directory, so I have used For Each loop.
but I got this error and stopped the loop.
System.UnauthorizedAccessException: 'Access to the path
'G:\$RECYCLE.BIN\S-1-5-18' is denied.'
The loop cannot access some of the files because of file permissions. I want to by-pass those files and move to the next file.
how can I do this,
Try
For Each filename As String In Directory.GetFiles(dir_path, pattern, sub_directory)
Console.WriteLine(filename.ToString)
Next filename
Catch ex As Exception
End Try
The loop is irrelevant. It's the call to GetFiles that is the problem. There is no way to call GetFiles with the recursive option and simply ignore inaccessible folders. You can only use that method when you know you can access every subfolder. Otherwise, you need to write your own recursive file search and explicitly catch those exceptions and ignore them. There are lots of examples of that on the web.
It should be noted that GetFiles does the entire search first, creates an array containing all the file paths and returns it, so your loop can't even begin until that's done. Even if this could work, it would still generally be preferable to call EnumerateFiles if you want to loop through the file paths on the spot. That's because EnumerateFiles is an iterator, basically returning the file paths one by one. That means that, for instance, you can break out of the loop if a particular condition is met without completing the entire search. EnumerateFiles will still throw an exception if it finds an inaccessible folder though, so it won't help here. If you do write your own method though, it would be nice to write an iterator if you plan to search big folders or you might not need to use all results.
EDIT:
Here's my home-spun versions. The first one does all the work first while the second is an iterator. Note that you may be able to make the first one more efficient by using a single external list to store all the file paths in rather than creating a new one in every recursive call.
Public Function GetFilesRecursively(path As String, searchPattern As String) As String()
Dim filePaths As New List(Of String)(Directory.GetFiles(path, searchPattern))
For Each folderPath In Directory.GetDirectories(path)
Try
filePaths.AddRange(GetFilesRecursively(folderPath, searchPattern))
Catch ex As UnauthorizedAccessException
'Ignore inaccessible folders
End Try
Next
Return filePaths.ToArray()
End Function
Public Iterator Function EnumerateFilesRecursively(path As String, searchPattern As String) As IEnumerable(Of String)
For Each filePath In Directory.EnumerateFiles(path, searchPattern)
Yield filePath
Next
For Each folderPath In Directory.EnumerateDirectories(path)
Try
For Each filePath In EnumerateFilesRecursively(folderPath, searchPattern)
Yield filePath
Next
Catch ex As UnauthorizedAccessException
'Ignore inaccessible folders
End Try
Next
End Function

VB .NET How to copy a directory without its subfolders

I want t copy all files in a directory, but ignore all sub-folders. Is there a single function to do this?
The standard function:
My.Computer.FileSystem.CopyDirectory(inputDir.FullName, outputDir.FullName)
copies all the sub-folders.
Iterating over all files:
For Each file In inputDir.GetFiles()
file.CopyTo(Path.Combine(outputDir.FullName, file.Name), True)
Next
seems to work fine. But it looks too complex to me. Is there a simpler way?
Make it into a function ...
Public Sub CopyFiles(inputPath As System.IO.DirectoryInfo,
outputPath As System.IO.DirectoryInfo)
For Each fi In inputPath.GetFiles()
fi.CopyTo(Path.Combine(outputPath.FullName, fi.Name), True)
Next
End Sub
... so it can simply be called like this when you want to use it
CopyFiles(New DirectoryInfo("C:\test1"), New DirectoryInfo("C:\test2"))

Call macros based on characters of a filename

How can I call macros based on the left n characters of a filename?
Details:
I get emailed many files per month whose names contain a few characters followed by a date or serial number.
For example
- Accounts receivable files are named ARDET 25-01-16.xls, ARDET 19-01-16.xls , ARDET 31-12-15.xls and so on
- Invoicing files are named Bkg_Inv_01.xls, Bkg_Inv_02.xls, Bkg_Inv_03.xls and so on
I have recorded various macros to run on these files. For example, I have Sub ARDET() and Sub Bkg_Inv() to handle the above files.
I want to create a single macro to call the above Subs if the first 5 characters of the filename matches certain text.
The code I am looking for needs to be roughly in the following syntax:
Sub Call_Macro_if_leftn_is()
' Making variables
This_File_name = Currently open file's filename
n = InputBox("Enter the total number of characters from the left of the filename to match")
y = Left n characters of This_File_name
'If Then statements to call other macros
If y = ARDET
Call ARDET()
Else if y = Bkg_I
Call Bkg_Inv()
Else if y = PDC_I
Call PDC_Inv()
Else Msgbox "Filename does not match specified characters"
End Sub
I'd try a set-up something like this:
Sub RunCodeBasedOnFileName()
Dim fileID As String
fileID = VBA.Left$(ThisWorkbook.Name, 5)
If fileID = "ARDET" Then
ARDET
ElseIf fileID = "Bkg_I" Then
Bkg_I
ElseIf fileID = "PDC_I" Then
PDC_I
Else
MsgBox "This file is of unknown origin!"
End If
End Sub
Sub ARDET()
'Do stuff
End Sub
Sub Bkg_I()
'Do stuff
End Sub
Sub PDC_I()
'Do stuff
End Sub
You don't need to use Call or have the parentheses () when calling a sub
I'd avoid getting people to input the first 5 characters - instead get it programmatically
The only bit I am unclear about is where you are running this code from and how you are iterating over the files that you are sent?
Where I have ThisWorkBook this requires the code to be running in the actual file. I think you'll need to modify that e.g. you loop over a bunch of files in a folder and access the filename that way.
Thanks Alex. I will test your code and tell you how it works.
I get such files through email atleast 10 times a day. I run my macros on files before I store them in folders named by month and category of file.
But coming to think of it, your idea of iterating through files in a folder is great ! Could you have the same code iterate over all files in a folder, and preferably, its sub folders as well?
My files are stored in folders in a hierarchy that goes like
- "C:\Dropbox\Work\2016\Jan\ARDET Jan" or
- "C:\Dropbox\Work\2016\Jan\BkgInv Jan"

How to rename directory with WinSCP .NET assembly?

I want to be able to rename directory whether it contains something or not. I do not see such option. For instance this is how I create directory, but how to rename it?:
Public Sub CreateDirectory(path As String)
If session IsNot Nothing Then
session.CreateDirectory(path)
End If
End Sub
Use the Session.MoveFile method:
session.MoveFile("/path/directory", "/path/new_name")

Open multiple files using arguments

I'm using this code to load multiple files using windows context menu, but the problem is that the aplication is open many times as files the user has selected.
For example: If I select 14 files, an open them with the application, the aplicacion is opened 14 times and load the form only one.
But there is a way to send all arguments once? Because %1 send only one file, or there is for example a %2 that send all file pats in one argument? If there is I'vent found.
This my actual code:
Public Class Program
Public Shared Sub Main()
Dim FurBase As New Core.clsDatabase
FurBase.Directory = My.Application.Info.DirectoryPath
Dim returnValue As String()
returnValue = Environment.GetCommandLineArgs()
If returnValue.Length > 1 Then
FurBase.AddTemporalFilepath(returnValue(1).ToString)
End If
If Not Process.GetProcessesByName(Process.GetCurrentProcess.ProcessName).Length > 1 Then
ShowUploader()
End If
End Sub
Private Shared Sub ShowUploader()
Dim Uploader As New frmUploader
Application.EnableVisualStyles()
Application.Run(Uploader)
End Sub
End Class
Please tell me what think about the code and if ther is any way to improve it.
Regards~
I was reading about that today; seems you'll need to deal with a DDE server.
There are an old question which can help you: What is the best .net alternative to dde for file associations?