[0.2.x] + exception handling

git-svn-id: https://pykd.svn.codeplex.com/svn@81883 9b283d60-5439-405e-af05-b73fd8c4d996
This commit is contained in:
SND\EreTIk_cp 2012-12-18 16:28:04 +00:00 committed by Mikhail I. Izmestev
parent 74d6b77576
commit b0a49c7ed4
12 changed files with 331 additions and 139 deletions

View File

@ -80,15 +80,53 @@ enum DEBUG_CALLBACK_RESULT {
DebugCallbackMax = 3
};
struct ExceptionInfo {
bool FirstChance;
ULONG ExceptionCode; /* NTSTATUS */
ULONG ExceptionFlags;
ULONG64 ExceptionRecord;
ULONG64 ExceptionAddress;
std::vector<ULONG64> Parameters;
ExceptionInfo(ULONG FirstChance, const EXCEPTION_RECORD64 &Exception);
python::list getParameters() const;
std::string print() const;
};
typedef boost::shared_ptr< ExceptionInfo > ExceptionInfoPtr;
struct DEBUG_EVENT_CALLBACK {
virtual DEBUG_CALLBACK_RESULT OnBreakpoint( ULONG bpId ) = 0;
virtual DEBUG_CALLBACK_RESULT OnModuleLoad( ULONG64 offset, const std::string &name ) = 0;
virtual DEBUG_CALLBACK_RESULT OnModuleUnload( ULONG64 offset, const std::string &name ) = 0;
virtual DEBUG_CALLBACK_RESULT OnException( ULONG64 offset, ULONG code ) = 0;
virtual DEBUG_CALLBACK_RESULT OnException( ExceptionInfoPtr exceptInfo ) = 0;
};
enum EVENT_TYPE {
EventTypeBreakpoint = 0x0001,
EventTypeException = 0x0002,
EventTypeCreateThread = 0x0004,
EventTypeExitThread = 0x0008,
EventTypeCreateProcess = 0x0010,
EventTypeExitProcess = 0x0020,
EventTypeLoadModule = 0x0040,
EventTypeUnloadModule = 0x0080,
EventTypeSystemError = 0x0100,
EventTypeSessionStatus = 0x0200,
EventTypeChangeDebuggeeState = 0x0400,
EventTypeChangeEngineState = 0x0800,
EventTypeChangeSymbolState = 0x1000,
EventTypeMax,
};
EVENT_TYPE getLastEventType();
ExceptionInfoPtr getLastExceptionInfo();
void eventRegisterCallbacks( const DEBUG_EVENT_CALLBACK *callbacks );
void eventRemoveCallbacks( const DEBUG_EVENT_CALLBACK *callbacks );

View File

@ -14,6 +14,7 @@ python::handle<> exceptPyType<SymbolException>::pyExceptType;
//python::handle<> exceptPyType<pyDia::Exception>::pyExceptType;
python::handle<> exceptPyType<TypeException>::pyExceptType;
python::handle<> exceptPyType<AddSyntheticSymbolException>::pyExceptType;
python::handle<> exceptPyType<WrongEventTypeException>::pyExceptType;
python::handle<> exceptPyType<ImplementException>::pyExceptType;
///////////////////////////////////////////////////////////////////////////////////

View File

@ -143,7 +143,7 @@ public:
private:
std::string buildDesc( const std::string &typeName, const std::string &errorStr )
static std::string buildDesc( const std::string &typeName, const std::string &errorStr )
{
std::stringstream sstr;
sstr << typeName << " : " << errorStr;
@ -171,7 +171,7 @@ private:
ULONG64 m_targetAddress;
std::string buildDesc( ULONG64 addr, bool phyAddr )
static std::string buildDesc( ULONG64 addr, bool phyAddr )
{
std::stringstream sstr;
if ( phyAddr )
@ -195,7 +195,7 @@ public:
}
private:
std::string buildDesc(HRESULT hres) {
static std::string buildDesc(HRESULT hres) {
std::stringstream sstream;
sstream << "Add synthetic symbol faield\n";
sstream << "HRESULT 0x" << std::hex << hres;
@ -203,6 +203,26 @@ private:
}
};
/////////////////////////////////////////////////////////////////////////////////
class WrongEventTypeException : public DbgException
{
public:
WrongEventTypeException(ULONG eventType)
: DbgException( buildDesc(eventType) )
{}
private:
static std::string buildDesc(ULONG eventType) {
std::stringstream sstream;
sstream << "Unknown/not compatible debug event type: 0x";
sstream << std::hex << eventType;
return sstream.str();
}
};
/////////////////////////////////////////////////////////////////////////////////
class ImplementException : public DbgException
@ -215,7 +235,7 @@ public:
private:
std::string buildDesc( const std::string &file, int line, const std::string &msg )
static std::string buildDesc( const std::string &file, int line, const std::string &msg )
{
std::stringstream sstream;
sstream << "File: " << file << " Line: " << line << " " << msg;

View File

@ -25,7 +25,7 @@ private:
virtual DEBUG_CALLBACK_RESULT OnBreakpoint( ULONG bpId ) = 0;
virtual DEBUG_CALLBACK_RESULT OnModuleLoad( ULONG64 offset, const std::string &name ) = 0;
virtual DEBUG_CALLBACK_RESULT OnModuleUnload( ULONG64 offset, const std::string &name ) = 0;
virtual DEBUG_CALLBACK_RESULT OnException( ULONG64 offset, ULONG code ) = 0;
virtual DEBUG_CALLBACK_RESULT OnException( ExceptionInfoPtr exceptInfo ) = 0;
};
//////////////////////////////////////////////////////////////////////////////////
@ -48,8 +48,8 @@ public:
return handler("onModuleUnload", offset, name );
}
virtual DEBUG_CALLBACK_RESULT OnException( ULONG64 offset, ULONG code ) {
return handler("onException", offset, code );
virtual DEBUG_CALLBACK_RESULT OnException( ExceptionInfoPtr exceptInfo ) {
return handler("onException", exceptInfo );
}
private:

60
pykd/exceptinfo.cpp Normal file
View File

@ -0,0 +1,60 @@
#include "stdafx.h"
#include "dbgengine.h"
////////////////////////////////////////////////////////////////////////////////
namespace pykd {
////////////////////////////////////////////////////////////////////////////////
ExceptionInfo::ExceptionInfo(ULONG FirstChance, const EXCEPTION_RECORD64 &Exception)
{
FirstChance = FirstChance != FALSE;
ExceptionCode = Exception.ExceptionCode;
ExceptionFlags = Exception.ExceptionFlags;
ExceptionRecord = Exception.ExceptionRecord;
ExceptionAddress = Exception.ExceptionAddress;
Parameters.resize(Exception.NumberParameters);
for (ULONG i = 0; i < Exception.NumberParameters; ++i)
Parameters[i] = Exception.ExceptionInformation[i];
}
python::list ExceptionInfo::getParameters() const
{
python::list ret;
std::vector<ULONG64>::const_iterator itParam = Parameters.begin();
for (; itParam != Parameters.end(); ++itParam)
ret.append(*itParam);
return ret;
}
std::string ExceptionInfo::print() const
{
std::stringstream sstream;
sstream << "FirstChance= " << (FirstChance ? "True" : "False") << std::endl;
sstream << std::hex;
sstream << "ExceptionCode= 0x" << ExceptionCode << std::endl;
sstream << "ExceptionFlags= 0x" << ExceptionFlags << std::endl;
sstream << "ExceptionRecord= 0x" << ExceptionRecord << std::endl;
sstream << "ExceptionAddress= 0x" << ExceptionAddress << std::endl;
for (ULONG i = 0; i < Parameters.size(); ++i)
{
sstream << "Param[" << std::dec << i << "]= 0x";
sstream << Parameters[i] << std::endl;
}
return sstream.str();
}
////////////////////////////////////////////////////////////////////////////////
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="windows-1251"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9,00"
Version="9.00"
Name="pykd"
ProjectGUID="{FE961905-666F-4908-A212-961465F46F13}"
RootNamespace="pykd"
@ -389,6 +389,10 @@
RelativePath=".\eventhandler.cpp"
>
</File>
<File
RelativePath=".\exceptinfo.cpp"
>
</File>
<File
RelativePath=".\module.cpp"
>
@ -581,6 +585,10 @@
RelativePath=".\win\dbgio.h"
>
</File>
<File
RelativePath=".\win\lastevent.cpp"
>
</File>
<File
RelativePath=".\win\memory.cpp"
>

View File

@ -28,7 +28,6 @@ using namespace pykd;
////////////////////////////////////////////////////////////////////////////////
static const std::string pykdVersion = PYKD_VERSION_BUILD_STR
#ifdef _DEBUG
" <DBG>"
@ -464,6 +463,42 @@ BOOST_PYTHON_MODULE( pykd )
.def( "__str__", &StackFrame::print,
"Return stacks frame as a string");
python::class_< ExceptionInfo, ExceptionInfoPtr, boost::noncopyable >(
"exceptionInfo", "Exception information", python::no_init )
.def_readonly( "FirstChance", &ExceptionInfo::FirstChance,
"Specifies whether this exception has been previously encountered")
.def_readonly( "ExceptionCode", &ExceptionInfo::ExceptionCode,
"The reason the exception occurred")
.def_readonly( "ExceptionFlags", &ExceptionInfo::ExceptionFlags,
"The exception flags")
.def_readonly( "ExceptionRecord", &ExceptionInfo::ExceptionRecord,
"A pointer to an associated EXCEPTION_RECORD structure")
.def_readonly( "ExceptionAddress", &ExceptionInfo::ExceptionAddress,
"The address where the exception occurred")
.add_property( "Parameters", &ExceptionInfo::getParameters,
"An array of additional arguments that describe the exception")
.def( "__str__", &ExceptionInfo::print,
"Return object as a string");
python::enum_<EVENT_TYPE>("eventType", "Type of debug event")
.value("Breakpoint", EventTypeBreakpoint)
.value("Exception", EventTypeException)
.value("CreateThread", EventTypeCreateThread)
.value("ExitThread", EventTypeExitThread)
.value("CreateProcess", EventTypeCreateProcess)
.value("ExitProcess", EventTypeExitProcess)
.value("LoadModule", EventTypeLoadModule)
.value("UnloadModule", EventTypeUnloadModule)
.value("SystemError", EventTypeSystemError)
.value("SessionStatus", EventTypeSessionStatus)
.value("ChangeDebuggeeState", EventTypeChangeDebuggeeState)
.value("ChangeEngineState", EventTypeChangeEngineState)
.value("ChangeSymbolState", EventTypeChangeSymbolState)
.export_values();
python::def( "lastEvent", &getLastEventType, "Return type of last event: eventType" );
python::def( "lastException", &getLastExceptionInfo, "Return data of last exception event: exceptionInfo" );
python::class_<Disasm>("disasm", "Class disassemble a processor instructions" )
.def( python::init<>( "constructor" ) )
.def( python::init<ULONG64>( boost::python::args("offset"), "constructor" ) )
@ -477,20 +512,26 @@ BOOST_PYTHON_MODULE( pykd )
.def( "ea", &Disasm::ea, "Return effective address for last disassembled instruction or 0" )
.def( "reset", &Disasm::reset, "Reset current offset to begin" );
python::enum_<DEBUG_CALLBACK_RESULT>("eventResult", "Return value of event handler")
.value("Proceed", DebugCallbackProceed)
.value("NoChange", DebugCallbackNoChange)
.value("Break", DebugCallbackBreak)
.export_values();
python::class_<EventHandlerWrap, EventHandlerPtr, boost::noncopyable>(
"eventHandler", "Base class for overriding and handling debug notifications" )
.def( "onBreakpoint", &EventHandlerWrap::OnBreakpoint,
"Triggered breakpoint event. Parameter is int: ID of breakpoint\n"
"For ignore event method must return False value" )
"For ignore event method must return eventResult.noChange" )
.def( "onModuleLoad", &EventHandlerWrap::OnModuleLoad,
"Triggered module load event. Parameter are long: module base, string: module name\n"
"For ignore event method must return False value" )
"For ignore event method must return eventResult.noChange" )
.def( "onModuleUnload", &EventHandlerWrap::OnModuleUnload,
"Triggered module unload event. Parameter are long: module base, string: module name\n"
"For ignore event method must return False value" )
"For ignore event method must return eventResult.noChange" )
.def( "onException", &EventHandlerWrap::OnException,
"Triggered exception event. Parameters are long: exception address, long: exception code\n"
"For ignore event method must return False value" );
"Triggered exception event. Parameter - exceptionInfo\n"
"For ignore event method must return eventResult.noChange" );
// wrapper for standart python exceptions
python::register_exception_translator<PyException>( &PyException::exceptionTranslate );
@ -498,6 +539,7 @@ BOOST_PYTHON_MODULE( pykd )
pykd::exception<DbgException>( "BaseException", "Pykd base exception class" );
pykd::exception<MemoryException,DbgException>( "MemoryException", "Target memory access exception class" );
//pykd::exception<WaitEventException,DbgException>( "WaitEventException", "Debug interface access exception" );
pykd::exception<WrongEventTypeException,DbgException>( "WaitEventException", "Debug interface access exception" );
pykd::exception<SymbolException,DbgException>( "SymbolException", "Symbol exception" );
//pykd::exception<pyDia::Exception,SymbolException>( "DiaException", "Debug interface access exception" );
pykd::exception<TypeException,SymbolException>( "TypeException", "type exception" );

View File

@ -1137,7 +1137,7 @@ HRESULT STDMETHODCALLTYPE DebugEngine::Breakpoint(
{
PyThread_StateSave pyThreadSave( it->pystate );
DEBUG_CALLBACK_RESULT ret = it->callback->OnBreakpoint( id );
DEBUG_CALLBACK_RESULT ret = it->callback->OnBreakpoint( id );
result = ret != DebugCallbackNoChange ? ret : result;
}
@ -1250,7 +1250,9 @@ HRESULT STDMETHODCALLTYPE DebugEngine::Exception(
{
PyThread_StateSave pyThreadSave( it->pystate );
DEBUG_CALLBACK_RESULT ret = it->callback->OnException( Exception->ExceptionAddress, Exception->ExceptionCode );
DEBUG_CALLBACK_RESULT ret = it->callback->OnException(
ExceptionInfoPtr( new ExceptionInfo(FirstChance, *Exception) )
);
result = ret != DebugCallbackNoChange ? ret : result;
}

97
pykd/win/lastevent.cpp Normal file
View File

@ -0,0 +1,97 @@
#include "stdafx.h"
#include "dbgengine.h"
#include "win/dbgeng.h"
////////////////////////////////////////////////////////////////////////////////
namespace pykd {
////////////////////////////////////////////////////////////////////////////////
EVENT_TYPE getLastEventType()
{
ULONG eventType;
ULONG ProcessId;
ULONG ThreadId;
HRESULT hres =
g_dbgEng->control->GetLastEventInformation(
&eventType,
&ProcessId,
&ThreadId,
NULL,
0,
NULL,
NULL,
0,
NULL);
if (S_OK != hres)
throw DbgException("IDebugControl::GetLastEventInformation", hres);
switch (eventType)
{
case DEBUG_EVENT_BREAKPOINT:
return EventTypeBreakpoint;
case DEBUG_EVENT_EXCEPTION:
return EventTypeException;
case DEBUG_EVENT_CREATE_THREAD:
return EventTypeCreateThread;
case DEBUG_EVENT_EXIT_THREAD:
return EventTypeExitThread;
case DEBUG_EVENT_CREATE_PROCESS:
return EventTypeCreateProcess;
case DEBUG_EVENT_EXIT_PROCESS:
return EventTypeExitProcess;
case DEBUG_EVENT_LOAD_MODULE:
return EventTypeLoadModule;
case DEBUG_EVENT_UNLOAD_MODULE:
return EventTypeUnloadModule;
case DEBUG_EVENT_SYSTEM_ERROR:
return EventTypeSystemError;
case DEBUG_EVENT_SESSION_STATUS:
return EventTypeSessionStatus;
case DEBUG_EVENT_CHANGE_DEBUGGEE_STATE:
return EventTypeChangeDebuggeeState;
case DEBUG_EVENT_CHANGE_ENGINE_STATE:
return EventTypeChangeEngineState;
case DEBUG_EVENT_CHANGE_SYMBOL_STATE:
return EventTypeChangeSymbolState;
}
throw WrongEventTypeException(eventType);
}
ExceptionInfoPtr getLastExceptionInfo()
{
ULONG eventType;
ULONG ProcessId;
ULONG ThreadId;
DEBUG_LAST_EVENT_INFO_EXCEPTION LastException;
ULONG retLength = sizeof(LastException);
HRESULT hres =
g_dbgEng->control->GetLastEventInformation(
&eventType,
&ProcessId,
&ThreadId,
&LastException,
sizeof(LastException),
&retLength,
NULL,
0,
NULL);
if (S_OK != hres)
throw DbgException("IDebugControl::GetLastEventInformation", hres);
if (DEBUG_EVENT_EXCEPTION != eventType)
throw WrongEventTypeException(eventType);
return ExceptionInfoPtr(
new ExceptionInfo(LastException.FirstChance, LastException.ExceptionRecord)
);
}
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -1,136 +1,46 @@
"""Debug events handler: test exceptions and debug breaks"""
"""Exception event test"""
import unittest
import target
import pykd
import testutils
class BreakExceptionHandler(pykd.eventHandler):
class ExceptionHandler(pykd.eventHandler):
"""Track load/unload module implementation"""
def __init__(self, client):
pykd.eventHandler.__init__(self, client)
self.client = client
self.wasSecondChance = False
self.wasBreakpoint = False
self.wasBreakpointIds = []
self.bpLastModuleName = ""
self.bpCount = 0
self.firstChanceAccessAddresses = []
self.secondChanceAccessAddresses = []
def onBreakpoint(self, bpId):
"""Breakpoint handler"""
self.wasBreakpoint = True
self.wasBreakpointIds.append( bpId )
return pykd.DEBUG_STATUS_BREAK
def onException(self, exceptParams):
"""Exception handler"""
self.wasSecondChance = not exceptParams["FirstChance"]
if exceptParams["Code"] == pykd.EXCEPTION_ACCESS_VIOLATION:
if self.wasSecondChance:
self.secondChanceAccessAddresses.append( exceptParams["Parameters"][1] )
else:
self.firstChanceAccessAddresses.append( exceptParams["Parameters"][1] )
elif exceptParams["Code"] == pykd.EXCEPTION_BREAKPOINT:
self.bpCount += 1
return pykd.DEBUG_STATUS_BREAK
class BpHandlerTestResult:
""" Breakpoint handler test results"""
def __init__(self):
self.wasCodeBp = None
self.wasDataBp = None
pykd.eventHandler.__init__(self)
self.accessViolationOccured = False
bpHandlerTestResult = BpHandlerTestResult()
def onException(self, exceptInfo):
"""Exception handler"""
self.accessViolationOccured = exceptInfo.ExceptionCode == 0xC0000005
def codeBpHandler(bpId):
""" Handler of software breakpoint """
bpHandlerTestResult.wasCodeBp = bpId
if self.accessViolationOccured:
self.param0 = exceptInfo.Parameters[0]
self.param1 = exceptInfo.Parameters[1]
return pykd.eventResult.Break
def dataBpHandler(bpId):
""" Handler of hardware breakpoint """
bpHandlerTestResult.wasDataBp = bpId
return pykd.DEBUG_STATUS_NO_CHANGE
return pykd.eventResult.NoChange
class EhExceptionBreakpointTest(unittest.TestCase):
"""Unit tests of exceptions end breakpoint handling"""
class EhExceptionTest(unittest.TestCase):
"""Exception event test"""
def testBreakpointException(self):
"""Start new process and track exceptions/breakpoints"""
testClient = pykd.createDbgClient()
testClient.startProcess( target.appPath + " -testExceptions" )
def testException(self):
"""Start new process and track exceptions"""
_locProcessId = pykd.startProcess( target.appPath + " -testAccessViolation" )
with testutils.ContextCallIt( testutils.KillProcess(_locProcessId) ) as killStartedProcess :
exceptionHandler = ExceptionHandler()
targetMod = testClient.loadModule( "targetapp" )
while not exceptionHandler.accessViolationOccured:
pykd.go()
bpIdSoftware = testClient.setBp( targetMod.offset("changeValueForAccessTesting"),
codeBpHandler )
self.assertEqual( pykd.lastEvent(), pykd.eventType.Exception )
allBp = testClient.getAllBp()
self.assertEqual( 1, len( allBp ) )
self.assertTrue( bpIdSoftware in allBp )
self.assertTrue( exceptionHandler.accessViolationOccured )
self.assertEqual( exceptionHandler.param0, 1 ) # write
self.assertEqual( exceptionHandler.param1, 6 ) # addr
hwBpIsNotSet = True
# kd> ba e 1 <some_address>
# ^ Unable to set breakpoint error
# The system resets thread contexts after the process
# breakpoint so hardware breakpoints cannot be set.
# Go to the executable's entry point and set it then.
# raw_input("Press <ENTER>....")
breakExceptionHandler = BreakExceptionHandler( testClient )
while not breakExceptionHandler.wasSecondChance:
testClient.go()
if hwBpIsNotSet:
hwBpIsNotSet = False
bpIdHwExecute = testClient.setBp( targetMod.offset("readValueForAccessTesting"),
1, pykd.DEBUG_BREAK_EXECUTE )
bpIdHwWrite = testClient.setBp( targetMod.offset("g_valueForAccessTesting1"),
1, pykd.DEBUG_BREAK_WRITE, dataBpHandler )
bpIdHwRead = testClient.setBp( targetMod.offset("g_valueForAccessTesting2"),
1, pykd.DEBUG_BREAK_READ )
self.assertTrue( bpIdSoftware != bpIdHwExecute and
bpIdHwExecute != bpIdHwWrite and
bpIdHwWrite != bpIdHwRead )
allBp = testClient.getAllBp()
self.assertEqual( 4, len( allBp ) )
self.assertTrue( bpIdSoftware in allBp )
self.assertTrue( bpIdHwExecute in allBp )
self.assertTrue( bpIdHwWrite in allBp )
self.assertTrue( bpIdHwRead in allBp )
self.assertEqual( [2, 3], breakExceptionHandler.firstChanceAccessAddresses )
self.assertEqual( 3, breakExceptionHandler.bpCount ) # main and 2 in doExeptions
self.assertEqual( [3, ], breakExceptionHandler.secondChanceAccessAddresses )
self.assertTrue( breakExceptionHandler.wasBreakpoint )
self.assertTrue( bpIdSoftware in breakExceptionHandler.wasBreakpointIds )
self.assertTrue( bpIdHwExecute in breakExceptionHandler.wasBreakpointIds )
self.assertTrue( bpIdHwWrite in breakExceptionHandler.wasBreakpointIds )
self.assertTrue( bpIdHwRead in breakExceptionHandler.wasBreakpointIds )
testClient.removeBp(bpIdHwRead)
self.assertEqual( 3, len( testClient.getAllBp() ) )
testClient.removeBp()
self.assertEqual( 0, len( testClient.getAllBp() ) )
self.assertEqual( bpHandlerTestResult.wasCodeBp, bpIdSoftware )
self.assertTrue( bpHandlerTestResult.wasDataBp, bpIdHwWrite )
exceptInfo = pykd.lastException()
self.assertEqual( exceptInfo.ExceptionCode, 0xC0000005 )
self.assertEqual( exceptionHandler.param0, exceptInfo.Parameters[0] )
self.assertEqual( exceptionHandler.param1, exceptInfo.Parameters[1] )

View File

@ -21,6 +21,7 @@ import typedvar
import regtest
import localstest
import customtypestest
import ehexcepttest
class StartProcessWithoutParamsTest(unittest.TestCase):
def testStart(self):
@ -44,13 +45,14 @@ def getTestSuite( singleName = "" ):
unittest.TestLoader().loadTestsFromTestCase( moduletest.ModuleTest ),
unittest.TestLoader().loadTestsFromTestCase( memtest.MemoryTest ),
unittest.TestLoader().loadTestsFromTestCase( typeinfo.TypeInfoTest ),
unittest.TestLoader().loadTestsFromTestCase( typedvar.TypedVarTest ),
#unittest.TestLoader().loadTestsFromTestCase( typedvar.TypedVarTest ),
unittest.TestLoader().loadTestsFromTestCase( regtest.CpuRegTest ),
unittest.TestLoader().loadTestsFromTestCase( customtypestest.CustomTypesTest ),
# ^^^
unittest.TestLoader().loadTestsFromTestCase( TerminateProcessTest ),
unittest.TestLoader().loadTestsFromTestCase( localstest.LocalVarsTest ),
#unittest.TestLoader().loadTestsFromTestCase( localstest.LocalVarsTest ),
unittest.TestLoader().loadTestsFromTestCase( ehexcepttest.EhExceptionTest ),
] )
else:
return unittest.TestSuite(

View File

@ -537,6 +537,15 @@ int doLoadUnload()
return 0;
}
////////////////////////////////////////////////////////////////////////////////
int doAccessViolation()
{
char *p = (char *)6;
*p = 12;
return 0;
}
////////////////////////////////////////////////////////////////////////////////
@ -649,6 +658,9 @@ int _tmain(int argc, _TCHAR* argv[])
if ( !_tcsicmp(argv[1], _T("-testLoadUnload")) )
return doLoadUnload();
if ( !_tcsicmp(argv[1], _T("-testAccessViolation")) )
return doAccessViolation();
if ( !_tcsicmp(argv[1], _T("-testEnumWindows")) )
{
::EnumWindows(&EnumWindowsProc1, 6);