I'm trying to monitor a folder for file changes using ReadDirectoryChangesW.
The problem with the code below is that TestFunc never gets called.
Does anyone know how to use ReadDirectoryChangesW asynchonously with a
completion routine?
Thanks
VOID WINAPI TestFunc(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered,
LPOVERLAPPED lpOverlapped)
{
...
}
void Test()
{
...
hDir = CreateFile(m_sPath, FILE_LIST_DIRECTORY, FILE_SHARE_READ |
FILE_SHARE_DELETE,
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS |
FILE_FLAG_OVERLAPPED, NULL);
DWORD dwNotifyFilter = FILE_NOTIFY_CHANGE_FILE_NAME |
FILE_NOTIFY_CHANGE_LAST_WRITE;
ReadDirectoryChangesW(hDir, chBuf, DIR_BUF_SIZE, TRUE, dwNotifyFilter,
&dwBytesRet,
&ol, (LPOVERLAPPED_COMPLETION_ROUTINE)(&TestFunc));
...
}
Please don't multipost your messages. Especially not to VB groups when
you write your code in C++.
Matt
============================================
Mattias Sjögren - mattiass @ hem.passagen.se
CodeHound - Free VB Search Engine
http://www.codehound.com
Here is how I am trying to use it:
Private mlDirectoryHandle As Long
Private mlNotifyInfo As FILE_NOTIFY_INFORMATION
Private mlBytesReturned As Long
Private mlOverlapped As OVERLAPPED
Public Function BeginWatchDirectory(ByVal sPath As String) As Boolean
Dim lHandle As Long
Dim lRet As Long
mlDirectoryHandle = CreateFile(sPath, FILE_LIST_DIRECTORY,
FILE_SHARE_READ Or FILE_SHARE_DELETE, 0, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS Or FILE_FLAG_OVERLAPPED, 0)
If Not mlDirectoryHandle = INVALID_HANDLE_VALUE Then
lRet = ReadDirectoryChangesW(mlDirectoryHandle, mlNotifyInfo,
Len(mlNotifyInfo), True, FILE_NOTIFY_CHANGE_FILE_NAME, mlBytesReturned,
mlOverlapped, AddressOf ChangeRoutine)
Debug.Print "ReadDirectoryChangesW returned " & lRet & "
LastDllError: " & Err.LastDllError
End If
End Function
Private Sub ChangeRoutine()
Debug.Print "Completion routine called"
End Sub
Can anyone tell me where I went wrong? Thanks.
Mattias Sjögren <mattiass.do...@hem.passagen.se> wrote in message
news:39f70fea...@msnews.microsoft.com...
According to the MSDN, the declaration for the I/O completion routine
requires accepting three parameters :
dwErrorCode (Long)
dwNumberOfBytesTransfered (Long)
lpOverlapped (OVERLAPPED struct if used, or a Long if not)
Me thinks too it should be Public, in a BAS module.
Public Sub ChangeRoutine (dwErrorCode As Long, _
dwNumberOfBytesTransfered As Long,
_
lpOverlapped As XXXXX )
For initial testing, you can change all overlapped use to nulls (0&),
declare the member in the API As Long (instead of OVERLAPPED), and use As
Long in the completion routine's last param.
--
Randy Birch
MVP Visual Basic
Take the vb.net poll at:
http://www.mvps.org/vbnet/
http://www.mvps.org/ccrp/
"Charles Herrington" <chu...@pandavoice.com> wrote in message
news:uhRTtIIZAHA.1260@tkmsftngp05...
: Has anyone successfully used ReadDirectoryChangesW in VB? I am attempting
: Mattias Sjvgren <mattiass.do...@hem.passagen.se> wrote in message
: news:39f70fea...@msnews.microsoft.com...
: >
: > Please don't multipost your messages. Especially not to VB groups when
: > you write your code in C++.
: >
: >
: > Matt
: >
: > ============================================
: > Mattias Sjvgren - mattiass @ hem.passagen.se
:
:
Private Declare Function ReadDirectoryChangesW Lib "kernel32" (ByVal hHandle
As Long, lpBuffer As FILE_NOTIFY_INFORMATION, nBufferLength As Long,
bWatchSubTree As Long, dwNotifyFilter As Long, lpBytesReturned As Long,
ByRef lpOverlapped As Long, lpCompletionRoutine As Long) As Long
Public Function BeginWatchDirectory(ByVal sPath As String) As Boolean
Dim lHandle As Long
Dim lRet As Long
'Get a handle to the directory
mlDirectoryHandle = CreateFile(sPath, FILE_LIST_DIRECTORY, _
FILE_SHARE_READ Or FILE_SHARE_DELETE, 0, OPEN_EXISTING, _
FILE_FLAG_BACKUP_SEMANTICS Or FILE_FLAG_OVERLAPPED, 0)
'Check to see if the handle is valid
If Not mlDirectoryHandle = INVALID_HANDLE_VALUE Then
'Request a change notification
lRet = ReadDirectoryChangesW(mlDirectoryHandle, mlNotifyInfo, _
Len(mlNotifyInfo), True, FILE_NOTIFY_CHANGE_FILE_NAME, _
mlBytesReturned, 0&, AddressOf ChangeRoutine)
'Print the results of the request to the debug window
Debug.Print "ReadDirectoryChangesW returned " & lRet & _
" LastDllError: " & Err.LastDllError
'If the request was not successfull, close the handle to the
directory
If lRet = 0 Then CloseHandle mlDirectoryHandle
End If
End Function
Public Sub ChangeRoutine(dwErrorCode As Long, _
dwNumberOfBytesTransfered As Long, lpOverlapped As Long)
Debug.Print "Completion routine called"
End Sub
I would greatly appreciate any help anyone can give.
Randy Birch <r...@mvps.org> wrote in message
news:#lih0aJZAHA.712@tkmsftngp04...
Post your declares and constants as well, please.
--
Randy Birch
MVP Visual Basic
Take the vb.net poll at:
http://www.mvps.org/vbnet/
http://www.mvps.org/ccrp/
"Charles Herrington" <chu...@pandavoice.com> wrote in message
news:OZ22bcSZAHA.448@tkmsftngp02...
: I made the changes you suggested and I am still getting the same error
: > :
: >
: >
:
:
Option Explicit
Public Type FILE_NOTIFY_INFORMATION
NextEntryOffset As Long
Action As Long
FileNameLength As Long
FileName As String
End Type
Public Type OVERLAPPED
Internal As Long
InternalHigh As Long
offset As Long
OffsetHigh As Long
hEvent As Long
End Type
Private Declare Function ReadDirectoryChangesW Lib "kernel32" (ByVal hHandle
As Long, lpBuffer As FILE_NOTIFY_INFORMATION, nBufferLength As Long,
bWatchSubTree As Long, dwNotifyFilter As Long, lpBytesReturned As Long,
ByRef lpOverlapped As Long, lpCompletionRoutine As Long) As Long
Private Declare Function CreateFile Lib "kernel32" Alias "CreateFileA"
(ByVal lpFileName As String, ByVal dwDesiredAccess As Long, ByVal
dwShareMode As Long, ByVal lpSecurityAttributes As Long, ByVal
dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal
hTemplateFile As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long)
As Long
Private Const FILE_NOTIFY_CHANGE_FILE_NAME = &H1
Private Const FILE_NOTIFY_CHANGE_DIR_NAME = &H2
Private Const FILE_NOTIFY_CHANGE_ATTRIBUTES = &H4
Private Const FILE_NOTIFY_CHANGE_SIZE = &H8
Private Const FILE_NOTIFY_CHANGE_LAST_WRITE = &H10
Private Const FILE_NOTIFY_CHANGE_SECURITY = &H100
Private Const INVALID_HANDLE_VALUE = -1
Private Const FILE_LIST_DIRECTORY = (&H1)
Private Const FILE_SHARE_READ = &H1
Private Const FILE_SHARE_DELETE = &H4
Private Const OPEN_EXISTING = 3
Private Const FILE_FLAG_BACKUP_SEMANTICS = &H2000000
Private Const FILE_FLAG_OVERLAPPED = &H40000000
Private mlDirectoryHandle As Long
Public glNotifyInfo As FILE_NOTIFY_INFORMATION
Public glBytesReturned As Long
Private mlOverlapped As OVERLAPPED
Public Function BeginWatchDirectory(ByVal sPath As String) As Boolean
Dim lHandle As Long
Dim lRet As Long
'Get a handle to the directory
mlDirectoryHandle = CreateFile(sPath, FILE_LIST_DIRECTORY, _
FILE_SHARE_READ Or FILE_SHARE_DELETE, 0, OPEN_EXISTING, _
FILE_FLAG_BACKUP_SEMANTICS Or FILE_FLAG_OVERLAPPED, 0)
'Check to see if the handle is valid
If Not mlDirectoryHandle = INVALID_HANDLE_VALUE Then
'Request a change notification
lRet = ReadDirectoryChangesW(mlDirectoryHandle, glNotifyInfo, _
Len(glNotifyInfo), True, FILE_NOTIFY_CHANGE_FILE_NAME, _
glBytesReturned, 0&, AddressOf ChangeRoutine)
'Print the results of the request to the debug window
Debug.Print "ReadDirectoryChangesW returned " & lRet & _
" LastDllError: " & Err.LastDllError
'If the request was not successfull, close the handle to the
directory
If lRet = 0 Then CloseHandle mlDirectoryHandle
End If
End Function
Public Sub ChangeRoutine(ByVal dwErrorCode As Long, _
ByVal dwNumberOfBytesTransfered As Long, ByVal lpOverlapped As Long)
Debug.Print "Completion routine called"
End Sub
Randy Birch <r...@mvps.org> wrote in message
news:OO2PtBXZAHA.2216@tkmsftngp02...
I did some additional reading in the msdn, and the completion routine will
not fire unless a "waitable state" api has been called (see comment below ).
Since this is synchronous, not asynchronous, this code simply monitors the
folder and finishes on the first change. But it will show you how to do some
of the necessary footwork. Paste the code below into a new project .. there
are some significant changes from your declares.
--
Randy Birch
MVP Visual Basic
Take the vb.net poll at:
http://www.mvps.org/vbnet/
http://www.mvps.org/ccrp/
'-----------------
The FileIOCompletionRoutine function is an application-defined callback
function used with the ReadFileEx and WriteFileEx functions. It is called
when the asynchronous input and output (I/O) operation is completed or
canceled and the calling thread is in an alertable state (using the SleepEx,
MsgWaitForMultipleObjectsEx, WaitForSingleObjectEx, or
WaitForMultipleObjectsEx function with the fAlertable flag set to TRUE).
'-----------------
Option Explicit
Public hFile As Long
Public Declare Function ReadDirectoryChangesW Lib "kernel32" _
(ByVal hHandle As Long, _
ByVal lpBuffer As Long, _
ByVal nBufferLength As Long, _
ByVal bWatchSubTree As Long, _
ByVal dwNotifyFilter As Long, _
lpBytesReturned As Long, _
ByVal lpOverlapped As Long, _
ByVal lpCompletionRoutine As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32" _
Alias "RtlMoveMemory" _
(Dest As Any, _
Source As Any, _
ByVal nbytes As Long)
Private Declare Function lstrlenW Lib "kernel32" _
(ByVal lpString As Long) As Long
Private Declare Function StrLen Lib "kernel32" _
Alias "lstrlenW" _
(ByVal lpString As Long) As Long
Public Declare Function GlobalAlloc Lib "kernel32" _
(ByVal wFlags As Long, ByVal dwBytes As Long) As Long
Public Declare Function GlobalFree Lib "kernel32" _
(ByVal hMem As Long) As Long
Public Const GMEM_FIXED = &H0
Public Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" _
(ByVal lpFileName As String, _
ByVal dwDesiredAccess As Long, _
ByVal dwShareMode As Long, _
ByVal lpSecurityAttributes As Long, _
ByVal dwCreationDisposition As Long, _
ByVal dwFlagsAndAttributes As Long, _
ByVal hTemplateFile As Long) As Long
Public Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long)
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
Public Const FILE_LIST_DIRECTORY = (&H1)
Public Const FILE_SHARE_READ = &H1
Public Const FILE_SHARE_WRITE = &H2
Public Const FILE_SHARE_DELETE = &H4
Public Const OPEN_EXISTING = 3
Public Const FILE_FLAG_BACKUP_SEMANTICS = &H2000000
Public Const FILE_FLAG_OVERLAPPED = &H40000000
Public Function BeginWatchDirectory(ByVal sPath As String) As Long
Dim dynBuffer As Long
Dim dynBufferSize As Long
Dim lpBytesReturned As Long
Dim c As Long
'Get a handle to the directory
hFile = CreateFile(sPath, _
FILE_LIST_DIRECTORY, _
FILE_SHARE_READ Or _
FILE_SHARE_WRITE Or _
FILE_SHARE_DELETE, _
0&, _
OPEN_EXISTING, _
FILE_FLAG_BACKUP_SEMANTICS, _
0&)
'Check to see if the handle is valid
If Not hFile = INVALID_HANDLE_VALUE Then
'set a size for the data returned
'and allocate memory for it
dynBufferSize = 4096
dynBuffer = GlobalAlloc(GMEM_FIXED, dynBufferSize)
'pass this to the API. The app now waits for
'a change (is unresponsive)
Call ReadDirectoryChangesW(hFile, _
dynBuffer, _
dynBufferSize, _
0&, _
FILE_NOTIFY_CHANGE_FILE_NAME, _
lpBytesReturned, _
0&, _
0&) 'AddressOf ChangeRoutine)
'Print results to the debug window
Debug.Print "RDC LastDllError " & _
Err.LastDllError & _
" " & Err.Description
Debug.Print "RDC lpBytesReturned " & lpBytesReturned
'if lpReturned = 0, then the buffer was too small.
'if its > 0, there is data. The call returns
'two sets of data on a file rename- the
'original filename and the new filename.
'The loop below takes care of extracting
'additional data if NextOffset indicates
'there is more.
If lpBytesReturned > 0 Then
'some working vars
Dim ptrNext As Long
Dim ptrNextOffset As Long
Dim ptrAction As Long
Dim ptrFileLen As Long
Dim ptrFileName As String
Dim cnt As Long
'since ptrNextOffset in the Do Loop
'points to the *next* block of data
'following the one being processed,
'we need a way to remember where the
'current offest was, in order to use
'Copymem. ptrNext fills this need.
ptrNext = 0
Do
cnt = cnt + 1
Debug.Print "Pass " & cnt
'copy the values from the buffer
'into longs. Since the data was
'returned smooshed into a single
'Long, we offset each extracted
'Long var by 4 bytes.
CopyMemory ptrNextOffset, ByVal dynBuffer + ptrNext, 4
CopyMemory ptrAction, ByVal dynBuffer + ptrNext + 4, 4
CopyMemory ptrFileLen, ByVal dynBuffer + ptrNext + 8, 4
Debug.Print " ptrNextOffset " & ptrNextOffset
Debug.Print " ptrAction " & ptrAction
Debug.Print " ptrFileLen " & ptrFileLen
'now copy the string
Debug.Print " ptrToByteString " & _
GetPointerToByteStringW(dynBuffer + ptrNext + 12)
ptrNext = ptrNextOffset
Loop Until ptrNextOffset = 0
End If
'free the memory and close the file
Call GlobalFree(dynBuffer)
If hFile <> 0 Then CloseHandle hFile
End If
End Function
Private Function GetPointerToByteStringW(lpString As Long) As String
Dim buff() As Byte
Dim nsize As Long
If lpString Then
'its Unicode, so mult. by 2
nsize = lstrlenW(lpString) * 2
If nsize Then
ReDim buff(0 To (nsize - 1)) As Byte
CopyMemory buff(0), ByVal lpString, nsize
GetPointerToByteStringW = buff
End If
End If
End Function
Public Function ChangeRoutine(ByVal dwErrorCode As Long, _
ByVal dwBytesTransfered As Long, _
ByVal lpOverlapped As Long) As Long
'actually not called !
Debug.Print "Completion routine called: " & _
"dwErrorCode" = dwErrorCode & vbNewLine & _
"dwBytesTransfered" = dwBytesTransfered & vbNewLine & _
"lpOverlapped" = lpOverlapped
End Function