⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 directorychanges.cpp

📁 局域网来访者IP监视以及文件修改监视: 1.监听常用的局域网访问工具
💻 CPP
📖 第 1 页 / 共 5 页
字号:

	}
	return FALSE;
}

BOOL CDirectoryChangeWatcher::CDirWatchInfo::SignalShutdown( HANDLE hCompPort )
//added to fix a bug -- this will be called normally by UnwatchDirectory(HANDLE)
//						and abnormally by the worker thread in the case that ReadDirectoryChangesW fails -- see code.
//
//	Signals the worker thread(via the I/O completion port) that it is to stop watching the 
//	directory for this object, and then returns.
//
{
	BOOL bRetVal = FALSE;
	ASSERT( hCompPort );
	ASSERT( m_hDir != INVALID_HANDLE_VALUE );
	//Lock the properties so that they aren't modified by another thread
	VERIFY( LockProperties() ); //unlikey to fail...
		
	//set the state member to indicate that the object is to stop monitoring the 
	//directory that this CDirWatchInfo is responsible for...
	m_RunningState = CDirectoryChangeWatcher::CDirWatchInfo::RUNNING_STATE_STOP;
	//put this object in the I/O completion port... GetQueuedCompletionStatus() will return it inside the worker thread.
	bRetVal = PostQueuedCompletionStatus(hCompPort, sizeof(CDirWatchInfo*), (DWORD)this, &m_Overlapped);

	if( !bRetVal )
	{
		TRACE(_T("PostQueuedCompletionStatus() failed! GetLastError(): %d\n"), GetLastError());
	}
	VERIFY( UnlockProperties() );
	
	return bRetVal;
}

BOOL CDirectoryChangeWatcher::CDirWatchInfo::WaitForShutdown()
//
//	This is to be called some time after SignalShutdown().
//	
//
{
	ASSERT_VALID(&m_StartStopEvent);
	
	//Wait for the Worker thread to indicate that the watch has been stopped
	DWORD dwWait;
	bool bWMQuitReceived = false;
	do{
		dwWait	= MsgWaitForMultipleObjects(1, &m_StartStopEvent.m_hObject, FALSE, 5000, QS_ALLINPUT);//wait five seconds
		switch( dwWait )
		{
		case WAIT_OBJECT_0:
			//handle became signalled!
			break;
		case WAIT_OBJECT_0 + 1:
			{
				//This thread awoke due to sent/posted message
				//process the message Q
				//
				//	There is a message in this thread's queue, so 
				//	MsgWaitForMultipleObjects returned.
				//	Process those messages, and wait again.

				MSG msg;
				while( PeekMessage(&msg, NULL, 0,0, PM_REMOVE ) ) 
				{
					if( msg.message != WM_QUIT)
					{
						TranslateMessage(&msg);
						DispatchMessage(&msg);
					}
					else
					{
						/****
						This appears to be causing quite a lot of pain, to quote Mustafa.

						//it's the WM_QUIT message, put it back in the Q and
						// exit this function
						PostMessage(msg.hwnd, msg.message, msg.wParam, msg.lParam );
						bWMQuitReceived = true;

						****/
						break;
					}
				}
			}break;
		case WAIT_TIMEOUT:
			{
				TRACE(_T("WARNING: Possible Deadlock detected! ThreadID: %d File: %s Line: %d\n"), GetCurrentThreadId(), _T(__FILE__), __LINE__);
			}break;
		}//end switch(dwWait)
	}while( dwWait != WAIT_OBJECT_0 && !bWMQuitReceived );
		
	
	
	ASSERT( dwWait == WAIT_OBJECT_0 || bWMQuitReceived);

	m_StartStopEvent.ResetEvent();
	
	return (BOOL) (dwWait == WAIT_OBJECT_0 || bWMQuitReceived);
}


UINT CDirectoryChangeWatcher::MonitorDirectoryChanges(LPVOID lpvThis)
/********************************************
   The worker thread function which monitors directory changes....
********************************************/
{
    DWORD numBytes;

    CDirWatchInfo * pdi;
    LPOVERLAPPED lpOverlapped;
    
	CDirectoryChangeWatcher * pThis = reinterpret_cast<CDirectoryChangeWatcher*>(lpvThis);
	ASSERT( pThis );

	pThis->On_ThreadInitialize();


    do
    {
        // Retrieve the directory info for this directory
        // through the io port's completion key
        if( !GetQueuedCompletionStatus( pThis->m_hCompPort,
                                   &numBytes,
                                   (LPDWORD) &pdi,//<-- completion Key
                                   &lpOverlapped,
                                   INFINITE) )
		{//The io completion request failed...
		 //probably because the handle to the directory that
		 //was used in a call to ReadDirectoryChangesW has been closed.

			//
			//	calling pdi->CloseDirectoryHandle() will cause GetQueuedCompletionStatus() to return false.
			//  
			//
			if( !pdi 
			|| ( pdi && AfxIsValidAddress(pdi, sizeof(CDirectoryChangeWatcher::CDirWatchInfo)))
					 &&  pdi->m_hDir != INVALID_HANDLE_VALUE //the directory handle is still open! (we expect this when after we close the directory handle )
			  )
			{
#ifdef _DEBUG
			TRACE(_T("GetQueuedCompletionStatus() returned FALSE\nGetLastError(): %d Completion Key: %p lpOverlapped: %p\n"), GetLastError(), pdi, lpOverlapped);
			MessageBeep( static_cast<UINT>(-1) );
#endif
			}
		}
		
		if ( pdi )//pdi will be null if I call PostQueuedCompletionStatus(m_hCompPort, 0,0,NULL);
        {
			//
			//	The following check to AfxIsValidAddress() should go off in the case
			//	that I have deleted this CDirWatchInfo object, but it was still in 
			//	"in the Queue" of the i/o completion port from a previous overlapped operation.
			//
			ASSERT( AfxIsValidAddress(pdi, 
					sizeof(CDirectoryChangeWatcher::CDirWatchInfo)) );
			/***********************************
			The CDirWatchInfo::m_RunningState is pretty much the only member
			of CDirWatchInfo that can be modified from the other thread.
			The functions StartMonitor() and UnwatchDirecotry() are the functions that 
			can modify that variable.

			So that I'm sure that I'm getting the right value, 
			I'm using a critical section to guard against another thread modyfying it when I want
			to read it...
			
			************************************/
			bool bObjectShouldBeOk = true;
			try{
			    VERIFY( pdi->LockProperties() );//don't give the main thread a chance to change this object
			}
			catch(...){
				//any sort of exception here indicates I've
				//got a hosed object.
				TRACE(_T("CDirectoryChangeWatcher::MonitorDirectoryChanges() -- pdi->LockProperties() raised an exception!\n"));
				bObjectShouldBeOk = false;
			}
			if( bObjectShouldBeOk )
			{
										    //while we're working with this object...

				CDirWatchInfo::eRunningState Run_State = pdi->m_RunningState ;
				
				VERIFY( pdi->UnlockProperties() );//let another thread back at the properties...
				/***********************************
				 Unlock it so that there isn't a DEADLOCK if 
				 somebody tries to call a function which will 
				 cause CDirWatchInfo::UnwatchDirectory() to be called
				 from within the context of this thread (eg: a function called because of
				 the handler for one of the CDirectoryChangeHandler::On_Filexxx() functions)
		
				************************************/
				
				ASSERT( pdi->GetChangeHandler() );
				switch( Run_State )
				{
				case CDirWatchInfo::RUNNING_STATE_START_MONITORING:
					{
						//Issue the initial call to ReadDirectoryChangesW()
						
						if( !ReadDirectoryChangesW( pdi->m_hDir,
											pdi->m_Buffer,//<--FILE_NOTIFY_INFORMATION records are put into this buffer
											READ_DIR_CHANGE_BUFFER_SIZE,
											pdi->m_bWatchSubDir,
											pdi->m_dwChangeFilter,
											&pdi->m_dwBufLength,//this var not set when using asynchronous mechanisms...
											&pdi->m_Overlapped,
											NULL) )//no completion routine!
						{
							pdi->m_dwReadDirError = GetLastError();
							if( pdi->GetChangeHandler() )
								pdi->GetChangeHandler()->On_WatchStarted(pdi->m_dwReadDirError, pdi->m_strDirName);
						}
						else
						{//read directory changes was successful!
						 //allow it to run normally
							pdi->m_RunningState = CDirWatchInfo::RUNNING_STATE_NORMAL;
							pdi->m_dwReadDirError = ERROR_SUCCESS;
							if( pdi->GetChangeHandler() )
								pdi->GetChangeHandler()->On_WatchStarted(ERROR_SUCCESS, pdi->m_strDirName );
						}
						pdi->m_StartStopEvent.SetEvent();//signall that the ReadDirectoryChangesW has been called
														 //check CDirWatchInfo::m_dwReadDirError to see whether or not ReadDirectoryChangesW succeeded...

						//
						//	note that pdi->m_dwReadDirError is the value returned by WatchDirectory()
						//
						
		
					}break;
				case CDirWatchInfo::RUNNING_STATE_STOP:
					{
						//We want to shut down the monitoring of the directory
						//that pdi is managing...
						
						if( pdi->m_hDir != INVALID_HANDLE_VALUE )
						{
						 //Since I've previously called ReadDirectoryChangesW() asynchronously, I am waiting
						 //for it to return via GetQueuedCompletionStatus().  When I close the
						 //handle that ReadDirectoryChangesW() is waiting on, it will
						 //cause GetQueuedCompletionStatus() to return again with this pdi object....
						 // Close the handle, and then wait for the call to GetQueuedCompletionStatus()
						 //to return again by breaking out of the switch, and letting GetQueuedCompletionStatus()
						 //get called again
							pdi->CloseDirectoryHandle();
							pdi->m_RunningState = CDirWatchInfo::RUNNING_STATE_STOP_STEP2;//back up step...GetQueuedCompletionStatus() will still need to return from the last time that ReadDirectoryChangesW() was called.....

						 //
						 //	The watch has been stopped, tell the client about it
						 //						if( pdi->GetChangeHandler() )
							pdi->GetChangeHandler()->On_WatchStopped( pdi->m_strDirName );
						}
						else
						{
							//either we weren't watching this direcotry in the first place,
							//or we've already stopped monitoring it....
							pdi->m_StartStopEvent.SetEvent();//set the event that ReadDirectoryChangesW has returned and no further calls to it will be made...
						}
						
					
					}break;
				case CDirWatchInfo::RUNNING_STATE_STOP_STEP2:
					{

						//GetQueuedCompletionStatus() has returned from the last
						//time that ReadDirectoryChangesW was called...
						//Using CloseHandle() on the directory handle used by
						//ReadDirectoryChangesW will cause it to return via GetQueuedCompletionStatus()....
						if( pdi->m_hDir == INVALID_HANDLE_VALUE )
							pdi->m_StartStopEvent.SetEvent();//signal that no further calls to ReadDirectoryChangesW will be made
															 //and this pdi can be deleted 
						else
						{//for some reason, the handle is still open..
												
							pdi->CloseDirectoryHandle();

							//wait for GetQueuedCompletionStatus() to return this pdi object again


						}
		
					}break;
															
				case CDirWatchInfo::RUNNING_STATE_NORMAL:
					{
						
						if( pdi->GetChangeHandler() )
							pdi->GetChangeHandler()->SetChangedDirectoryName( pdi->m_strDirName );
		
						DWORD dwReadBuffer_Offset = 0UL;

						//process the FILE_NOTIFY_INFORMATION records:
						CFileNotifyInformation notify_info( (LPBYTE)pdi->m_Buffer, READ_DIR_CHANGE_BUFFER_SIZE);

						pThis->ProcessChangeNotifications(notify_info, pdi, dwReadBuffer_Offset);
		

						//	Changes have been processed,
						//	Reissue the watch command
						//
						if( !ReadDirectoryChangesW( pdi->m_hDir,
											  pdi->m_Buffer + dwReadBuffer_Offset,//<--FILE_NOTIFY_INFORMATION records are put into this buffer 
								              READ_DIR_CHANGE_BUFFER_SIZE - dwReadBuffer_Offset,
											  pdi->m_bWatchSubDir,
										      pdi->m_dwChangeFilter,
											  &pdi->m_dwBufLength,//this var not set when using asynchronous mechanisms...
											&pdi->m_Overlapped,
											NULL) )//no completion routine!
						{
							//
							//	NOTE:  
							//		In this case the thread will not wake up for 
							//		this pdi object because it is no longer associated w/
							//		the I/O completion port...there will be no more outstanding calls to ReadDirectoryChangesW
							//		so I have to skip the normal shutdown routines(normal being what happens when CDirectoryChangeWatcher::UnwatchDirectory() is called.
							//		and close this up, & cause it to be freed.
							//
							TRACE(_T("WARNING: ReadDirectoryChangesW has failed during normal operations...failed on directory: %s\n"), pdi->m_strDirName);

							ASSERT( pThis );
							//
							//	To help insure that this has been unwatched by the time
							//	the main thread processes the On_ReadDirectoryChangesError() notification
							//	bump the thread priority up temporarily.  The reason this works is because the notification
							//	is really posted to another thread's message queue,...by setting this thread's priority
							//	to highest, this thread will get to shutdown the watch by the time the other thread has a chance
							//	to handle it. *note* not technically guaranteed 100% to be the case, but in practice it'll work.
							int nOldThreadPriority = GetThreadPriority( GetCurrentThread() );
							SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);							

							//
							//	Notify the client object....(a CDirectoryChangeHandler derived class)
							//
							try{
								pdi->m_dwReadDirError = GetLastError();
								pdi->GetChangeHandler()->On_ReadDirectoryChangesError( pdi->m_dwReadDirError, pdi->m_strDirName );


								//Do the shutdown
								pThis->UnwatchDirectoryBecauseOfError( pdi );
								//pdi = NULL; <-- DO NOT set this to NULL, it will cause this worker thread to exit.
								//pdi is INVALID at this point!!
							}
							catch(...)
							{
								//just in case of exception, this thread will be set back to 
								//normal priority.
							}
							//
							//	Set the thread priority back to normal.
							//
							SetThreadPriority(GetCurrentThread(), nOldThreadPriority);
													
						}
						else
						{//success, continue as normal
							pdi->m_dwReadDirError = ERROR_SUCCESS;
						}
					}break;
				default:
					TRACE(_T("MonitorDirectoryChanges() -

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -