Привет всем.
Недавно мне поставили задачу создать файловый монитор наподобие
filemon, но работающий исключительно в UserMode. Пока в голову приходит
только использование возможнойстей shell, но они достаточно ограничены и
не полны. Я слышал, что помимо shell в Винде есть еще два механизма слежения
за файлами и директориями, но не знаю, как их зовут и где искать. Покорнейше прошу,
натолкните на мысль. Куда идти, где копать.
It is always bad to give advices, but you will be never forgiven for a good one.
Oscar Wilde
Здравствуйте, Аноним, Вы писали:
А>Недавно мне поставили задачу создать файловый монитор
Вот писал для себя. Узнавал кто же удаляет мои файлы.
Использую и ReadDirectoryChangesW и FindFirstChangeNotification/FindNextChangeNotification и перехват API и native ф-ций удаления файлов внутри своего просесса.
#include "stdafx.h"
//#define _WIN32_WINNT 0x0500 // must defined in the project
#include <detours.h> // download from http://research.microsoft.com/research/downloads/download.aspx?FUID={10E5D78C-592C-419D-A53E-BAE8DBD81801}&CodeName=Detours&Version=1.5
#pragma comment(lib, "detours")
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
//#define __NT_DDK__ // можно определить, если установлено DDK
#ifdef __NT_DDK__
namespace NT {
extern "C" {
#pragma warning(disable:4005)
#include <ntddk.h> // need DDK
#pragma warning(default:4005)
}
}
using NT::NTSTATUS;
#endif // __NT_DDK__
/*******************************************************************************************/
//
// Вспомогательные ф-ции
//
/*******************************************************************************************/
inline CString Format(LPCTSTR szFormat, ...) {
va_list arglist;
va_start(arglist, szFormat);
CString strResult;
strResult.FormatV(szFormat, arglist);
va_end(arglist);
return strResult;
}
inline CString ErrorCode2Str(DWORD dwErrCode) {
CString strRes;
LPTSTR lpMsgBuf = NULL;
DWORD dwRes = ::FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dwErrCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR)&lpMsgBuf,
0,
NULL
);
if (dwRes != 0) {
strRes = lpMsgBuf;
strRes.Replace(_T("\r\n"), _T(" "));
//strRes.Replace(_T('\r'), _T(' '));
//strRes.Replace(_T('\n'), _T(' '));
strRes.TrimRight();
} else {
DWORD dwErrCode2 = ::GetLastError(); // must be ERROR_MR_MID_NOT_FOUND ???
}
::LocalFree(lpMsgBuf);
return strRes;
}
/*******************************************************************************************/
//
// LOGGER
//
/*******************************************************************************************/
namespace nsLogger {
inline void Put(IN LPCTSTR szStr, ...) {
va_list argList;
va_start(argList, szStr);
CString strRes; strRes.FormatV(szStr, argList);
::OutputDebugString(strRes);
::OutputDebugString(_T("\r\n"));
va_end(argList);
}
inline void PutErrorCode(IN LPCTSTR szStr, IN DWORD dwErrCode) {
CString strErr = ErrorCode2Str(dwErrCode);
nsLogger::Put(_T("%sError code is %i%s"), szStr ? szStr : _T(""), dwErrCode, (LPCTSTR)(strErr.IsEmpty() ? _T("") : (LPCTSTR)(_T(" (")+strErr+_T(")"))));
}
} // namespace nsLogger
#define LOG_PUT(funcName, logLevel) nsLogger::funcName(
/*******************************************************************************************/
//
// Types defenitions
//
/*******************************************************************************************/
#ifndef __NT_DDK__
// описываю сам нужные структры если нет установленного DDK
namespace NT {
typedef LONG NTSTATUS;
#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING;
typedef UNICODE_STRING *PUNICODE_STRING;
typedef struct _OBJECT_ATTRIBUTES {
ULONG Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor; // Points to type SECURITY_DESCRIPTOR
PVOID SecurityQualityOfService; // Points to type SECURITY_QUALITY_OF_SERVICE
} OBJECT_ATTRIBUTES;
typedef OBJECT_ATTRIBUTES *POBJECT_ATTRIBUTES;
typedef struct _IO_STATUS_BLOCK {
union {
NTSTATUS Status;
PVOID Pointer;
};
ULONG_PTR Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
typedef enum _FILE_INFORMATION_CLASS {
// end_wdm
FileDirectoryInformation = 1,
FileFullDirectoryInformation, // 2
FileBothDirectoryInformation, // 3
FileBasicInformation, // 4 wdm
FileStandardInformation, // 5 wdm
FileInternalInformation, // 6
FileEaInformation, // 7
FileAccessInformation, // 8
FileNameInformation, // 9
FileRenameInformation, // 10
FileLinkInformation, // 11
FileNamesInformation, // 12
FileDispositionInformation, // 13
FilePositionInformation, // 14 wdm
FileFullEaInformation, // 15
FileModeInformation, // 16
FileAlignmentInformation, // 17
FileAllInformation, // 18
FileAllocationInformation, // 19
FileEndOfFileInformation, // 20 wdm
FileAlternateNameInformation, // 21
FileStreamInformation, // 22
FilePipeInformation, // 23
FilePipeLocalInformation, // 24
FilePipeRemoteInformation, // 25
FileMailslotQueryInformation, // 26
FileMailslotSetInformation, // 27
FileCompressionInformation, // 28
FileObjectIdInformation, // 29
FileCompletionInformation, // 30
FileMoveClusterInformation, // 31
FileQuotaInformation, // 32
FileReparsePointInformation, // 33
FileNetworkOpenInformation, // 34
FileAttributeTagInformation, // 35
FileTrackingInformation, // 36
FileIdBothDirectoryInformation, // 37
FileIdFullDirectoryInformation, // 38
FileMaximumInformation
// begin_wdm
} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
typedef struct _FILE_DISPOSITION_INFORMATION {
BOOLEAN DeleteFile;
} FILE_DISPOSITION_INFORMATION, *PFILE_DISPOSITION_INFORMATION;
#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L) // The requested operation was unsuccessful.
} // namespace NT
using NT::NTSTATUS;
#endif // __NT_DDK__
/*******************************************************************************************/
//
// Перехваченные ф-ции
//
/*******************************************************************************************/
typedef BOOL (WINAPI* PF_DeleteFileA)(IN LPCSTR lpFileName);
PF_DeleteFileA g_pfDeleteFileA = DeleteFileA;
BOOL WINAPI MyDeleteFileA(IN LPCSTR lpFileName) {
LOG_PUT(Put, LL_DEBUG) _T("> DeleteFileA: Файл [%s]: "), (LPCTSTR)CString(lpFileName));
BOOL bRes = g_pfDeleteFileA(lpFileName);
DWORD dwErrCode = ::GetLastError();
LOG_PUT(Put, LL_DEBUG) _T("< DeleteFileA: return %s: "), bRes ? _T("TRUE") : _T("FALSE"));
::SetLastError(dwErrCode);
return bRes;
}
typedef BOOL (WINAPI* PF_DeleteFileW)(IN LPCWSTR lpFileName);
PF_DeleteFileW g_pfDeleteFileW = DeleteFileW;
BOOL WINAPI MyDeleteFileW(IN LPCWSTR lpFileName) {
LOG_PUT(Put, LL_DEBUG) _T("> DeleteFileW: Файл [%s]: "), (LPCTSTR)CString(lpFileName));
BOOL bRes = g_pfDeleteFileW(lpFileName);
DWORD dwErrCode = ::GetLastError();
LOG_PUT(Put, LL_DEBUG) _T("< DeleteFileW: return %s: "), bRes ? _T("TRUE") : _T("FALSE"));
::SetLastError(dwErrCode);
return bRes;
}
typedef NTSTATUS (NTAPI* PF_ZwDeleteFile)(IN NT::POBJECT_ATTRIBUTES pObjectAttributes);
PF_ZwDeleteFile g_pfZwDeleteFile = NULL;
NTSTATUS NTAPI MyZwDeleteFile(IN NT::POBJECT_ATTRIBUTES pObjectAttributes) {
LOG_PUT(Put, LL_DEBUG) _T("> ZwDeleteFile: "));
CString strFileName;
if (pObjectAttributes && pObjectAttributes->ObjectName) {
DWORD ulLength = pObjectAttributes->ObjectName->Length>>1; // для WCHAR строки длину в байтах преобразую в длину в символах
try {
#ifdef UNICODE
strFileName.Format(_T("%.*s"), ulLength, pObjectAttributes->ObjectName->Buffer);
#else
strFileName.Format(_T("%.*S"), ulLength, pObjectAttributes->ObjectName->Buffer);
#endif
} catch(...) {
LOG_PUT(Put, LL_ERROR) _T(" ZwDeleteFile: Exception при выборке имени файла из структуры OBJECT_ATTRIBUTES. Длина строки %i байт, адрес строки 0x%08X. "), pObjectAttributes->ObjectName->Length, pObjectAttributes->ObjectName->Buffer);
}
}
LOG_PUT(Put, LL_DEBUG) _T(" ZwDeleteFile: Файл [%s]: "), (LPCTSTR)strFileName);
NTSTATUS lRes = g_pfZwDeleteFile ? g_pfZwDeleteFile(pObjectAttributes) : STATUS_UNSUCCESSFUL;
DWORD dwErrCode = ::GetLastError();
LOG_PUT(Put, LL_DEBUG) _T("< ZwDeleteFile: return 0x%08X: "), lRes);
::SetLastError(dwErrCode);
return lRes;
}
typedef NTSTATUS (NTAPI* PF_NtDeleteFile)(IN NT::POBJECT_ATTRIBUTES pObjectAttributes);
PF_NtDeleteFile g_pfNtDeleteFile = NULL;
NTSTATUS NTAPI MyNtDeleteFile(IN NT::POBJECT_ATTRIBUTES pObjectAttributes) {
LOG_PUT(Put, LL_DEBUG) _T("> NtDeleteFile: "));
CString strFileName;
if (pObjectAttributes && pObjectAttributes->ObjectName) {
DWORD ulLength = pObjectAttributes->ObjectName->Length>>1; // для WCHAR строки длину в байтах преобразую в длину в символах
try {
#ifdef UNICODE
strFileName.Format(_T("%.*s"), ulLength, pObjectAttributes->ObjectName->Buffer);
#else
strFileName.Format(_T("%.*S"), ulLength, pObjectAttributes->ObjectName->Buffer);
#endif
} catch(...) {
LOG_PUT(Put, LL_ERROR) _T(" NtDeleteFile: Exception при выборке имени файла из структуры OBJECT_ATTRIBUTES. Длина строки %i байт, адрес строки 0x%08X. "), pObjectAttributes->ObjectName->Length, pObjectAttributes->ObjectName->Buffer);
}
}
LOG_PUT(Put, LL_DEBUG) _T(" NtDeleteFile: Файл [%s]: "), (LPCTSTR)strFileName);
NTSTATUS lRes = g_pfNtDeleteFile ? g_pfNtDeleteFile(pObjectAttributes) : STATUS_UNSUCCESSFUL;
DWORD dwErrCode = ::GetLastError();
LOG_PUT(Put, LL_DEBUG) _T("< NtDeleteFile: return 0x%08X: "), lRes);
::SetLastError(dwErrCode);
return lRes;
}
typedef NTSTATUS (NTAPI* PF_ZwSetInformationFile)(IN HANDLE FileHandle, OUT NT::PIO_STATUS_BLOCK IoStatusBlock, IN PVOID FileInformation, IN ULONG Length, IN NT::FILE_INFORMATION_CLASS FileInformationClass);
PF_ZwSetInformationFile g_pfZwSetInformationFile = NULL;
NTSTATUS NTAPI MyZwSetInformationFile(IN HANDLE FileHandle, OUT NT::PIO_STATUS_BLOCK IoStatusBlock, IN PVOID FileInformation, IN ULONG Length, IN NT::FILE_INFORMATION_CLASS FileInformationClass) {
if (FileInformationClass == NT::FileDispositionInformation) {
if (Length == sizeof(NT::FILE_DISPOSITION_INFORMATION)) {
NT::PFILE_DISPOSITION_INFORMATION pFileDispositionInformation = (NT::PFILE_DISPOSITION_INFORMATION)FileInformation;
if (pFileDispositionInformation->DeleteFile) {
LOG_PUT(Put, LL_DEBUG) _T(" ZwSetInformationFile: Удаление файла "));
}
}
}
NTSTATUS lRes = g_pfZwSetInformationFile ? g_pfZwSetInformationFile (FileHandle, IoStatusBlock, FileInformation, Length, FileInformationClass) : STATUS_UNSUCCESSFUL;
//DWORD dwErrCode = ::GetLastError();
//::SetLastError(dwErrCode);
return lRes;
}
typedef NTSTATUS (NTAPI* PF_NtSetInformationFile)(IN HANDLE FileHandle, OUT NT::PIO_STATUS_BLOCK IoStatusBlock, IN PVOID FileInformation, IN ULONG Length, IN NT::FILE_INFORMATION_CLASS FileInformationClass);
PF_NtSetInformationFile g_pfNtSetInformationFile = NULL;
NTSTATUS NTAPI MyNtSetInformationFile(IN HANDLE FileHandle, OUT NT::PIO_STATUS_BLOCK IoStatusBlock, IN PVOID FileInformation, IN ULONG Length, IN NT::FILE_INFORMATION_CLASS FileInformationClass) {
#ifdef _DEBUG
//::AfxTrace(_T("> NtSetInformationFile: \r\n"));
#endif
if (FileInformationClass == NT::FileDispositionInformation) {
if (Length == sizeof(NT::FILE_DISPOSITION_INFORMATION)) {
NT::PFILE_DISPOSITION_INFORMATION pFileDispositionInformation = (NT::PFILE_DISPOSITION_INFORMATION)FileInformation;
if (pFileDispositionInformation->DeleteFile) {
LOG_PUT(Put, LL_DEBUG) _T(" NtSetInformationFile: Удаление файла "));
}
}
}
NTSTATUS lRes = g_pfNtSetInformationFile ? g_pfNtSetInformationFile (FileHandle, IoStatusBlock, FileInformation, Length, FileInformationClass) : STATUS_UNSUCCESSFUL;
//DWORD dwErrCode = ::GetLastError();
#ifdef _DEBUG
//::AfxTrace(_T("> NtSetInformationFile: return 0x%08X \r\n"), lRes);
#endif
//::SetLastError(dwErrCode);
return lRes;
}
/*******************************************************************************************/
//
// Собственно главная ф-ция очереди мониторинга удаления файлов
//
/*******************************************************************************************/
UINT ControllingFunction(LPVOID) {
/////////////////////////////////////////////////////////////////////////
// Перехватываю ф-ции удаления с помощью Detours API
/////////////////////////////////////////////////////////////////////////
HMODULE hModule = ::GetModuleHandle(_T("ntdll.dll"));
BOOL bRes = (hModule != NULL);
DWORD dwErrCode = ERROR_SUCCESS;
if (!bRes) {
dwErrCode = ::GetLastError();
LOG_PUT(PutErrorCode, LL_ERROR) _T(" ControllingFunction: GetModuleHandle('ntdll.dll'): "), dwErrCode);
hModule = ::LoadLibrary(_T("ntdll.dll")); // ну это уже лишнее, конечно
bRes = (hModule != NULL);
if (!bRes) {
dwErrCode = ::GetLastError();
LOG_PUT(PutErrorCode, LL_ERROR) _T(" ControllingFunction: LoadLibrary('ntdll.dll'): "), dwErrCode);
}
}
if (bRes) {
#define GET_PROC_ADDRESS_NTDLL(FuncName, TextFuncNameA, TextFuncNameT) \
g_pf##FuncName = (PF_##FuncName)::GetProcAddress(hModule, TextFuncNameA); \
bRes = (g_pf##FuncName != NULL); \
if (!bRes) { \
dwErrCode = ::GetLastError(); \
LOG_PUT(PutErrorCode, LL_ERROR) _T(" ControllingFunction: GetProcAddress('ntdll.dll', '") TextFuncNameT _T("'): "), dwErrCode); \
}
GET_PROC_ADDRESS_NTDLL(ZwDeleteFile , "ZwDeleteFile" , _T("ZwDeleteFile" ))
GET_PROC_ADDRESS_NTDLL(NtDeleteFile , "NtDeleteFile" , _T("NtDeleteFile" ))
GET_PROC_ADDRESS_NTDLL(ZwSetInformationFile, "ZwSetInformationFile", _T("ZwSetInformationFile"))
GET_PROC_ADDRESS_NTDLL(NtSetInformationFile, "NtSetInformationFile", _T("NtSetInformationFile"))
}
g_pfDeleteFileA = (PF_DeleteFileA )DetourFunction((PBYTE)::DeleteFileA , (PBYTE)MyDeleteFileA );
g_pfDeleteFileW = (PF_DeleteFileW )DetourFunction((PBYTE)::DeleteFileW , (PBYTE)MyDeleteFileW );
g_pfZwDeleteFile = (PF_ZwDeleteFile )DetourFunction((PBYTE)g_pfZwDeleteFile , (PBYTE)MyZwDeleteFile );
//g_pfNtDeleteFile = (PF_NtDeleteFile )DetourFunction((PBYTE)g_pfNtDeleteFile , (PBYTE)MyNtDeleteFile );
g_pfZwSetInformationFile = (PF_ZwSetInformationFile)DetourFunction((PBYTE)g_pfZwSetInformationFile, (PBYTE)MyZwSetInformationFile);
//g_pfNtSetInformationFile = (PF_NtSetInformationFile)DetourFunction((PBYTE)g_pfNtSetInformationFile, (PBYTE)MyNtSetInformationFile);
/////////////////////////////////////////////////////////////////////////
#pragma message("> Незабудь поменять каталог!\n")
CString strDirCache(_T("c:\\"));
HANDLE hEventNotifyCloseThread = NULL;
try {
#pragma message("> Незабудь раскоментировать эвент окончания главногой очереди процесса!\n Иначе WaitForMultipleObjects будет вылетать на ошибке ERROR_INVALID_HANDLE...\n")
//hEventNotifyCloseThread = ((CMyProject*)::AfxGetApp())->m_hEventNotifyCloseThread;
} catch(...) {}
HANDLE hDirCache = ::CreateFile(
strDirCache, // pointer to the file name
FILE_LIST_DIRECTORY, // access (read/write) mode
FILE_SHARE_READ|FILE_SHARE_DELETE, // share mode
NULL, // security descriptor
OPEN_EXISTING, // how to create
FILE_FLAG_OVERLAPPED | FILE_FLAG_BACKUP_SEMANTICS,//FILE_ATTRIBUTE_DIRECTORY,// // file attributes
NULL // file with attributes to copy
);
if (hDirCache == INVALID_HANDLE_VALUE) {
dwErrCode = ::GetLastError();
LOG_PUT(PutErrorCode, LL_ERROR) ::Format(_T(" ControllingFunction: Не сумел открыть для контроля директорию: CreateFile('%s', FILE_LIST_DIRECTORY, ...): "), (LPCTSTR)strDirCache), dwErrCode);
}
HANDLE hEventChangeDirCache = ::CreateEvent(NULL, FALSE, FALSE, NULL);
if (hEventChangeDirCache == NULL) {
dwErrCode = ::GetLastError();
LOG_PUT(PutErrorCode, LL_ERROR) _T(" ControllingFunction: Не сумел создать эвент открыть для контроля директорию: CreateEvent: "), dwErrCode);
}
OVERLAPPED ovChangeDirCache = {0,0,0,0, hEventChangeDirCache};
BYTE pBuffer[1024];
const DWORD dwBufferLength = sizeof(pBuffer);
bRes = ::ReadDirectoryChangesW(
hDirCache,
pBuffer,
dwBufferLength,
TRUE, // BOOL bWatchSubtree,
FILE_NOTIFY_CHANGE_FILE_NAME, // DWORD dwNotifyFilter,
NULL, // PDWORD lpBytesReturned // [out] For synchronous calls
&ovChangeDirCache, // LPOVERLAPPED lpOverlapped,
NULL // LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
dwErrCode = ::GetLastError();
if (!bRes) {
LOG_PUT(PutErrorCode, LL_ERROR) _T(" ControllingFunction: ReadDirectoryChangesW: "), dwErrCode);
}
HANDLE hChangeNotifyCache = ::FindFirstChangeNotification(
strDirCache, // directory to watch
TRUE, // do not watch the subtree
FILE_NOTIFY_CHANGE_FILE_NAME // watch file name changes
);
if (hChangeNotifyCache == INVALID_HANDLE_VALUE) {
dwErrCode = ::GetLastError();
LOG_PUT(PutErrorCode, LL_ERROR) ::Format(_T(" ControllingFunction: Не сумел открыть для контроля директорию: FindFirstChangeNotification('%s', TRUE, FILE_NOTIFY_CHANGE_FILE_NAME): "), (LPCTSTR)strDirCache), dwErrCode);
}
if ((hDirCache != INVALID_HANDLE_VALUE) && (hChangeNotifyCache != INVALID_HANDLE_VALUE)) {
const HANDLE pH[3] = {hEventNotifyCloseThread, hEventChangeDirCache, hChangeNotifyCache};
do {
BOOL bBreak = TRUE;
DWORD dwRes = ::WaitForMultipleObjects(3, pH, FALSE, INFINITE);
dwErrCode = ::GetLastError();
switch (dwRes) {
case WAIT_OBJECT_0+0:
LOG_PUT(Put, LL_INFO) _T(" ControllingFunction: Главная очередь процесса завершилась!"));
break;
case WAIT_OBJECT_0+1:
bBreak = FALSE;
{
LOG_PUT(Put, LL_DEBUG) _T(" ControllingFunction: Пришло уведомление (ReadDirectoryChangesW) о изменени в директории. "));
DWORD dwNumberOfBytesTransferred = 0;
bRes = ::GetOverlappedResult(hDirCache, &ovChangeDirCache, &dwNumberOfBytesTransferred, FALSE);
if (!bRes) {
dwErrCode = ::GetLastError();
LOG_PUT(PutErrorCode, LL_ERROR) _T(" ControllingFunction: GetOverlappedResult: "), dwErrCode);
} else {
if (dwNumberOfBytesTransferred == 0) {
LOG_PUT(Put, LL_ERROR) _T(" ControllingFunction: ReadDirectoryChangesW & GetOverlappedResult: dwNumberOfBytesTransferred == 0 "));
} else {
const FILE_NOTIFY_INFORMATION *pPNI = (FILE_NOTIFY_INFORMATION*)pBuffer;
int i = 0;
DWORD dwCountByte = 0;
do {
i++;
if (dwCountByte + pPNI->FileNameLength > dwNumberOfBytesTransferred) {
LOG_PUT(Put, LL_ERROR) _T(" ControllingFunction: ReadDirectoryChangesW & GetOverlappedResult: Неверно работаю с FILE_NOTIFY_INFORMATION - залажу за её пределы: dwCountByte + pPNI->FileNameLength > dwNumberOfBytesTransferred (%i+%i > %i) "), dwCountByte, pPNI->FileNameLength, dwNumberOfBytesTransferred);
break;
}
CString strFileName;
DWORD dwFileNameLength = pPNI->FileNameLength>>1; // для WCHAR строки длину в байтах преобразую в длину в символах
try {
#ifdef UNICODE
strFileName.Format(_T("%.*s"), dwFileNameLength, pPNI->FileName);
#else
strFileName.Format(_T("%.*S"), dwFileNameLength, pPNI->FileName);
#endif
} catch(...) {
LOG_PUT(Put, LL_ERROR) _T(" ControllingFunction: Exception при выборке имени файла из структуры FILE_NOTIFY_INFORMATION. Длина строки %i байт, адрес строки 0x%08X. "), pPNI->FileNameLength, pPNI->FileName);
}
switch (pPNI->Action) {
case FILE_ACTION_ADDED: // The file was added to the directory.
LOG_PUT(Put, LL_DEBUG) _T(" ControllingFunction: Операция FILE_ACTION_ADDED. Файл [%s]"), (LPCTSTR)strFileName);
break;
case FILE_ACTION_REMOVED: // The file was removed from the directory.
LOG_PUT(Put, LL_DEBUG) _T(" ControllingFunction: Операция FILE_ACTION_REMOVED. Файл [%s]"), (LPCTSTR)strFileName);
break;
case FILE_ACTION_MODIFIED: // The file was modified. This can be a change in the time stamp or attributes.
LOG_PUT(Put, LL_DEBUG) _T(" ControllingFunction: Операция FILE_ACTION_MODIFIED. Файл [%s]"), (LPCTSTR)strFileName);
break;
case FILE_ACTION_RENAMED_OLD_NAME: // The file was renamed and this is the old name.
LOG_PUT(Put, LL_DEBUG) _T(" ControllingFunction: Операция FILE_ACTION_RENAMED_OLD_NAME. Файл [%s]"), (LPCTSTR)strFileName);
break;
case FILE_ACTION_RENAMED_NEW_NAME: // The file was renamed and this is the new name.
LOG_PUT(Put, LL_DEBUG) _T(" ControllingFunction: Операция FILE_ACTION_RENAMED_NEW_NAME. Файл [%s]"), (LPCTSTR)strFileName);
break;
default:
LOG_PUT(Put, LL_ERROR) _T(" ControllingFunction: Неизвестный код (%i) действия над файлом [%s] (цикл #%i обработки структуры FILE_NOTIFY_INFORMATION) "), pPNI->Action, (LPCTSTR)strFileName, i);
break;
}
if (pPNI->NextEntryOffset == 0) break;
pPNI = (FILE_NOTIFY_INFORMATION*)(((BYTE*)pPNI)+pPNI->NextEntryOffset);
dwCountByte += pPNI->NextEntryOffset;
if (dwCountByte >= dwNumberOfBytesTransferred) {
LOG_PUT(Put, LL_ERROR) ::Format(_T(" ControllingFunction: ReadDirectoryChangesW & GetOverlappedResult: Неверно работаю с FILE_NOTIFY_INFORMATION - залажу за её пределы: dwCountByte >= dwNumberOfBytesTransferred (%i >= %i). Последний pPNI->NextEntryOffset == %i "), dwCountByte, dwNumberOfBytesTransferred, pPNI->NextEntryOffset));
break;
}
} while(true);
}
}
bRes = ::ReadDirectoryChangesW(
hDirCache,
pBuffer,
dwBufferLength,
TRUE, // BOOL bWatchSubtree,
FILE_NOTIFY_CHANGE_FILE_NAME, // DWORD dwNotifyFilter,
NULL, // PDWORD lpBytesReturned // [out] For synchronous calls
&ovChangeDirCache, // LPOVERLAPPED lpOverlapped,
NULL // LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
dwErrCode = ::GetLastError();
if (!bRes) {
LOG_PUT(PutErrorCode, LL_ERROR) _T(" ControllingFunction: ReadDirectoryChangesW: "), dwErrCode);
}
}
break;
case WAIT_OBJECT_0+2:
bBreak = FALSE;
{
LOG_PUT(Put, LL_DEBUG) _T(" ControllingFunction: Пришло уведомление (FindFirstNextChangeNotification) о изменени в директории. "));
//RefreshDirectory(); // application-defined function
bRes = ::FindNextChangeNotification(hChangeNotifyCache);
if (!bRes) {
dwErrCode = ::GetLastError();
LOG_PUT(PutErrorCode, LL_ERROR) ::Format(_T(" ControllingFunction: Запрос на следующую нотификацию изменения директории [%s]: FindNextChangeNotification: "), (LPCTSTR)strDirCache), dwErrCode);
}
}
break;
case WAIT_TIMEOUT:
LOG_PUT(PutErrorCode, LL_ERROR) _T(" ControllingFunction: WaitForMultipleObjects return WAIT_TIMEOUT: "), dwErrCode);
break;
case WAIT_FAILED:
LOG_PUT(PutErrorCode, LL_ERROR) _T(" ControllingFunction: WaitForMultipleObjects return WAIT_FAILED: "), dwErrCode);
break;
default:
LOG_PUT(PutErrorCode, LL_ERROR) ::Format(_T(" ControllingFunction: WaitForMultipleObjects return %i: "), dwRes), dwErrCode);
break;
}
if (bBreak) break;
} while(true);
}
if (hDirCache != INVALID_HANDLE_VALUE) {
bRes = ::CloseHandle(hDirCache); hDirCache = NULL;
if (!bRes) {
LOG_PUT(PutErrorCode, LL_ERROR) _T(" ControllingFunction: Закрытие дескриптора директории: CloseHandle: "), ::GetLastError());
}
}
if (hChangeNotifyCache == INVALID_HANDLE_VALUE) {
bRes = ::FindCloseChangeNotification(hChangeNotifyCache); hChangeNotifyCache = NULL;
if (!bRes) {
LOG_PUT(PutErrorCode, LL_ERROR) _T(" ControllingFunction: Закрытие дескриптора нотификации изменений директории: FindCloseChangeNotification: "), ::GetLastError());
}
}
LOG_PUT(Put, LL_INFO) _T(" ControllingFunction: Выхожу из очереди. "));
return 0;
}