Unable to move the file because it is being used in another process - vb.net

In my project I launch a batch file (not mine) that opens a pdf file, I wait 7000 milliseconds, then I close the Adobe processes and repeat the sequence again, at the end of everything I move the file to a new folder, but every now and then the program goes in exception
the function I use is this (vb.net):
Function Close(name as String)
{
dim processList as Process() = Process.GetProcessByName(name)
If processList.Count < 0 then
processList = Process.GetProcessByName(“AcroRd32”)
End If
For i as Integer = 0 to processList.Count - 1
processList(i).CloseMainWindow()
Next
}
I state that this exception when testing my program had never happened, in the client's desk it happened

Related

VB.NET: The process cannot access the file 'filename' because it is being used by another process

I should add a list of files into a ZIP. Procedure code is like this
Sub CreateZip
Dim FileList As New ArrayList 'List of File Paths to be added to the ZIP
For Each path in FileList
Try
AddFileToZip(ZipFilePath, path.ToString)
Catch (ex as New Exception)
....
End Try
Next
End Sub
And this is AddFileToZip
Public Sub AddFileToZip(ByVal zipFilename As String, ByVal fileToAdd As String)
Using zip As Package = System.IO.Packaging.Package.Open(zipFilename, FileMode.OpenOrCreate)
Dim destFilename As String = ".\" & Path.GetFileName(fileToAdd)
Dim uri As Uri = PackUriHelper.CreatePartUri(New Uri(destFilename, UriKind.Relative))
If zip.PartExists(uri) Then
zip.DeletePart(uri)
End If
Dim part As PackagePart = zip.CreatePart(uri, "", CompressionOption.Normal)
Using fileStream As New FileStream(fileToAdd, FileMode.Open, FileAccess.Read)
Using dest As Stream = part.GetStream()
CopyStream(fileStream, dest)
End Using
End Using
End Using
End Sub
At runtime, I get this error message
The process cannot access the file [ZipFilePath] because it is being used by another process
this error is raised randomly, maybe on adding small files into the Zip. If I place breakpoints and run procedure in debug mode, it works.
It seems clear that procedure thread is not synchronized with IO, so that my procedure loop continues even if IO is still adding processed file into Zip (ie VB.NET is faster than IO).
I also tried to place a Thread.Sleep(1000) before AddFileToZip, but this may be not enough to synchronize the two processes. Placing Thread.Sleep(2000) seems to make procedure work, but it may slow down dramaticly performances (I should pack more than 50 files into my Zip).
So, how can I force my loop to "wait" until IO Process has released ZIP file?

Filestream read only locking PC

I'm trying to read the Windows update log on remote PCs on my LAN. Most of the time I can successfully read the file but at times the program locks up. Likely due to one issue or another - doesn't really matter. What I do need is a way to recover when the Filestream/Streamreader locks up - I'm not sure which is causing the lock. Some streams can set a timeout but the filestream below returns False on a .CanTimeout call.
How can I break out if the stream locks up? (Sometimes the lock is so tight a power off is needed to recover.)
Is there a way to test if the stream will fail before I actually attempt the read?
Is there an alternate way to read a remote log file that another program has open? (I'm using the stream method because the regular File.IO was blocked because the file is open on the remote PC.)
I'm getting closer (I think) with this code. I browed the pathExists code from the referenced post but it was the OP and not an answer.
Imports System.IO
Import System.Threading
...
Function GetAULog(PCName As String) As String
Try
Dim sLogPath As String = String.Format("\\{0}\c$\Windows\SoftwareDistribution\ReportingEvents.log", PCName)
If PCName = My.Computer.Name Then
sLogPath = String.Format("C:\Windows\SoftwareDistribution\ReportingEvents.log", PCName)
End If
' read file open by another process
If Not pathExists(sLogPath) Then
MsgBox("AU log file not found - PC on?")
Return "NA"
End If
Using fs As New FileStream(sLogPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
Using sr As New StreamReader(fs)
Dim s As String = sr.ReadToEnd
Return s
End Using
End Using
Catch ex As Exception
MsgBox(ex.Message)
Return ""
End Try
End Function
Public Function pathExists(path As String) As Boolean
Dim exists As Boolean = True
Dim t As New Thread(New ThreadStart(Sub() exists = System.IO.File.Exists(path)))
t.Start()
Dim completed As Boolean = t.Join(500)
'half a sec of timeout
If Not completed Then
exists = False
t.Abort()
End If
t = Nothing
Return exists
End Function
At least when the PC is off the pathExists() code returns False in short order.
My problem now is the process does not end when the program exits - at least in the IDE, didn't check runtime.
I added t = Nothing but that didn't help. I couldn't figure out the proper Using syntax to test that. How do I properly cleanup after a thread timeout?
I've had the situation with this locking until restart problem. It seems to be caused by the tcpip auto tuning feature. You can cure this issue by running
netsh interface tcp set global autotuninglevel=disable
Run this on both machines if you have access. I tried a few workarounds for this issue with checking locks etc but the only way I could solve it was to disable this. The issue is not really with locking but with something at a lower level in the file sharing protocol.
See this article for more detail
"Final" code shown below. The exceptions are not firing when the timeout occurs so the .Abort was evidently OK.
When the timeout does occur, because the remote PC did not respond, there is a process left hanging which goes away after 30 seconds or so. I notice this when using the IDE, I run the program and test a PC that is off. If I then exit the program the form closes but the IDE hangs for ~30 seconds - I can click Stop-Debugging at this point and it works, but the IDE continues on its own after the ~30 second timeout.
I guess the t = Nothing in the Finally block does not dispose of the thread. t.Dispose does not exists.
So, things are working OK with the exception of the dangling thread that eventually clears itself up. The program is no longer hanging to the point where it cannot not be stopped.
'Imports System.IO
'Imports System.Threading
Public Function pathExists(path As String) As Boolean
' check for file exists on remote PC
Dim exists As Boolean = False
Dim t As New Thread(New ThreadStart(Sub() exists = System.IO.File.Exists(path)))
Try
t.Start()
Dim completed As Boolean = t.Join(500)
'half a sec of timeout
If Not completed Then
exists = False
t.Abort()
End If
Catch ex2 As ThreadInterruptedException
MsgBox("timeout on AU log exists test" & vbNewLine & ex2.Message,, "ThreadInterruptedException")
Catch exAbort As ThreadAbortException
MsgBox("timeout on AU log exists test" & vbNewLine & exAbort.Message,, "ThreadAbortException")
Catch ex As Exception
MsgBox("exception on AU log exists test" & vbNewLine & ex.Message)
Finally
t = Nothing
End Try
Return exists
End Function

Writing to Command Line Not Working

An application I need to use (USB capture utility) has a .cmd version that I can call from my Visual Basic code. I am able to launch the application and put it in "command line mode" like this:
Public Class MyClass
Dim StreamWriteUtility As System.IO.StreamWriter
Dim StreamReadUtility As System.IO.StringReader
Dim ProcessInfo As ProcessStartInfo
Dim Process As Process
Public Sub StartUSBCapture(ByVal DataStorageLocation As String)
Dim ProcessInfo As ProcessStartInfo
Dim Process As New Process
ProcessInfo = New ProcessStartInfo("C:\FW_Qualification_Suite\data-center-windows\data-center\bin\datacenter.cmd", "-c ")
ProcessInfo.CreateNoWindow = True
ProcessInfo.UseShellExecute = False 'Must be changed if redirect set to True
ProcessInfo.RedirectStandardInput = True
Process = Process.Start(ProcessInfo)
SWUtility = Process.StandardInput
While True
SWUtility.WriteLine("run") 'Looping for test to ensure this isn't a timing issue
End While
End Sub
End Class
This launches the application and opens a separate command line window that should accept further commands (i.e., capture, run, stop, etc). However, I am having trouble getting those subsequent commands to show up in the command line window. I've tried redirecting the standard input of the process, but still nothing shows up in the console window.
Can anyone tell how I'm supposed to actually get these commands from my Visual Basic program into this application?

Check the file whether is it by another process

I have some ask to you to check and advice if my code is enough and correct. My application works on files sometimes its downloading multiple files by windows service and then by other windows service parser process those files are parsing. Unfortunately before process file has to be finished downloading before process parser should open the file and play with it therefore i prepared some code to check firstly whether the file is free - means not open by another process or some person, whatever. I use most cases streamreader to open the file and play with it but as i said i would like firstly check file if its ready.
This is my code, what do you think:
Public Shared Function FileInUse(ByVal sFile As String, ByVal log As String) As Boolean
Dim thisFileInUse As Boolean = True
Dim counter As Integer = 0
For i = 0 To 30
counter += 1
If System.IO.File.Exists(sFile) Then
Try
Using f As New IO.FileStream(sFile, FileMode.Open, FileAccess.ReadWrite, FileShare.None)
thisFileInUse = False
Exit For
End Using
Catch
System.Threading.Thread.Sleep(10000) '10 sec
End Try
End If
Next
Return thisFileInUse
End Function

Auto-restart and then continue the Sub in VB.NET

Well I have some subs like:
Private Sub somesub()
'Processses that reach 900 mb in 1 hour and an half
End Sub
I want to restart the app, dispose memory and then return to where I was.
Exactly, I have an app that adds contacts and well It reach 900mb when 2000 contacts are added... I want to stop every 200 contacts, and do that I have said, and the code that I have no tested:
Imports SKYPE4COMLib
Public Class frmMain
Dim pUser As SKYPE4COMLib.User
Dim contactos As Integer
If contactos < 200 Then
For Each oUser In ListBox1.Items
pUser = oSkype.User(oUser)
pUser.BuddyStatus = SKYPE4COMLib.TBuddyStatus.budPendingAuthorization
oSkype.Friends.Add(pUser)
contactos += 1
Next
Else
'System.Windows.Forms.Application.Restart()
'I need a code that continues where I was, here...
End If
End Sub
End Class
What can I do? Thanks!
I have written some code below that may solve your problem. It certainly should save your position to a file and then when the file runs again, it would reload that position.
A couple of points.
I moved your declaration of pUser, and set it to nothing when I was done. This way the object is marked for disposal immediately.. you might get more than 200 revolutions out of this with that structure change.. but it might be a tad slower.
You will need some sort of reload for your listbox. I assume that you did not include it as part of your sample for brevity.
I changed your foreach loop to a for loop structure. This allows you to track your position within the list .. as a result I had to create a new oUser since you were iterating that in the foreach and did not list its type, you will need to fix that part of the code.
Obviously I have not compiled the below code, but it should give you a decent start on what your trying to do.
Be careful of the Process.Start, as you can set the current process to start another process and wait for that process to exit before exiting the current one and that would be very very very very bad and really cause an OutOfMemoryException quickly. You need to let the current process start the next instance and then without checking to see if it was successful at starting it .. exit. Or if you have used the restart command in your comments use that. The process spawning method might do what your wanting more effectively because your starting a new process on the computer and letting the old one be garbage collected (thus releasing the resources it was using.
Imports SKYPE4COMLib
Public Class frmMain
'Put code here to load position from the file
Dim startingPosition as Integer = 0
If IO.File.Exists("c:\filename.txt")
Using sr as New IO.StreamReader("c:\filename.txt")
sr.Read
StartingPosition = Convert.ToInteger(sr.ReadToEnd)
sr.Close
End Using
End If
'Probably needs some code somewhere to reload your listbox
Dim contactos As Integer
Dim CurrentPosition as Integer = 0
If contactos < 200 and StartingPosition < ListBox1.Items.Count Then
For x as integer = StartingPosition to ListBox1.Items.Count - 1
Dim oUser as <YOURTYPEHERE> = Ctype(ListBox1.Items(x), <YOURTYPEHERE>)
Dim pUser As SKYPE4COMLib.User
pUser = oSkype.User(oUser)
pUser.BuddyStatus = SKYPE4COMLib.TBuddyStatus.budPendingAuthorization
oSkype.Friends.Add(pUser)
contactos += 1
pUser = Nothing 'set the garbage collection to collect this.
CurrentPosition = x
Next
Else
'Save Your place to an external File, all your doing here is opening a file
'and saving the index of where you are in the listbox.
Using sw as New IO.StreamWriter("c:\filename.txt")
sw.Write(CurrentPosition)
sw.Close
End Using
'use the process namespace to have this app start the next copy of your app
'be careful not to use WaitForExit or you will have two copies in memory...
Process.Start("exename")
'or if the command below exists.. use that.. I never have.
'System.Windows.Forms.Application.Restart()
'I need a code that continues where I was, here...
End If
End Sub
End Class