Source

NPAPI-chrome-file-api / Win / FileWatcherTaskWin.cpp

Full commit

/*
* Copyright 2012 Ryan Ackley (ryanackley@gmail.com)
*
* This file is part of NPAPI Chrome File API
*
* NPAPI Chrome File API is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* 
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
* 
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#include <string>
#include "FileWatcherTaskWin.h"
#include "variant_list.h"



FileWatcherTaskWin::FileWatcherTaskWin(std::string path, FB::JSObjectPtr callback)
{
	stop = false;

	WCHAR widePathBuf[2048];
	int wpBufSize = MultiByteToWideChar(CP_UTF8, 0, path.c_str(), -1, widePathBuf, 2048);
	
	m_path = widePathBuf;
	m_callback = callback;

	ZeroMemory(&m_ol, sizeof(m_ol));

	m_handle = CreateFile(m_path.c_str(), 
								FILE_LIST_DIRECTORY, 
								FILE_SHARE_READ | FILE_SHARE_WRITE,
								NULL,
								OPEN_EXISTING,
								FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
								NULL);
	m_ol.hEvent = this;
}
void FileWatcherTaskWin::StartWatching()
{
	m_thread = boost::thread(boost::bind(&FileWatcherTaskWin::StartAlertLoop, this));
	QueueUserAPC(FileWatcherTaskWin::InitDirectoryWatcher, m_thread.native_handle(), (ULONG_PTR)this);
}
void FileWatcherTaskWin::StartAlertLoop()
{
	while (!stop)
	{
		SleepEx(INFINITE, TRUE);
	}
}
void CALLBACK FileWatcherTaskWin::InitDirectoryWatcher(ULONG_PTR arg)
{
	FileWatcherTaskWin* task = (FileWatcherTaskWin*)arg;
	BOOL success = task->ListenForFileEvents();
}

BOOL FileWatcherTaskWin::ListenForFileEvents()
{
	DWORD dwBytes=0;

	return ReadDirectoryChangesW(m_handle, m_buffer, 4096, TRUE, FILE_NOTIFY_CHANGE_LAST_WRITE, &dwBytes, &m_ol, &WatcherCallback);
}

void CALLBACK FileWatcherTaskWin::WatcherCallback(DWORD dwErrorCode,DWORD bytesInBuf,LPOVERLAPPED lpOverlapped)
{
	FileWatcherTaskWin* task = (FileWatcherTaskWin*)lpOverlapped->hEvent; 
	if (bytesInBuf > 0)
	{
		BOOL moreToRead = true;
		char* readFrom = task->m_buffer;
		while(moreToRead)
		{
			FILE_NOTIFY_INFORMATION* fni = (FILE_NOTIFY_INFORMATION*)readFrom;
			std::wstring fileName(fni->FileName, fni->FileNameLength/sizeof(WCHAR));
			std::wstring wideFinalPath = task->m_path + L"\\" + fileName;

			char utf8Buf[2048];
			int pathLength = WideCharToMultiByte(CP_UTF8, 0, wideFinalPath.c_str(), -1, utf8Buf, 2048, NULL, NULL);
			std::string finalPath = utf8Buf;
			
			task->m_callback->Invoke("", FB::variant_list_of(finalPath));
			if (!fni->NextEntryOffset)
			{
				moreToRead = false;
			}
			else
			{
				readFrom += fni->NextEntryOffset;
			}
		}
	}
	BOOL success = task->ListenForFileEvents();
}
void CALLBACK FileWatcherTaskWin::TerminateWatch(ULONG_PTR arg)
{
	FileWatcherTaskWin* task = (FileWatcherTaskWin*)arg;
	task->CloseHandlesStopAlertLoop();
}
void FileWatcherTaskWin::CloseHandlesStopAlertLoop()
{
	CancelIo(m_handle);
	CloseHandle(m_handle);
	this->stop = true;
}
void FileWatcherTaskWin::StopWatching()
{
	QueueUserAPC(FileWatcherTaskWin::TerminateWatch, m_thread.native_handle(), (ULONG_PTR)this);
	// 5 seconds should be plenty of time
	m_thread.timed_join(boost::posix_time::seconds(5));
}