📄 synchexceptionmgr.cpp
字号:
//
// FILE: SynchExceptionMgr.cpp
//
// Copyright (c) 1997 by Aaron Michael Cohen and Mike Woodring
//
/////////////////////////////////////////////////////////////////////////
#include <windows.h>
#include <stdio.h>
#include "SynchExceptionMgr.h"
CExceptionHandler *CSynchronousExceptionManager::s_APCHandler = 0;
CExceptionHandler::NOTIFICATION_CONTEXT CSynchronousExceptionManager::s_APCContext = CExceptionHandler::THREAD_EXCEPTION;
DWORD CSynchronousExceptionManager::s_APCExceptionCode = 0;
BOOL CSynchronousExceptionManager::RegisterHandler( CExceptionHandler *pHandler )
{
CMclAutoLock Lock(m_CritSec);
BOOL fOkay;
fOkay = CExceptionManager::RegisterHandler(pHandler);
if( fOkay )
{
fOkay = m_HandlerInfo.PutOnTailOfList(new CHandlerInfo(pHandler));
if( !fOkay )
{
CExceptionManager::UnregisterHandler(pHandler);
}
}
return(fOkay);
}
void CSynchronousExceptionManager::UnregisterHandler( CExceptionHandler *pHandler )
{
CMclAutoLock Lock(m_CritSec);
CSynchronousExceptionManager::ForEachHandler(RemoveHandlerInfo, (DWORD)pHandler);
CExceptionManager::UnregisterHandler(pHandler);
}
BOOL CSynchronousExceptionManager::RemoveHandlerInfo
(
CHandlerInfo *pHandlerInfo,
DWORD dwArg1, // CExceptionHandler *
DWORD dwArg2, // Not used
DWORD dwArg3 // Not used
)
{
BOOL fKeepOnList = TRUE;
if( pHandlerInfo->GetHandler() == (CExceptionHandler *)dwArg1 )
{
delete pHandlerInfo;
fKeepOnList = FALSE;
}
return(fKeepOnList);
}
void CSynchronousExceptionManager::NotifyExceptionHandler
(
CExceptionHandler *pHandler,
CExceptionHandler::NOTIFICATION_CONTEXT Context,
DWORD dwExceptionCode
)
{
CSynchronousExceptionManager::ForEachHandler(ScheduleAPC, (DWORD)pHandler, (DWORD)Context, dwExceptionCode);
}
BOOL CSynchronousExceptionManager::ScheduleAPC
(
CHandlerInfo *pHandlerInfo,
DWORD dwArg1, // CExceptionHandler *pHandler
DWORD dwArg2, // CExceptionHandler::NOTIFICATION_CONTEXT Context
DWORD dwArg3 // Exception code
)
{
// This function is called indirectly by NotifyExceptionHandler for each handler info
// element on the list of registered handlers. But since the base class is already
// iterating over the list of registered handlers, we only want to schedule an APC
// to the handler that matches the one specified by dwArg1.
//
if( pHandlerInfo->GetHandler() != (CExceptionHandler *)dwArg1 )
{
// Skip this handler, keeping him on the list.
//
return(TRUE);
}
// If the handler specified by dwArg1 is the handler for the current
// thread, we just call directly to the OnException function. Otherwise,
// the call to SuspendThread will suspend this thread, and we'll never
// continue executing.
//
if( pHandlerInfo->GetThreadId() != GetCurrentThreadId() )
{
HANDLE hThread = pHandlerInfo->GetThreadHandle();
printf(
"[tid 0x%08lx] Scheduling APC to handler 0x%x, ctx = %s, ecode = 0x%x\n",
GetCurrentThreadId(),
pHandlerInfo->GetHandler(),
(dwArg2 == CExceptionHandler::THREAD_EXCEPTION ? "thread" : "unhandled"),
dwArg3
);
if( SuspendThread(hThread) != 0xffffffff )
{
CONTEXT ThreadContext;
BOOL fSetThreadContext = FALSE;
memset(&ThreadContext, 0, sizeof(ThreadContext));
ThreadContext.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;
if( GetThreadContext(hThread, &ThreadContext) )
{
ThreadContext.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;
// Warning: begin Intel architecture specific code
//
ThreadContext.Eip = (DWORD)APCCallback;
//
// Warning: end of Intel architecture specific code
// Information about the exception is be passed to the APC
// routine (APCCallback) through the following three static class
// member variables.
//
s_APCHandler = (CExceptionHandler *)dwArg1;
s_APCContext = (CExceptionHandler::NOTIFICATION_CONTEXT)dwArg2;
s_APCExceptionCode = dwArg3;
fSetThreadContext = SetThreadContext(hThread, &ThreadContext);
}
ResumeThread(hThread);
if( fSetThreadContext )
{
// Only bother waiting for the thread to exit if we successfully
// altered its context. As soon as the thread executes user mode
// code again, it's context will be set to the start of the
// APCCallback function. Note that even if we timeout waiting for
// that to occur, it can still happen between the time that we timeout
// and ExitProcess is called.
//
WaitForSingleObject(hThread, 5000);
}
}
}
else
{
((CExceptionHandler *)dwArg1)->OnException((CExceptionHandler::NOTIFICATION_CONTEXT)dwArg2, dwArg3);
}
// Since the objective of this routine is to schedule an APC to the target
// thread that causes it to execute its cleanup function that then automatically
// call ExitThread, we don't need to keep any handlers in the list. This is
// true whether or not we succeed in altering that thread's context. When this
// function returns FALSE, the handler info structure is removed from the list
// and deleted, which causes the thread handle to be closed.
//
return(FALSE);
}
void CSynchronousExceptionManager::ForEachHandler( PHANDLERINFO_ITERATOR pfxnIterator, DWORD dwArg1, DWORD dwArg2, DWORD dwArg3 )
{
CMclLinkedList<CHandlerInfo *> Handlers;
CHandlerInfo *pHandlerInfo = 0;
while( m_HandlerInfo.GetFromHeadOfList(pHandlerInfo, 0) )
{
if( pfxnIterator(pHandlerInfo, dwArg1, dwArg2, dwArg3) )
{
// Keep this handler on the list of registered handlers.
//
Handlers.PutOnTailOfList(pHandlerInfo);
}
}
while( Handlers.GetFromHeadOfList(pHandlerInfo, 0) )
{
m_HandlerInfo.PutOnTailOfList(pHandlerInfo);
}
}
// APCCallback
//
// This static class member function is invoked in the context of the thread
// that is being notified of an exception in another thread. The parameters
// to this function are passed to this function by the ScheduleAPC function
// using three class static member variables. This function then performs
// the call to the virtual OnException function that is implemented by the
// object being notified of the exception. After the thread has been notified,
// it is terminated with an exit code of -1.
//
void _cdecl CSynchronousExceptionManager::APCCallback( void )
{
printf(
"[tid 0x%08lx] APCCallback executing for handler 0x%x, ctx = %x, ecode = 0x%x\n",
GetCurrentThreadId(),
s_APCHandler,
(s_APCContext == CExceptionHandler::THREAD_EXCEPTION ? "thread" : "unhandled"),
s_APCExceptionCode
);
if( s_APCHandler )
{
s_APCHandler->OnException(s_APCContext, s_APCExceptionCode);
}
ExitThread(0xffffffff);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -