184 lines
5.8 KiB
C++
184 lines
5.8 KiB
C++
|
//
|
||
|
// The MIT License
|
||
|
//
|
||
|
// Copyright (c) 2010 James E Beveridge
|
||
|
//
|
||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
// of this software and associated documentation files (the "Software"), to deal
|
||
|
// in the Software without restriction, including without limitation the rights
|
||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
|
// copies of the Software, and to permit persons to whom the Software is
|
||
|
// furnished to do so, subject to the following conditions:
|
||
|
//
|
||
|
// The above copyright notice and this permission notice shall be included in
|
||
|
// all copies or substantial portions of the Software.
|
||
|
//
|
||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||
|
// THE SOFTWARE.
|
||
|
|
||
|
|
||
|
// This sample code is for my blog entry titled, "Understanding ReadDirectoryChangesW"
|
||
|
// http://qualapps.blogspot.com/2010/05/understanding-readdirectorychangesw.html
|
||
|
// See ReadMe.txt for overview information.
|
||
|
|
||
|
|
||
|
#include "stdafx.h"
|
||
|
#include "ReadDirectoryChanges.h"
|
||
|
#include "ReadDirectoryChangesPrivate.h"
|
||
|
#include <shlwapi.h>
|
||
|
|
||
|
|
||
|
// The namespace is a convenience to emphasize that these are internals
|
||
|
// interfaces. The namespace can be safely removed if you need to.
|
||
|
namespace ReadDirectoryChangesPrivate
|
||
|
{
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////////////
|
||
|
// CReadChangesRequest
|
||
|
|
||
|
CReadChangesRequest::CReadChangesRequest(CReadChangesServer* pServer, LPCTSTR sz, BOOL b, DWORD dw, DWORD size)
|
||
|
{
|
||
|
m_pServer = pServer;
|
||
|
m_dwFilterFlags = dw;
|
||
|
m_bIncludeChildren = b;
|
||
|
m_wstrDirectory = sz;
|
||
|
m_hDirectory = 0;
|
||
|
|
||
|
::ZeroMemory(&m_Overlapped, sizeof(OVERLAPPED));
|
||
|
|
||
|
// The hEvent member is not used when there is a completion
|
||
|
// function, so it's ok to use it to point to the object.
|
||
|
m_Overlapped.hEvent = this;
|
||
|
|
||
|
m_Buffer.resize(size);
|
||
|
m_BackupBuffer.resize(size);
|
||
|
}
|
||
|
|
||
|
|
||
|
CReadChangesRequest::~CReadChangesRequest()
|
||
|
{
|
||
|
// RequestTermination() must have been called successfully.
|
||
|
_ASSERTE(m_hDirectory == NULL || m_hDirectory == INVALID_HANDLE_VALUE);
|
||
|
}
|
||
|
|
||
|
|
||
|
bool CReadChangesRequest::OpenDirectory()
|
||
|
{
|
||
|
// Allow this routine to be called redundantly.
|
||
|
if (m_hDirectory)
|
||
|
return true;
|
||
|
|
||
|
m_hDirectory = ::CreateFileW(
|
||
|
m_wstrDirectory.c_str(), // pointer to the file name
|
||
|
FILE_LIST_DIRECTORY, // access (read/write) mode
|
||
|
FILE_SHARE_READ // share mode
|
||
|
| FILE_SHARE_WRITE
|
||
|
| FILE_SHARE_DELETE,
|
||
|
NULL, // security descriptor
|
||
|
OPEN_EXISTING, // how to create
|
||
|
FILE_FLAG_BACKUP_SEMANTICS // file attributes
|
||
|
| FILE_FLAG_OVERLAPPED,
|
||
|
NULL); // file with attributes to copy
|
||
|
|
||
|
if (m_hDirectory == INVALID_HANDLE_VALUE)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void CReadChangesRequest::BeginRead()
|
||
|
{
|
||
|
DWORD dwBytes=0;
|
||
|
|
||
|
// This call needs to be reissued after every APC.
|
||
|
BOOL success = ::ReadDirectoryChangesW(
|
||
|
m_hDirectory, // handle to directory
|
||
|
&m_Buffer[0], // read results buffer
|
||
|
m_Buffer.size(), // length of buffer
|
||
|
m_bIncludeChildren, // monitoring option
|
||
|
m_dwFilterFlags, // filter conditions
|
||
|
&dwBytes, // bytes returned
|
||
|
&m_Overlapped, // overlapped buffer
|
||
|
&NotificationCompletion); // completion routine
|
||
|
}
|
||
|
|
||
|
//static
|
||
|
VOID CALLBACK CReadChangesRequest::NotificationCompletion(
|
||
|
DWORD dwErrorCode, // completion code
|
||
|
DWORD dwNumberOfBytesTransfered, // number of bytes transferred
|
||
|
LPOVERLAPPED lpOverlapped) // I/O information buffer
|
||
|
{
|
||
|
CReadChangesRequest* pBlock = (CReadChangesRequest*)lpOverlapped->hEvent;
|
||
|
|
||
|
if (dwErrorCode == ERROR_OPERATION_ABORTED)
|
||
|
{
|
||
|
::InterlockedDecrement(&pBlock->m_pServer->m_nOutstandingRequests);
|
||
|
delete pBlock;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// This might mean overflow? Not sure.
|
||
|
if(!dwNumberOfBytesTransfered)
|
||
|
return;
|
||
|
|
||
|
// Can't use sizeof(FILE_NOTIFY_INFORMATION) because
|
||
|
// the structure is padded to 16 bytes.
|
||
|
_ASSERTE(dwNumberOfBytesTransfered >= offsetof(FILE_NOTIFY_INFORMATION, FileName) + sizeof(WCHAR));
|
||
|
|
||
|
pBlock->BackupBuffer(dwNumberOfBytesTransfered);
|
||
|
|
||
|
// Get the new read issued as fast as possible. The documentation
|
||
|
// says that the original OVERLAPPED structure will not be used
|
||
|
// again once the completion routine is called.
|
||
|
pBlock->BeginRead();
|
||
|
|
||
|
//pBlock->ProcessNotification();
|
||
|
|
||
|
pBlock->m_pServer->m_pBase->Notify(pBlock->GetDirectory());
|
||
|
}
|
||
|
|
||
|
void CReadChangesRequest::ProcessNotification()
|
||
|
{
|
||
|
BYTE* pBase = m_BackupBuffer.data();
|
||
|
|
||
|
for (;;)
|
||
|
{
|
||
|
FILE_NOTIFY_INFORMATION& fni = (FILE_NOTIFY_INFORMATION&)*pBase;
|
||
|
|
||
|
wstring wstrFilename(fni.FileName, fni.FileNameLength/sizeof(wchar_t));
|
||
|
// Handle a trailing backslash, such as for a root directory.
|
||
|
if (m_wstrDirectory.back() != L'\\')
|
||
|
wstrFilename = m_wstrDirectory + L"\\" + wstrFilename;
|
||
|
else
|
||
|
wstrFilename = m_wstrDirectory + wstrFilename;
|
||
|
|
||
|
// If it could be a short filename, expand it.
|
||
|
LPCWSTR wszFilename = PathFindFileNameW(wstrFilename.c_str());
|
||
|
int len = lstrlenW(wszFilename);
|
||
|
// The maximum length of an 8.3 filename is twelve, including the dot.
|
||
|
if (len <= 12 && wcschr(wszFilename, L'~'))
|
||
|
{
|
||
|
// Convert to the long filename form. Unfortunately, this
|
||
|
// does not work for deletions, so it's an imperfect fix.
|
||
|
wchar_t wbuf[MAX_PATH];
|
||
|
if (::GetLongPathNameW(wstrFilename.c_str(), wbuf, _countof(wbuf)) > 0)
|
||
|
wstrFilename = wbuf;
|
||
|
}
|
||
|
|
||
|
//m_pServer->m_pBase->Push(fni.Action, wstrFilename);
|
||
|
|
||
|
if (!fni.NextEntryOffset)
|
||
|
break;
|
||
|
pBase += fni.NextEntryOffset;
|
||
|
};
|
||
|
}
|
||
|
|
||
|
}
|