2017年12月29日 星期五

VB.NET 檔案是否被鎖定

  在和朋友聊到他寫的程式,有些使用者反應程式沒有正常執行(好像停擺了),和朋友討論著有哪些原因會造成程式無法正常執行,其中討論到會不會是檔案還在建立中還沒有完成,結果程式去搬移了檔案或複製檔案發生例外錯誤,導致程式無法正常運作。
  我和朋友把這個原因,納入是程式無法正常執行的原因,開始上網搜尋資料,找到以下幾篇參考資料,自己先作個陽春版的程式,實作先檢查檔案是否存在,再檢查檔案是否可以正常讀取檔案,如果不能讀取就表示檔案已被鎖定(Lock)。


參考資料1:[C#]如何偵測特定檔案是否為Lock狀態
參考資料2:can't check in file : file is open by another application
參考資料3:VB.NET Checking if a File is Open before proceeding with a Read/Write?

說明

首先拉個陽春的畫面,畫面上放上2個TextBox、1個Button,TextBox1的作用是自己填上完整的檔案路徑、TextBox2顯示執行過程訊息(其屬性設定為ScrollBars=Vertical、Multiline=True),Button1為執行按鈕。

P.S 因為是陽春測試的小程式,所以沒有防呆,在測試時路徑要填寫完整(例如:D:\test.mp4),不要填錯了。

程式碼

Imports System.IO
Imports System.Runtime.InteropServices

Public Class Form1

    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
        Dim str_thispath As String = String.Empty '完整檔案路徑
        str_thispath = TextBox1.Text
        TextBox2.Text = ""
        If File.Exists(str_thispath) Then
            IsFileOpen(str_thispath)
        Else
            TextBox2.Text = "檔案不存在:" & Date.Now.ToString("yyyy/MM/dd HH:mm:ss.fffffff") & " 結束執行。" & vbNewLine & TextBox2.Text
        End If
        TextBox2.Text = "系統於 " & Date.Now.ToString("yyyy/MM/dd HH:mm:ss.fffffff") & " 結束執行。" & vbNewLine & TextBox2.Text
    End Sub

    Function IsFileOpen(ByVal str_thispath As String) As Boolean
        Dim stream As FileStream = Nothing
        Try
            stream = File.Open(str_thispath, FileMode.Open, FileAccess.ReadWrite, FileShare.None)
            stream.Close()
            TextBox2.Text = "檔案可開啟:" & Date.Now.ToString("yyyy/MM/dd HH:mm:ss.fffffff") & " " & vbNewLine & TextBox2.Text
        Catch ex As Exception

            If TypeOf ex Is IOException AndAlso IsFileLocked(ex) Then
                TextBox2.Text = "發生例外錯誤:原因可能檔案正在使用中已上鎖..." & Date.Now.ToString("yyyy/MM/dd HH:mm:ss.fffffff") & " " & vbNewLine & TextBox2.Text
                Return True
            End If
        End Try
        Return False
    End Function

    Function IsFileLocked(exception As Exception) As Boolean
        Dim ERROR_SHARING_VIOLATION As Integer = 32, ERROR_LOCK_VIOLATION As Integer = 33
        Dim errorCode As Integer = Marshal.GetHRForException(exception) And ((1 << 16) - 1)
        Return errorCode = ERROR_SHARING_VIOLATION OrElse errorCode = ERROR_LOCK_VIOLATION
    End Function

End Class