Search code examples
.netpowershellfilestreammicrosoft-distributed-file-systemsystem.io.file

Unexpected behavior when opening a file stream in a PowerShell script


I am trying to create a script that appends (or creates, then appends) to an existing file. This file is being interfaced to from another system, so in order to avoid a situation where the file is being read while I'm appending to it, I'm creating a no-share lock on it. Script seems to work perfectly with one exception that I cannot understand. Here's a code sample:

$ErrorActionPreference = "Stop"
try
{
    [System.IO.FileStream]$file1 = [System.IO.File]::Open("test.txt", [System.IO.FileMode]::Append,[System.IO.FileAccess]::Write, [System.IO.FileShare]::None)
}
catch
{
    Write-Host "Unable to create a lock on this file"
}

There's one condition that does NOT trigger the catch block - if the file is already locked via a powershell console AND if this code is ran from a script and not directly typed in the window. If I open the file first in Excel, the catch block always works. If I execute this code by typing it into the console, it also always works. But if I lock a file first by typing this into the console, and then run a script that attempts to do the same thing, there's no exception thrown and the catch block does not trigger.

What am I missing here?

Update: I did additional troubleshooting and here's my findings. First, how to reproduce this:

  • Create a file named test.txt
  • Create a script named test1.ps1, place the above code in it
  • Run this command in console:

    [System.IO.FileStream]$file2 = [System.IO.File]::Open("test.txt", [System.IO.FileMode]::Append,[System.IO.FileAccess]::Write, [System.IO.FileShare]::None)
    
  • Now execute the test1.ps1 script

And the catch block of the script does not trigger... in my work environment! I tested at home, and it works as expected. So I went back to work and tested it again, this time using a file on the PC's local drive and it worked. The one difference that made it not work was the fact that the file I was working on was located on my company's DFS namespace. I tried it on a local network's drive mapped on my PC and it worked fine.

So, for whatever reason, PowerShell does not react to files locked by PowerShell on a DFS namespace like it is supposed to. Every other app I tried does detect the lock: Notepad and Excel refused to read the file, and when I tested it on a PDF document, Acrobat wouldn't open it either. But PowerShell does not complain, and even processes all the subsequent methods without an error - which have no effect; CopyTo() and Flush() methods do not fail, but the file is unchanged.


Solution

  • When you run the above code as a script the file/handle is automatically closed when the script (or, more precisely, the PowerShell process) terminates. You need to keep the script running to keep the handle open, e.g. by adding an infinite loop after the Open() call:

    $ErrorActionPreference = "Stop"
    try {
        $writeStream = [IO.File]::Open($FilePath,[IO.FileMode]::Append,[IO.FileAccess]::Write, [IO.FileShare]::None)
        do {
            Start-Sleep -Milliseconds 100
        } while ($true)
    } catch {
        Write-Host "Unable to create a lock on this file"
    }