ファイルの更新を監視するには?


ころっけ  2006-02-04 03:03:36  No: 130080

Win2000pro
VB6.0sp6
です。よろしくお願いします。

特定のテキストファイルを監視して、
そのテキストファイルが更新されれば即座にそれを感知し、
その内容を表示する、
というようなプログラムを作成したいです。

TimerコントロールとFileLen関数を組み合わせて、
一定間隔ごとにファイルのサイズを比較して、
変化を監視する、というのを思いつきましたが、
即座に更新を感知するためにはIntervalを短く設定する必要があり、
パフォーマンス的にどうかな、と思いました。

もっとスマートなやり方を教えてください。


ころっけ  2006-02-04 04:13:31  No: 130081

FindFirstChangeNotificationというAPIで、
特定のディレクトリを監視して、ファイルのサイズ変更等も監視
できるみたいなんですが、
以下のコードがうまくいきません。

これは標準モジュールです------------------------
Public Declare Function FindFirstChangeNotification Lib "kernel32.dll" _
      Alias "FindFirstChangeNotificationA" _
     (ByVal lpPathName As String, ByVal bWatchSubtree As Long, _
     ByVal dwNotifyFilter As Long) As Long

Public Declare Function FindCloseChangeNotification Lib "kernel32.dll" _
    (ByVal hChangeHandle As Long) As Long

Public Declare Function GetLastError Lib "kernel32.dll" () As Long

Public Const FILE_NOTIFY_CHANGE_FILE_NAME = &H1
Public Const FILE_NOTIFY_CHANGE_DIR_NAME = &H2
Public Const FILE_NOTIFY_CHANGE_ATTRIBUTES = &H4
Public Const FILE_NOTIFY_CHANGE_SIZE = &H8
Public Const FILE_NOTIFY_CHANGE_LAST_WRITE = &H10
Public Const FILE_NOTIFY_CHANGE_SECURITY = &H100
Public Const INVALID_HANDLE_VALUE = -1

----------------------ここまで

ここからがメインモジュールです----------------
Private Sub Command1_Click()
Dim obj_a As Long
    
    obj_a = FindFirstChangeNotification( _
                "c:\test.txt", _
                0, _
                FILE_NOTIFY_CAHNGE_LAST_WRITE _
                )
    If obj_a = INVALID_HANDLE_VALUE Then
           print "エラーです"
           print GetLastError
    End If
    
    Print FindCloseChangeNotification(obj_a)

End Sub
---------------------------------ここまで

これを実行するとフォームに、
「エラーです
0
0

と表示されます。どうやらうまくいっていないようですが、
原因がわかりません。


ころっけ  2006-02-04 04:16:18  No: 130082

すみません。上記のコード一箇所訂正です。
FindFirstChangeNotificationの第一引数は、
"c:\test"
です。


へむりん  2006-02-04 05:36:26  No: 130083

これでは?
×FILE_NOTIFY_CAHNGE_LAST_WRITE
○FILE_NOTIFY_CHANGE_LAST_WRITE

このあとでWaitForSingleObject()に続いていきます。

#Option Explicitは入れるようにしたほうが。。。


ころっけ  2006-02-04 05:44:54  No: 130084

何度もすみません。
以下のコードで一応それらしいことはできました。
標準モジュールにAPIや定数を宣言しています。

Private Sub Command1_Click()
    Dim obj_a As Long
    
    obj_a = FindFirstChangeNotification("c:\test", 0, FILE_NOTIFY_CHANGE_SIZE)
    
    If obj_a = INVALID_HANDLE_VALUE Then
        Print "エラー"
        Print GetLastError
    End If
   
    WaitForSingleObject obj_a, INFINITE
    MsgBox "変更された" 
  
    FindCloseChangeNotification (obj_a)

End Sub

しかし、このやり方ではディレクトリ全体に対する監視は出来ても、
ファイル単独に関して更新されたかどうかというのは判別できません。
上記のコードに各々のファイルに対してサイズの変化をチェックする
コードを加えなければなりません。
ということは、一番最初の投稿のように、
TimerコントロールとFileLen関数を使用して、1秒ごとのファイルの
サイズの変化をチェックするというやり方がベストなのでしょうか?
もっとよいやり方はないでしょうか?


ころっけ  2006-02-04 05:49:12  No: 130085

へむりんさん、ありがとうございます。
おっしゃるとおり単純なスペルミスでした。
すみません。
変数の宣言を強制するオプションをチェックしていた
つもりだったのですが、はずれていました。
お手数かけました。

一応それっぽいことはできたのですが、
まだいろいろと改善しなければならないようです。
たとえば、
WaitForSingleObjectの第二引数にINFINITEを指定してしまうと、
コードを実行後、プログラムが無反応になります。
監視しているディレクトリに変化を起こせば解除されますが、
ちょっと不便です。


へむりん  2006-02-04 06:09:31  No: 130086

obj_a = 0
do
if obj_a = 0 then 
    obj_a = FindFirstChangeNotification
else 
    FindNextChangeNotification(obj_a)
end if

If obj_a = INVALID_HANDLE_VALUE Then
end if

ret = WaitForSingleObject obj_a, 100
if ret = WAIT_OBJECT_0 then exit do

DoEvents
loop

手打ち & コピペでエラーチェックもしていませんが
こんな感じでは?
変更されたファイル名はRaiseEventで取得できるようですが
未確認です。

Googleで検索してみてください。
キーワード
「RaiseEvent FindFirstChangeNotification」等


ガッ  2006-02-04 07:53:11  No: 130087

うーん…
リアルタイムにしようとすれば、
VB側でウィンドウプロシージャを扱える状態にして、
その上でCか何かでDLLを作ってその中で
FindFirstChangeNotification()とWaitForSingleObject()を使って監視する無限ループを持つスレッドを作ってやって、
変化があったらVB側のウィンドウにウィンドウメッセージを投げて、
VB側で受け取って…
という具合になるかな。

そこまでするのが面倒なら…他に手がありそうだけど、思いつかない(orz


ガッ  2006-02-04 08:09:47  No: 130088

何かぁゃιぃのを見つけてきましたよ…:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/APISP/html/sp_shlobj_pcjr.asp


ガッ  2006-02-04 08:14:37  No: 130089

何か問題があるようです@SHChangeNotifyRegister:
http://tinyurl.com/8jlf8

やっぱり別スレッドで監視し続けるのが一番いいのかな…?


K.J.K.  2006-02-04 19:02:21  No: 130090

OSがNT系ならReadDirectoryChangesWの方が楽でしょう。

SHChangeNotifyRegisterはメッセージベースで扱いが楽ですが、
結局のところSHChangeNotifyに対する反応+α程度でしかありません。
また、95系とNT系では使う手順が異なりますし。
これは、そもそもはIShellChangeNotify::OnChangeを呼び出すため
の補助、として作られた、という経緯もあります。
# ですから、既存のシェル機能と連携して使う分にはむしろ有効。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加