Excel (VBA)

Excel VBAに関するフォーラムです。
  • 解決済みのトピックにはコメントできません。
このトピックは解決済みです。
質問

 
(Windows 10 Home : Excel 2013)
ファイルの最終更新日の取得について
投稿日時: 22/08/24 19:35:28
投稿者: Yaima

エクセルのシート上に、別のブック(データファイル)の最終更新日を表示したいのですが、
条件によって、正しく表示されないので困っています。
データファイルの種類は、Microsoft Excel 97-2003 Worksheet (.xls)です。
 
ファイルの最終更新日は、FileDateTime関数又は、Scripting.FileSystemObjectを
使えば可能ということなので、2つの方法とも試してみましたが、
データファイルが開かれている状態では、正しい結果が得られません。
 
そこで、下のコードを使って、色々な種類のファイルについて、
ファイルを開いた状態と開いてない状態で試してみました。
ただし、このコードでは、.txtファイルについては、開いていることの判定が
できなかったのですが、今回の問題とは異なるので無視することとしました。
 
結果は、Microsoft Excel 97-2003 Worksheet (.xls)以外のファイル型式については、
FileDateTime関数、Scripting.FileSystemObjectのいずれの方法でも正しい結果となりました。
Microsoft Excel 97-2003 Worksheet (.xls)については、ファイルを開いた時刻が返されるようです。
 
何か勘違いしているのか、Excel VBAの仕様なのか分かりません。
ご助言、ご指摘等をお願いします。
 
また、別の方法で実現できるのであれば、ご教授くださるようお願いします。
 
使用したコード(D:\に Test.xls、Test.xlsx等をテスト用に作成して試しました。)
 
Sub Test()
    Dim tmp, i As Long, xFName As String
    tmp = Array("xls", "xlsx", "xlsm", "csv", "txt")
    For i = LBound(tmp) To UBound(tmp)
        xFName = "D:\Test." & tmp(i)
        Debug.Print IsOpenFile(xFName), tmp(i), FileDateTime(xFName), "FileDateTime"
    Next
    For i = LBound(tmp) To UBound(tmp)
        xFName = "D:\Test." & tmp(i)
        With CreateObject("Scripting.FileSystemObject").GetFile(xFName)
            Debug.Print IsOpenFile(xFName), tmp(i), .DateLastModified, "FSO"
        End With
    Next
End Sub
 
Function IsOpenFile(xFName As String)
    Dim xFNo As Long, xErrNo As Long
    xFNo = FreeFile
    On Error Resume Next
    Open xFName For Binary Access Write Lock Write As #xFNo
    Close #xFNo
    xErrNo = Err.Number
    On Error GoTo 0
    If xErrNo = 0 Then
        IsOpenFile = "Close"
    Else
        IsOpenFile = "Open"
    End If
End Function

回答
投稿日時: 22/08/24 21:31:49
投稿者: WinArrow
投稿者のウェブサイトに移動

通常、xls形式のファイルを開くと、更新モードで開かれます。
その時、最終更新日に、開いた時点の日時が書きこまれます。
上書き保存せずに閉じると、元の日時に戻ります。
ReadOnly=trueで開くと、更新日時は変更されません。
 
Excelの仕様ではなく、OSの仕様でしょう。
 

回答
投稿日時: 22/08/24 22:47:34
投稿者: simple

少なくとも、Excel2003頃にはそれは割とよく知られたことであって、
過去、議論したことが何回かあります。
 
Excel2003の頃の記事を紐解くと、
> ※開いているブックの場合は、FileDateTime関数やFSOのDateLastModified
>  プロパティが返す値は、正しくない場合があります(開いた後更新して
>  いなければ開いた日時が返ります)ので、そちらは使えません。

などという発言がありました。
(加えて、その場合でも、BuiltinDocumentProperties("Last Save Time")は
  常に固定した更新時刻を返します。)
 
私は、そういう記憶がありましたので実験してみましたら、
 
・xlsファイルは、上記のとおりの状況でした。 つまり(くどいですが)、
    ・開くと、FileDateTime関数やFSOのDateLastModifiedは暫定的に、開いた時刻を返し、
    ・閉じてから再度取得すると、それらは古い更新時間に戻っている。
    ・BuiltinDocumentProperties("Last Save Time")は開いても、変化しない時刻を返す。
・今どきのxlsmは動作が変わっていて、
    ・開いても、FileDateTime関数やFSOのDateLastModifiedが変わることは無いようです。
 
たぶん2007以降にMicrosoftが仕様を変更したのではないですかね。
そのように受け止めるしかありません。

こればっかりは、一般ユーザーの感知できるところではなく、
いわば天体観測と同じようなものです。
事実と受け止めるしかない、こちらから影響を及ぼせないという意味で。
 
■参考までに検証資料を以下に。
== xlsファイル ===================Microsoft Excel 97-2003 Worksheet (.xls)

Close状態---- 
2022/08/24 20:41:59 FileDateTime
2022/08/24 20:41:59 DateLastModified
Open状態---- 
2022/08/24 22:31:04 FileDateTime            ■  暫定的に開いた時刻を返す
2022/08/24 22:31:04 DateLastModified        ■  暫定的に開いた時刻を返す
2022/08/24 20:41:59 BuiltinDocumentProperties("Last Save Time")
再度Close状態---- 
2022/08/24 20:41:59 FileDateTime
2022/08/24 20:41:59 DateLastModified

== 20019のxlsmファイル ===================
Close状態---- 
2022/08/24 20:41:02 FileDateTime
2022/08/24 20:41:02 DateLastModified
Open状態---- 
2022/08/24 20:41:02 FileDateTime
2022/08/24 20:41:02 DateLastModified
2022/08/24 20:41:02 BuiltinDocumentProperties("Last Save Time")
再度Close状態---- 
2022/08/24 20:41:02 FileDateTime
2022/08/24 20:41:02 DateLastModified

【参考】
Sub test()
    Dim f$
    f = "D:\AAA.xls"    ' 
    check f
    f = "D:\BBB.xlsm"
    check f
End Sub

Function check(f As String)
    Dim fso As Object
    Dim wb As Workbook
    Set fso = CreateObject("Scripting.FileSystemObject")

    Debug.Print "Close状態---- "
    Debug.Print FileDateTime(f); "FileDateTime"
    Debug.Print fso.GetFile(f).DateLastModified; "DateLastModified"

    Set wb = Workbooks.Open(f)

    Debug.Print "Open状態---- "
    Debug.Print FileDateTime(f); "FileDateTime"
    Debug.Print fso.GetFile(f).DateLastModified; "DateLastModified"
    Debug.Print wb.BuiltinDocumentProperties("Last Save Time"); "BuiltinDocumentProperties(""Last Save Time"")"
    
    wb.Close False
    
    Debug.Print "再度Close状態---- "
    Debug.Print FileDateTime(f); "FileDateTime"
    Debug.Print fso.GetFile(f).DateLastModified; "DateLastModified"
    
    Set fso = Nothing
End Function

投稿日時: 22/08/25 21:24:08
投稿者: Yaima

WinArrowさん
simpleさん
ありがとうございます。
 
教えていただいた方法で、
自分でデータファイルを開く場合は、
ReadOnly=True
 
データファイルが他のシステムで開かれている場合は、
BuiltinDocumentProperties("Last Save Time")
 
を使えば期待した結果が得られることが確認できました。
 
システム全体を自分で作る場合は、.XLS ではなく、.XLSXとすれば、
問題はなくなると思うのですが、
他のシステムと連携する場合は、まだまだ.XLSが使われている
こともあるので、とても参考になりました。
 
3つの方法をケースバイケースで使い分けていこうと思います。