My script generally assumes the existence of a *.txt file with settings to help it function better. However, if the script doesn't exist, it creates a local file to hold these settings. I realise there's no logical need to then read this file, but I'd like to understand why I can't.
[void][System.IO.File]::Create($PSFileName)
$ReadPS = New-Object System.IO.StreamReader($PSFileName)
Immediately after the script may (rarely) create the file, it attempts to read it, which generates the following error: New-Object : Exception calling ".ctor" with "1" argument(s): "The process cannot access the file 'C:\Temp\MyFile.txt' because it is being used by another process."
So I have to wait for the file to be available, right? Yet a simple start-sleep for 5s doesn't work. But if I wrap it in a loop with a try-catch, it works within a fraction of a second every time:
[void][System.IO.File]::Create($PSFileName)
$RCount = 0 # if new file created, sometimes it takes a while for the lock to be released.
Do{
try{
$ReadPS = New-Object System.IO.StreamReader($PSFileName)
$RCount+=100
}catch{ # if error encountered whilst setting up StreamReader, try again up to 100 times.
$RCount++
Start-Sleep -Milliseconds 1 # Wait long enough for the process to complete. 50ms seems to be the sweet spot for fewest loops at fastest performance
}
}Until($RCount -ge 100)
$ReadPS.Close()
$ReadPS.Dispose()
This is overly convoluted. Why does the file stay locked for an arbitrary length of time that seems to increase the more I wait for it? Is there anything I can adjust or add between the file creation and the StreamReader to ensure the file is available?
Get-Contenton that file, i get a red error text sayingGet-Content : The process cannot access the file 'C:\Temp\Testing.txt' because it is being used by another process. if i do 3 calls in a row, the 1st fails, but the 2nd & 3rd show no errors. if i addStart-Sleepbefore the 1st G-C call, i get an error on that one. ///// i suspect that it is because the whole script STOPS for 5 seconds ... and the file handle is held open while the script is stopped. that seems to agree with your try/catch solution ...File.Createproduces an open file handle encapsulated by aFileStream. If you don't dispose it, that will cause a locking conflict (despite the fact that you, the original process, opened it). Use[System.IO.File]::Create($PSFileName).Dispose(), or (a little more posh)$null | Out-File $PSFileName -Encoding ascii.Out-Fileis a little too obscure:[System.IO.File]::WriteAllBytes($PSFileName, @()). Note that both this andOut-Filewill truncate the file if it already exists; if this is not desirable, use-AppendforOut-FileandAppendAllText($PSFileName, "")forFile(there is, oddly enough, noFile.AppendAllBytesmethod).