I'm making a watch folder class, but at the moment I'm unable to figure out what would cause it to give the error #1, INVALID_FUNCTION. I'm trying to make an asynchronous call by giving the CompletionRoutine, but I just don't see what I'm doing against what is said over at MSDN.
I know there are other VB6 codes that do work, but they always use events or synchronous processing, which means they're looping a tight loop with DoEvents to see if something has happened or not. This is not something I want. The callback code is tested and works with SelfTimer for an example (known as SelfCallback) - I did it like this to only have this one class and atleast some degree of IDE safety (but I'm pretty sure this is not enough).
Some test code that I have put together, which just triggers the error from the Watch function:
Code:
Option Explicit
Private WithEvents Folder As WatchFolder
Private Sub Folder_Change(ByVal Filename As String, ByVal Action As WatchFileAction)
Debug.Print Filename
End Sub
Private Sub Folder_Error(ByVal ErrorCode As Long)
Debug.Print "Error #" & Hex$(ErrorCode)
End Sub
Private Sub Form_Load()
Set Folder = New WatchFolder
Folder.Watch "C:\Documents and Settings\"
End Sub
Private Sub Form_Unload(Cancel As Integer)
Set Folder = Nothing
End Sub
I can't answer your direct question, but I wanted to suggest a slick API called FindFirstChangeNotification that deals with watching directories, just as you would want to with your custom class. Windows itself is doing the watching and polling, so your program can be doing other things.
Private Const FILE_NOTIFY_CHANGE_ATTRIBUTES = &H4
Private Const FILE_NOTIFY_CHANGE_DIR_NAME = &H2
Private Const FILE_NOTIFY_CHANGE_FILE_NAME = &H1
Private Const FILE_NOTIFY_CHANGE_SIZE = &H8
Private Const FILE_NOTIFY_CHANGE_LAST_WRITE = &H10
Private Const FILE_NOTIFY_CHANGE_SECURITY = &H100
Private Const FILE_NOTIFY_CHANGE_ALL = &H4 Or &H2 Or &H1 Or &H8 Or &H10 Or &H100
Private Declare Function FindFirstChangeNotification Lib "kernel32" Alias "FindFirstChangeNotificationA" (ByVal lpPathName As String, ByVal bWatchSubtree As Long, ByVal dwNotifyFilter As Long) As Long
Private Declare Function FindCloseChangeNotification Lib "kernel32" (ByVal hChangeHandle As Long) As Long
Private Declare Function FindNextChangeNotification Lib "kernel32" (ByVal hChangeHandle As Long) As Long
Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
Private Declare Function ResetEvent Lib "kernel32" (ByVal hEvent As Long) As Long
Private Sub Form_Load()
'KPD-Team 2000
'URL: http://www.allapi.net/
'E-Mail: [email protected]
Dim Ret As Long
'Set the notification hook
Ret = FindFirstChangeNotification("C:\", &HFFFFFFFF, FILE_NOTIFY_CHANGE_ALL)
'Wait until the event is triggered
WaitForSingleObject Ret, &HFFFFFFFF
MsgBox "Event Triggered for the first time"
'Reactivate our hook
FindNextChangeNotification Ret
'Wait until the event is triggered
WaitForSingleObject Ret, &HFFFFFFFF
MsgBox "Event Triggered for the second time"
'Remove our hook
FindCloseChangeNotification Ret
End Sub
Unfortunately both of those examples rely on continuous looping (synchronous processing) instead of calling a procedure (for asynchronous event style processing).
My plan B at the moment would be to simply expand the SelfTimer class and do manual polling with a timer, thus avoiding a loop. It wouldn't be as realtime though, I'd like to have the class to get an immediate information of changes: makes it less likely that another process starts doing something else while doing your own processing, such as reading changes in the file. Using too high of a frequency with the timer would make it too performance hungry.
I originally thought that you were trying to avoid polling the directory (drives) in a loop, which the FindFirstChangeNotification would cure. You are right, of course; even though polling the dirctory would no longer be required when using this API, you would still have to occasionally check the results of the API itself, in some type of loop or timer.
Now that I finally had the time to properly look in to this, I noticed I had missed the text about thread being in an alertable state. This forces to make a new thread for listening the folder changes to keep the main thread responsive. This is getting messier than I'd like it to be... looks like I'll have to take a look into Wokawidget's multithreading code and then simplify it as much as possible if I want immediate interaction when changes occur.
There must be a better way, probably based on RegisterWaitForSingleObject and a callback. I wonder if this would automatically use VB6's internal threadpool implicitly, without requiring any extreme measures to get at multithreading?
Sorry, don't have anything. I think I somewhat figured out what was the problem, but it probably required too much work at the time so I never got my hands dirty enough to get it fixed. I've very much forgotten all the information that would be of value.