Асинхронное ожидание множества событий файловых операций (пример для класса от The Trick) - VB

Узнай цену своей работы

Формулировка задачи:

Написал пример для использования vbWaitForMultipleObjects для мониторинга множества папок с помощью этого класса. Видимо неправильно передаю указатель на массив хендлов событий, ибо начинает сыпатся куча уведомлений с пустым именем файлов. The Trick, можешь, пожалуйста, как освободишься, посмотреть? И еще, как в Callback процедуре получить код возврата WaitForMultipleObjects, чтобы узнать от которого из событий пришло уведомление?
Листинг программы
  1. Option Explicit
  2. Private Const MAX_PATH As Long = 260&
  3. Private Type OVERLAPPED
  4. Internal As Long
  5. InternalHigh As Long
  6. offset As Long
  7. OffsetHigh As Long
  8. hEvent As Long
  9. End Type
  10. Private Type FILE_NOTIFY_INFORMATION
  11. dwNextEntryOffset As Long
  12. dwAction As Long
  13. dwFileNameLength As Long
  14. wcFileName(MAX_PATH * 2) As Byte
  15. End Type
  16. Private Type bufEvents_TYPE
  17. bufEvent() As Long ' Буфер уведомлений для мониторинга файлов
  18. End Type
  19. Private Declare Function ReadDirectoryChanges Lib "kernel32.dll" Alias "ReadDirectoryChangesW" (ByVal hDirectory As Long, lpBuffer As Any, ByVal nBufferLength As Long, ByVal bWatchSubTree As Long, ByVal dwNotifyFilter As Long, ByVal lpBytesReturned As Long, lpOverlapped As Any, ByVal lpCompletionRoutine As Long) As Long
  20. Private Declare Function CreateFile Lib "kernel32.dll" Alias "CreateFileW" (ByVal lpFileName As Long, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, ByRef lpSecurityAttributes As Any, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
  21. Private Declare Function CancelIo Lib "kernel32" (ByVal hFile As Long) As Long
  22. Private Declare Function CreateEvent Lib "kernel32" Alias "CreateEventW" (ByVal lpEventAttributes As Long, ByVal bManualReset As Long, ByVal bInitialState As Long, ByVal lpName As Long) As Long
  23. Private Declare Function ResetEvent Lib "kernel32" (ByVal hEvent As Long) As Long
  24. Private Declare Function memcpy Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long) As Long
  25. Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
  26. Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
  27. Private Declare Function ArrPtr Lib "msvbvm60" Alias "VarPtr" (pArr() As Any) As Long
  28. Private Const SYNCHRONIZE As Long = &H100000
  29. Private Const INFINITE As Long = -1
  30. Private Const WAIT_OBJECT_0 As Long = 0
  31. Private Const FILE_LIST_DIRECTORY As Long = &H1
  32. Private Const FILE_SHARE_DELETE As Long = &H4
  33. Private Const FILE_SHARE_READ As Long = &H1
  34. Private Const FILE_SHARE_WRITE As Long = &H2
  35. Private Const FILE_FLAG_BACKUP_SEMANTICS As Long = &H2000000
  36. Private Const FILE_FLAG_OVERLAPPED As Long = &H40000000
  37. Private Const OPEN_EXISTING As Long = &H3
  38. Private Const INVALID_HANDLE_VALUE As Long = -1
  39. Private Const FILE_NOTIFY_CHANGE_FILE_NAME As Long = 1
  40. Private Const FILE_ACTION_ADDED As Long = &H1
  41. Private Const FILE_ACTION_REMOVED As Long = &H2
  42. Private Const FILE_ACTION_RENAMED_OLD_NAME As Long = &H4
  43. Private Const FILE_ACTION_RENAMED_NEW_NAME As Long = &H5
  44. Dim WithEvents mon As clsTrickWait ' Объект событий мониторинга файлов
  45. Dim hDirectory() As Long ' Описатель мониторящейся директории
  46. Dim hEvent() As Long ' Описатель события для мониторящейся директории
  47. Dim EventIDs As Long ' Кол-во успешно созданных событий
  48. Dim ovr() As OVERLAPPED
  49. Dim Events() As bufEvents_TYPE
  50. Private Sub Command1_Click()
  51. Unload Me
  52. End Sub
  53. Private Sub Form_Load()
  54. Dim Folders(0) As String
  55. Folders(0) = "c:\rsit"
  56. Folders(1) = "c:\windows"
  57. BeginMonitor Folders()
  58. End Sub
  59. Private Sub BeginMonitor(Folders() As String)
  60. Dim i As Long
  61. Dim FCnt As Long
  62. ' Создаем объект для уведомлений
  63. Set mon = New clsTrickWait
  64. FCnt = UBound(Folders)
  65. ReDim hDirectory(FCnt)
  66. ReDim hEvent(FCnt)
  67. ReDim Events(FCnt)
  68. ReDim ovr(FCnt)
  69. For i = 0 To FCnt
  70. ' Открываем директорию для мониторинга
  71. hDirectory(EventIDs) = CreateFile(StrPtr(Folders(i)), FILE_LIST_DIRECTORY, FILE_SHARE_READ Or FILE_SHARE_WRITE Or FILE_SHARE_DELETE, _
  72. ByVal 0&, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS Or FILE_FLAG_OVERLAPPED, 0)
  73. ' При ошибке уведомляем и выходим
  74. If hDirectory(EventIDs) = INVALID_HANDLE_VALUE Then
  75. Debug.Print "Cannot get a handle of directory: " & Folders(i)
  76. Else
  77. ' Создаем событие для уведомления
  78. hEvent(EventIDs) = CreateEvent(0, True, True, 0)
  79. ' При ошибке уведомляем и выходим
  80. If hEvent(EventIDs) = 0 Then
  81. CloseHandle hDirectory(EventIDs): hDirectory(EventIDs) = 0
  82. Debug.Print "Error create notify event for: " & Folders(i)
  83. Else
  84. ' Заполняем структуру OVERLAPPED для асинхронного вызова
  85. ovr(EventIDs).hEvent = hEvent(EventIDs)
  86. ReDim Events(EventIDs).bufEvent(16383) As Long
  87. ' начинаем мониторить в асинхронном режиме
  88. If ReadDirectoryChanges(hDirectory(EventIDs), Events(EventIDs).bufEvent(0), UBound(Events(i).bufEvent) + 1, False, FILE_NOTIFY_CHANGE_FILE_NAME, 0&, ovr(EventIDs), 0&) = 0 Then
  89. ' При ошибке уведомляем и выходим
  90. Debug.Print "Error start monitor of: " & Folders(i)
  91. CloseHandle hEvent(EventIDs): hEvent(EventIDs) = 0
  92. CloseHandle hDirectory(EventIDs): hDirectory(EventIDs) = 0
  93. Else
  94. ' увеличиваем счетчик успешно созданных событий
  95. EventIDs = EventIDs + 1
  96. End If
  97. End If
  98. End If
  99. Next
  100. ' урезаем массив событий до кол-ва успешно созданных
  101. ReDim Preserve hEvent(EventIDs - 1)
  102. ' Если хоть одно событие было успешно создано
  103. If hEvent(0) Then
  104. ' Запускаем асинхронное уведомление для всех созданных событий
  105. mon.vbWaitForMultipleObjects UBound(hEvent) + 1, ArrPtr(hEvent), 0&, INFINITE
  106. End If
  107. End Sub
  108. ' // Событие возникает при изменениях в директории на которые мы подписаны
  109. Private Sub mon_OnWait(ByVal Handle As Long, ByVal Result As Long)
  110. Dim notify As FILE_NOTIFY_INFORMATION
  111. Dim OBJ_idx As Long
  112. Dim idx As Long
  113. Dim name As String
  114. OBJ_idx = WAIT_OBJECT_0 'Result ' !!! <--- Должен быть индекс отслеживаемого события
  115. ' Проход по буферу уведомлений
  116. Do
  117. ' Копируем во временную структуру уведомление
  118. ' Здесь правильнее сделать через указатели, но для примера я оставил так (более понятно)
  119. memcpy notify, Events(OBJ_idx).bufEvent(idx), Len(notify)
  120. ' Узнаем имя файла
  121. name = Chr$(34) & Left$(notify.wcFileName, notify.dwFileNameLength \ 2) & Chr$(34)
  122. ' Проверяем тип уведомления
  123. Select Case notify.dwAction
  124. Case FILE_ACTION_ADDED: name = "ADDED: " & name ' Файл добавлен
  125. Case FILE_ACTION_REMOVED: name = "REMOVED: " & name ' Файл удален
  126. Case FILE_ACTION_RENAMED_OLD_NAME: name = "RENAMED (old name): " & name ' Файл переименован - это старое имя
  127. Case FILE_ACTION_RENAMED_NEW_NAME: name = "RENAMED (new name): " & name ' Файл переименован - это новое имя
  128. End Select
  129. ' Добавить в список
  130. Debug.Print name
  131. ' Переход к следующему уведомлению
  132. idx = idx + notify.dwNextEntryOffset
  133. ' Пока есть уведомления в буфере повторяем
  134. Loop While notify.dwNextEntryOffset
  135. ' Сбрасываем событие
  136. ResetEvent Handle
  137. ' Заполняем структуру OVERLAPPED
  138. ovr(OBJ_idx).hEvent = Handle
  139. ' Запускаем мониторинг
  140. Call ReadDirectoryChanges(hDirectory(OBJ_idx), Events(OBJ_idx).bufEvent(0), UBound(Events(OBJ_idx).bufEvent) + 1, False, FILE_NOTIFY_CHANGE_FILE_NAME, 0&, ovr(OBJ_idx), 0&)
  141. ' Снимаем старое уведомление
  142. mon.Abort
  143. ' Запускаем новое
  144. ' !!! - handle ???
  145. mon.vbWaitForMultipleObjects UBound(hEvent) + 1, Handle, 0&, INFINITE
  146. End Sub
  147. Public Sub EndMonitoring()
  148. Dim i As Long
  149. ' Проверяем если директория открыта, то значит останавливаем
  150. If hDirectory(0) Then
  151. ' Завершаем ожидание
  152. mon.Abort
  153. ' Закрываем описатели события и директории
  154. For i = 0 To EventIDs - 1
  155. CloseHandle hEvent(i): hEvent(i) = 0
  156. CloseHandle hDirectory(i): hDirectory(i) = 0
  157. Next
  158. End If
  159. End Sub
  160. Private Sub Form_Unload(Cancel As Integer)
  161. Dim i As Long
  162. ' Прервывание ожиданий
  163. mon.Abort
  164. ' Если мониторинг
  165. If hDirectory(0) Then
  166. For i = 0 To EventIDs - 1
  167. ' Остановка ожидания мониторинга
  168. CancelIo hDirectory(i)
  169. ' Закрываем описатели
  170. CloseHandle hDirectory(i)
  171. CloseHandle hEvent(i)
  172. Next
  173. End If
  174. End Sub

Решение задачи: «Асинхронное ожидание множества событий файловых операций (пример для класса от The Trick)»

textual
Листинг программы
  1. ' // Ñîáûòèå âîçГ*ГЁГЄГ*ГҐГІ ïðè èçìåГ*ГҐГ*ГЁГїГµ Гў äèðåêòîðèè Г*Г* êîòîðûå ìû ïîäïèñГ*Г*Г»
  2. Private Sub mon_OnWait(ByVal Handle As Long, ByVal Result As Long)
  3.     Dim notify  As FILE_NOTIFY_INFORMATION
  4.     Dim OBJ_idx As Long
  5.     Dim idx     As Long
  6.     Dim name    As String
  7.    
  8.     OBJ_idx = Result ' !!! <--- ÄîëæåГ* áûòü ГЁГ*äåêñ îòñëåæèâГ*åìîãî ñîáûòèÿ
  9.    
  10.     If OBJ_idx >= 0 Then
  11.         ' Ïðîõîä ГЇГ® áóôåðó óâåäîìëåГ*ГЁГ©
  12.        Do
  13.             ' Êîïèðóåì ГўГ® âðåìåГ*Г*ГіГѕ ñòðóêòóðó óâåäîìëåГ*ГЁГҐ
  14.            ' Çäåñü ГЇГ°Г*âèëüГ*ГҐГҐ ñäåëГ*ГІГј ÷åðåç ГіГЄГ*Г§Г*òåëè, Г*Г® äëÿ ïðèìåðГ* Гї îñòГ*ГўГЁГ« ГІГ*ГЄ (áîëåå ГЇГ®Г*ГїГІГ*Г®)
  15.            memcpy notify, Events(OBJ_idx).bufEvent(idx), Len(notify)
  16.             ' ÓçГ*Г*ГҐГ¬ èìÿ ГґГ*éëГ*
  17.            name = Chr$(34) & Left$(notify.wcFileName, notify.dwFileNameLength \ 2) & Chr$(34)
  18.             ' Ïðîâåðÿåì ГІГЁГЇ óâåäîìëåГ*ГЁГї
  19.            Select Case notify.dwAction
  20.             Case FILE_ACTION_ADDED:             name = "ADDED: " & name                 ' Г”Г*éë äîáГ*âëåГ*
  21.            Case FILE_ACTION_REMOVED:           name = "REMOVED: " & name               ' Г”Г*éë ГіГ¤Г*ëåГ*
  22.            Case FILE_ACTION_RENAMED_OLD_NAME:  name = "RENAMED (old name): " & name    ' Г”Г*éë ïåðåèìåГ*îâГ*Г* - ГЅГІГ® Г±ГІГ*ðîå èìÿ
  23.            Case FILE_ACTION_RENAMED_NEW_NAME:  name = "RENAMED (new name): " & name    ' Г”Г*éë ïåðåèìåГ*îâГ*Г* - ГЅГІГ® Г*îâîå èìÿ
  24.            End Select
  25.             ' ÄîáГ*ГўГЁГІГј Гў ñïèñîê
  26.            Debug.Print name
  27.             ' Ïåðåõîä ГЄ ñëåäóþùåìó óâåäîìëåГ*ГЁГѕ
  28.            idx = idx + notify.dwNextEntryOffset
  29.             ' ÏîêГ* ГҐГ±ГІГј óâåäîìëåГ*ГЁГї Гў áóôåðå ïîâòîðÿåì
  30.        Loop While notify.dwNextEntryOffset
  31.         ' ÑáðГ*ñûâГ*ГҐГ¬ ñîáûòèå
  32.        ResetEvent hEvent(OBJ_idx)
  33.         ' Г‡Г*ïîëГ*ГїГҐГ¬ ñòðóêòóðó OVERLAPPED
  34.        ovr(OBJ_idx).hEvent = hEvent(OBJ_idx)
  35.         ' Г‡Г*ГЇГіГ±ГЄГ*ГҐГ¬ ìîГ*èòîðèГ*ГЈ
  36.        Call ReadDirectoryChanges(hDirectory(OBJ_idx), Events(OBJ_idx).bufEvent(0), UBound(Events(OBJ_idx).bufEvent) + 1, False, FILE_NOTIFY_CHANGE_FILE_NAME, 0&, ovr(OBJ_idx), 0&)
  37.     End If
  38.     ' Г‘Г*ГЁГ¬Г*ГҐГ¬ Г±ГІГ*ðîå óâåäîìëåГ*ГЁГҐ
  39.    mon.Abort
  40.     ' Г‡Г*ГЇГіГ±ГЄГ*ГҐГ¬ Г*îâîå
  41.    ' !!! - handle ???
  42.    mon.vbWaitForMultipleObjects UBound(hEvent) + 1, VarPtr(hEvent(0)), 0&, INFINITE
  43. End Sub

ИИ поможет Вам:


  • решить любую задачу по программированию
  • объяснить код
  • расставить комментарии в коде
  • и т.д
Попробуйте бесплатно

Оцени полезность:

11   голосов , оценка 4.182 из 5

Нужна аналогичная работа?

Оформи быстрый заказ и узнай стоимость

Бесплатно
Оформите заказ и авторы начнут откликаться уже через 10 минут
Похожие ответы