I need to unzip en zip some files in my application using Shell32. Right now, I use srcFolder.CopyHere(destFolder.Items()) to achieve this. However, my next line of code requires the newly made ZIP-file. But since the CopyHere method is Async, how can I check when it in finished? Right now I use a Thread.Sleep for around 500 ms which is enough for my computer to finish creating the ZIP file, but it's not good code imo.
Any ideas?
More info/code can be provided if necessary.
Here is another one for waiting to finish copying
'Wait until the File/Folder has finished writing to zip file...
Do Until Shell.NameSpace(Filename).Items.Count = Shell.NameSpace(Input).Items.Count
System.Threading.Thread.Sleep(200)
System.Diagnostics.Debug.Print("Waiting...")
Loop
I found it, used something like this:
srcFolder.CopyHere(destFolder.Items())
While FileInUse(FILEPATH & "BudgetJaarOverzichtSMB.zip")
Thread.Sleep(100)
End While
Private Function FileInUse(ByVal FilePath As String) As Boolean
Try
FileOpen(1, FilePath, OpenMode.Input)
FileClose(1)
Return False ' File not in use
Catch ex As Exception
Return True ' File in use
End Try
End Function
Not really perfect but will lose less time than with my first approach.
Related
I want to write some text to a .txt file, this is what I have, and it works mostly:
If System.IO.File.Exists(sListItems) = True Then
Dim writeToFile As New System.IO.StreamWriter(sListItems, True)
writeToFile.WriteLine(vbCrLf & txtGameTitle.Text)
writeToFile.Close()
Else
MsgBox("Error!")
End If
End If
The problem is with that, when I enter text such as 'Hello', it would instead put into the txt file 'Hello ' (with a space). Is there anyway to resolve this?
Don't use File.Exists(). It's a race condition waiting to blow up, and it's slower. Instead, look for the FileMode that matches how you want to use the file; then handle the exception if it fails (you need to handle the exception anyway, because of the race condition potential mentioned earlier).
Additionally, if you have your Using blocks correct, you don't need to call .Close().
Try
Using fs As New FileStream(sListItems, FileMode.Open)
fs.Seek(0, SeekOrigin.End)
Using sw As New StreamWriter(fs)
sw.WriteLine(txtGameTitle.Text)
End Using
End Using
Catch ex As IOException
MsgBox("Error! -- " & ex.Message)
End If
But I wonder if you really care that the file already exists, and all you really need to do is this:
File.AppendAllText(sListItems, txtGameTitle.Text)
Well, I think I figured it out. I changed:
writeToFile.WriteLine()
to
writeToFile.Write()
and it no longer gives spaces.
Edit:
I know what my problem was, I was using lbGameList.SelectedItem.ToString() to find the name and it was giving me spaces, so I added .TrimEnd to it and it works now, thanks
i have an application that create a txt file and write in it a log.
I create the txt file with the following code:
If System.IO.File.Exists(sFileName) = True Then
System.IO.File.Delete(sFileName)
End If
'System.IO.File.Create(sFileName) '.dispose
Dim objWriter As New System.IO.StreamWriter(sFileName, True)
When i finish to write the log (using objWriter.WriteLine) and close it (objwriter.close and objwriter.dispose), send it by mail and need to delete it.
In order to delete the file i use this code:
For i = 0 To 10
Try
'System.IO.File.Delete(sFileName)
'My.Computer.FileSystem.DeleteFile(sFileName, FileIO.UIOption.OnlyErrorDialogs, FileIO.RecycleOption.DeletePermanently)
My.Computer.FileSystem.DeleteFile(sFileName)
Exit For
Catch ex As Exception
If i = 10 Then
invioMailTest(ex.ToString)
End If
Threading.Thread.Sleep(1000)
End Try
Next
The code works well in local, but when i run it on the server (as administrator) it gives me the following error:
System.IO.IOException: The process cannot access the file 'C:\Log_Eventi\Export log 2016-04-13.txt' because it is being used by another process.
i don't know how to delete it and i'm losing too much time on it....
When you create your file I'm guessing you are using
System.IO.File.Create(sFileName)
You can overcome that problem by using this code:
Using File.Create(sFileName)
End Using
put all code that deals with sFileName within Using.
other solutions:
use Close when you create it
System.IO.File.Create(sFileName).Close()
use filestream
Dim fs As FileStream = File.Create(sFileName)
do your work here
fs.Close()
You mention "send it by mail", so the problem is likely that the attachment is still being held by the server's email system.
I check for the existence of the file with File.Exists(filePath). Then I try to open the file from within Excel with Excel.Workbooks.OpenText(filePath). But Excel complains that the file's not there. What the heck?
The context is that I am shelling out to another application to process a given file and produce a .out file, which I then convert to an Excel workbook.
'' At this point, filePath is a .txt file.
Dim args As String = String.Format("""{0}""", filePath)
...
Dim exe As String = Config.ExtractEXE
Dim i As New ProcessStartInfo(exe)
i.Arguments = args
Dim p As Process = Process.Start(i)
p.WaitForExit()
...
'' filePath now becomes the .out file.
'' Then eventually, I get around to checking:
'If Not File.Exists(filePath) Then
' MsgBox("Please ensure...")
' Exit Sub
'End If
'' In response to an answer, I no longer check for the existence of the file, but
'' instead try to open the file.
Private Function fileIsReady(filePath As String) As Boolean
Try
Using fs As FileStream = File.OpenRead(filePath)
Return True
End Using
Catch
Return False
End Try
End Function
Do Until fileIsReady(filePath)
'' Wait.
Loop
ExcelFile.Convert(filePath...)
'' Wherein I make the call to:
Excel.Workbooks.OpenText(filePath...)
'' Which fails because filePath can't be found.
Is there a latency issue, such that .Net recognizes the existence of the file before it's accessible to other applications? I just don't understand why File.Exists() can tell me the file is there and then Excel can't find it.
As far as I know, the only application that might have the file open is the application I call to do the processing. But that application should be finished with the file by the time p.WaitForExit() finishes, right?
I've had to deploy the application with this as a known bug, which really sucks. There's an easy workaround for the user; but still--this bug should not be. Hope you can help.
Whether or not a file exists is not the only factor in whether you can open it. You also need to look at file system permissions and locking.
File.Exists can lie to you (it returns false if you pass a directory path or if any error occurs, even if the file does exist)
The file system is volatile, and things can change even in the brief period between an if (File.Exists(...)) line and trying to open the file in the next line.
In summary: you should hardly ever use file.exists(). Almost any time you are tempted to do so, just try to open the file and make sure you have a good exception handler instead.
The following codes is moving a file as long as the file doesn't already exist. If it does, it won't move the file.
My question is regarding the File.Move. When will the msgbox display? Will it display once the file is completely moved or will it display right after the File.Move line is executed.
Depending on the file size, it may take awhile to move the file and thus I don't want the msgbox to display until the file is moved completely.
Is there a better way of doing this?
For Each foundFile As String In My.Computer.FileSystem.GetFiles("C:\Temp\", FileIO.SearchOption.SearchAllSubDirectories, "*.zip")
Dim foundFileInfo As New System.IO.FileInfo(foundFile)
If My.Computer.FileSystem.FileExists("C:\Transfer\" & foundFileInfo.Name) Then
Msgbox("File already exists and will not moved!")
Exit Sub
Else
File.Move(foundFile, "C:\Transfer\" & foundFileInfo.Name)
Msgbox("File has been moved!")
End If
Next
Accordingly to this source, the File.Movecall is synchronous, which means that your msgbox will be shown only after the file is moved, regardless of its size.
For completeness, if you don't want to block the UI, you can try something like this:
' This must be placed outside your sub/function
Delegate Sub MoveDelegate(iSrc As String, iDest As String)
' This line and the following go inside your sub/function
Dim f As MoveDelegate = AddressOf File.Move
' Call File.Move asynchronously
f.BeginInvoke(
foundFile,
"C:\Transfer\" & foundFile,
New AsyncCallback(Sub(r As IAsyncResult)
' this code is executed when the move is complete
MsgBox("File has been moved!")
End Sub), Nothing)
or you can explore the new async / await instructions.
File.Move is a synchronous operation, so the application will not execute the next line of code (your messagebox) until the move is complete.
As you indicated, if the file is large (and you are moving across drives) the messagebox will not show up until the file move is complete. This can create a poor user experience, as your GUI will appear to be non-responsive during this time.
I would recommend taking the time to learn how to utilize background threads or async/await calls to perform the operation in the background.
There is a good article on Asynchronous IO on MSDN: http://msdn.microsoft.com/en-us/library/kztecsys.aspx
Finally you could also use the FileSystem object's MoveFile method, which can pop up a file move UI for you, if you are just worried about keeping your UI responsive:
FileSystem.MoveFile(sourceFileName, destinationFileName, UIOption.AllDialogs)
unfortunately, the code is executed line after the other so the Msgbox will pop up as long as the file has been completely moved.
if you want to monitor the progress, visit this link for more details.
The Message box will be displayed after the file is completely moved irrespective of the file size.
Unless a method is asynchronous, a line of code will always finish executing before proceeding with the next line.
Note, if the file move is slow, and it holding up your program is a Bad Thing, then you could do the move in a background thread using for instance a BackgroundWorker.
Using VBA, how can I:
test whether a file exists, and if so,
delete it?
1.) Check here. Basically do this:
Function FileExists(ByVal FileToTest As String) As Boolean
FileExists = (Dir(FileToTest) <> "")
End Function
I'll leave it to you to figure out the various error handling needed but these are among the error handling things I'd be considering:
Check for an empty string being passed.
Check for a string containing characters illegal in a file name/path
2.) How To Delete a File. Look at this. Basically use the Kill command but you need to allow for the possibility of a file being read-only. Here's a function for you:
Sub DeleteFile(ByVal FileToDelete As String)
If FileExists(FileToDelete) Then 'See above
' First remove readonly attribute, if set
SetAttr FileToDelete, vbNormal
' Then delete the file
Kill FileToDelete
End If
End Sub
Again, I'll leave the error handling to you and again these are the things I'd consider:
Should this behave differently for a directory vs. a file? Should a user have to explicitly have to indicate they want to delete a directory?
Do you want the code to automatically reset the read-only attribute or should the user be given some sort of indication that the read-only attribute is set?
EDIT: Marking this answer as community wiki so anyone can modify it if need be.
An alternative way to code Brettski's answer, with which I otherwise agree entirely, might be
With New FileSystemObject
If .FileExists(yourFilePath) Then
.DeleteFile yourFilepath
End If
End With
Same effect but fewer (well, none at all) variable declarations.
The FileSystemObject is a really useful tool and well worth getting friendly with. Apart from anything else, for text file writing it can actually sometimes be faster than the legacy alternative, which may surprise a few people. (In my experience at least, YMMV).
I'll probably get flamed for this, but what is the point of testing for existence if you are just going to delete it? One of my major pet peeves is an app throwing an error dialog with something like "Could not delete file, it does not exist!"
On Error Resume Next
aFile = "c:\file_to_delete.txt"
Kill aFile
On Error Goto 0
return Len(Dir$(aFile)) > 0 ' Make sure it actually got deleted.
If the file doesn't exist in the first place, mission accomplished!
The following can be used to test for the existence of a file, and then to delete it.
Dim aFile As String
aFile = "c:\file_to_delete.txt"
If Len(Dir$(aFile)) > 0 Then
Kill aFile
End If
In VB its normally Dir to find the directory of the file. If it's not blank then it exists and then use Kill to get rid of the file.
test = Dir(Filename)
If Not test = "" Then
Kill (Filename)
End If
set a reference to the Scripting.Runtime library and then use the FileSystemObject:
Dim fso as New FileSystemObject, aFile as File
if (fso.FileExists("PathToFile")) then
aFile = fso.GetFile("PathToFile")
aFile.Delete
End if
Here's a tip: are you re-using the file name, or planning to do something that requires the deletion immediately?
No?
You can get VBA to fire the command DEL "C:\TEMP\scratchpad.txt" /F from the command prompt asynchronously using VBA.Shell:
Shell "DEL " & chr(34) & strPath & chr(34) & " /F ", vbHide
Note the double-quotes (ASCII character 34) around the filename: I'm assuming that you've got a network path, or a long file name containing spaces.
If it's a big file, or it's on a slow network connection, fire-and-forget is the way to go.
Of course, you never get to see if this worked or not; but you resume your VBA immediately, and there are times when this is better than waiting for the network.
You can set a reference to the Scripting.Runtime library and then use the FileSystemObject. It has a DeleteFile method and a FileExists method.
See the MSDN article here.
A shorter version of the first solution that worked for me:
Sub DeleteFile(ByVal FileToDelete As String)
If (Dir(FileToDelete) <> "") Then
' First remove readonly attribute, if set
SetAttr FileToDelete, vbNormal
' Then delete the file
Kill FileToDelete
End If
End Sub