c语言寻找大富翁,异常处理 (4)

异常处理 (4)

发布时间:2010-04-11 |

c9607458a56ac80898a5b3912f6a4792.gif

1楼: 关于异常处理使用的SEH链实现异常嵌套.

|------------|

线程信息块 |-------------| | ... |

|-------------| | 异常处理1 |--->| pop fs:[0] |

|----->|ExceptionList|---->| |-------------| | pop eax |

| |-------------| | |--->| FFFFFFFF | | ret |

| | ... | | | |-------------| |------------|

| |-------------| | | | ... |

| | | | | |-------------|

| |-------------| | | | | |------------|

|| pop fs:[0] |

|-------------| | | | |-------------| | pop eax |

| ... | | | ||-------------| |------------|

|------>| 段描述符 |->| | | | ... |

| |-------------| | | |-------------|

| | | | | | | |------------|

| |-------------| | | |-------------| | ... |

| (LDT) | | | 异常处理 |--->| pop fs:[0] |

| | | |-------------| | pop eax |

| | |----|-------------| |------------|

| | | | ... |

| | | |-------------|

|-----------| | | | | |------------|

| fs 寄存器| | | |-------------| | ... |

|-----------| | | | 异常处理 |--->| pop fs:[0] |

| | |-------------| | pop eax |

| ||-------------| |------------|

| |

|-------------|

字串6 2楼: 大侠给解释一下啦! 字串8 3楼: 最简单的例子, 来自 http://www.microsoft.com/msj/0197/exception/exception.aspx

//==================================================

// MYSEH - Matt Pietrek 1997

// Microsoft Systems Journal, January 1997

// File: MySEH.dpr

// To compile: Delphi 7.0

//==================================================

program MySEH;

{$AppType Console}

uses

Windows, SysUtils;

type

TExceptionDisposition = (

ExceptionContinueExecution = 0,

ExceptionContinueSearch = 1,

ExceptionNestedException = 2,

ExceptionCollidedUnwind = 3 );

var

scratch: DWORD;

// 异常回调

function FilterFunc(ExceptionRecord: PExceptionRecord; EstablisherFrame: Pointer;

ContextRecord: PContext; DispatcherContext: Pointer): TExceptionDisposition; Cdecl;

begin

// 看到这句说明进入了异常回调

Writeln(‘Hello from an exception handler‘);

// 修改线程的EAX为一个合法地址

ContextRecord.Eax := DWORD(@scratch);

// 要求重新执行导致异常的指令

Result := ExceptionContinueExecution;

end;

// 程序入口

var

Handler: DWORD;

begin

Handler := DWORD(@FilterFunc);

asm

PUSH Handler // 异常回调地址

PUSH FS:[0] // 上一节点地址

MOV FS:[0], ESP // 插入链表表头

end;

asm

MOV EAX , 0 // 清空EAX寄存器

MOV [EAX], 1 // 访问[EAX]处,这将引发一个异常

end;

Writeln(‘After writing!‘); // 写入成功

asm

MOV EAX, [ESP] // 上一节点地址

MOV FS:[0], EAX // 设为链表表头

ADD ESP, 8 // 恢复堆栈指针

end;

Readln;

end.

字串1 4楼: 下面是《Windows 核心编程》中的一个例子,利用异常机制恢复线程堆栈,我按照SEH的原理用PASCAL写了一遍,测试通过~~

// 1.工程文件 Summation.dpr :

program Summation;

{$R ‘Summation.res‘ ‘Summation.rc‘}

uses

Windows, Messages;

const

IDD_SUMMATION = 101; // 资源ID

IDI_SUMMATION = 102;

IDC_SUMNUM = 1000; // 控件ID

IDC_CALC = 1001;

IDC_ANSWER = 1002;

UINT_MAX = DWORD(-1); // 非法标识

type

// 异常回调返回值

TExceptionDisposition = (

ExceptionContinueExecution = 0, // 继续执行遇到异常的线程(回调已经作了修复工作)

ExceptionContinueSearch = 1, // 回调未作处理, 请在链表上继续寻找其他回调

ExceptionNestedException = 2,

ExceptionCollidedUnwind = 3

);

// 标准异常结构体

PExceptionRegistration = ^TExceptionRegistration;

TExceptionRegistration = record

PrevStruct: PExceptionRegistration; // 上一节点位置

ExceptionHandler: Pointer; // 异常回调地址

end;

// 扩展异常结构体

PExcFrame = ^TExcFrame;

TExcFrame = record

PStruct: PExcFrame; // 上一节点位置

Handler: Pointer; // 异常回调地址

SafeEip: Pointer; // 安全指令地址

end;

// 递归求和

function Sum(uNum: UINT): UINT;

begin

if (uNum = 0) then Result := 0 else Result := uNum + Sum(uNum - 1);

end;

// 异常回调

function ExceptHandler(var ExceptionRecord: TExceptionRecord; var EstablisherFrame: TExcFrame;

var ContextRecord: TContext; DispatcherContext: Pointer): TExceptionDisposition; Cdecl;

begin

if (ExceptionRecord.ExceptionCode = STATUS_STACK_OVERFLOW) then

begin

ContextRecord.Eax := UINT_MAX; // 线程函数返回‘错误标识‘

ContextRecord.Eip := DWORD(EstablisherFrame.SafeEip); // 跳到‘Call Sum‘指令之后执行

ContextRecord.Esp := DWORD(@EstablisherFrame); // 恢复栈顶为执行‘Call Sum‘前的位置

Result := ExceptionContinueExecution;

end else

Result := ExceptionContinueSearch;

end;

// 线程函数

function SumThreadFunc(uSumNum: UINT): DWORD;

asm

// 在堆栈中构建异常结构

PUSH OFFSET @@SafeEip // TExcFrame.SafeEip

PUSH OFFSET ExceptHandler // TExcFrame.Handler

PUSH FS:[0] // TExcFrame.PStruct

// 将该结构插入链表首部

MOV FS:[0], ESP // FS:[0]乃TIB.ExceptionList

// 参数/返回值均在EAX中

CALL Sum

// 遇到异常时的跳转位置

@@SafeEip:

// 从链表摘除我们的结构

MOV EDX , [ESP] // 上个节点地址

MOV FS:[0], EDX // 设其为首节点

// 清除异常结构占用堆栈

ADD ESP , 12 // 修改栈顶指针

end;

// 对话框WM_INITDIALOG消息处理

function Dlg_OnInitDialog(hWnd, hWndFocus: HWND; lParam: LPARAM): BOOL;

begin

// 设置窗口图标

SendMessage(hWnd, WM_SETICON, ICON_BIG, LoadIcon(HInstance, MakeIntResource(IDI_SUMMATION)));

SendMessage(hWnd, WM_SETICON, ICON_SMALL, LoadIcon(HInstance, MakeIntResource(IDI_SUMMATION)));

// 限制输入长度

SendMessage(GetDlgItem(hWnd, IDC_SUMNUM), EM_LIMITTEXT, 9, 0);

// 接受默认焦点

Result := TRUE;

end;

// 对话框WM_COMMAND消息处理

procedure Dlg_OnCommand(hWnd: HWND; id: Integer; hWndCtl: HWND; codeNotify: UINT);

var

dwThreadId: DWORD;

uSum: UINT;

hThread: THandle;

begin

case (id) of

IDCANCEL:

begin

EndDialog(hWnd, id);

end;

IDC_CALC:

begin

// 取得输入数值

uSum := GetDlgItemInt(hWnd, IDC_SUMNUM, PBOOL(nil)^, FALSE);

// 建立计算线程

hThread := BeginThread(nil, 0, @SumThreadFunc, Pointer(uSum), 0, dwThreadId);

// 等待线程结束

WaitForSingleObject(hThread, INFINITE);

// 线程退出代码 (等于线程函数返回值)

GetExitCodeThread(hThread, uSum);

// 关闭内核对象

CloseHandle(hThread);

// 堆栈是否溢出

if (uSum = UINT_MAX) then

begin

SetDlgItemText(hWnd, IDC_ANSWER, ‘Error‘);

MessageBox(0, ‘The number is too big, please enter a smaller number‘, ‘Summation‘, MB_OK);

end else

SetDlgItemInt(hWnd, IDC_ANSWER, uSum, FALSE);

end;

end;

end;

// 对话框回调

function Dlg_Proc(hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): BOOL; stdcall;

begin

case (uMsg) of

WM_INITDIALOG:

begin

Result := BOOL(SetWindowLong(hWnd,

DWL_MSGRESULT, Longint(Dlg_OnInitDialog(hWnd, wParam, lParam))));

end;

WM_COMMAND:

begin

Dlg_OnCommand(hWnd, LOWORD(wParam), lParam, HIWORD(wParam));

Result := TRUE;

end;

else

Result := FALSE; // 未曾处理

end;

end;

// 主线程入口

begin

DialogBox(HInstance, MakeIntResource(IDD_SUMMATION), 0, @Dlg_Proc);

end.

// 2.资源脚本 Summation.rc :

// 定义

#define IDD_SUMMATION 101

#define IDI_SUMMATION 102

#define IDC_SUMNUM 1000

#define IDC_CALC 1001

#define IDC_ANSWER 1002

#define IDC_STATIC -1

// 语言

LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED

// 图标

IDI_SUMMATION ICON DISCARDABLE “Summation.ico“

// 模板

IDD_SUMMATION DIALOG DISCARDABLE 18, 18, 162, 41

STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU

CAPTION “Summation“

FONT 8, “MS Sans Serif“

BEGIN

LTEXT “Calculate the sum of the numbers from 0 through &x, where x is: “, IDC_STATIC, 4, 4, 112, 20

EDITTEXT IDC_SUMNUM, 120, 8, 40, 13, ES_AUTOHSCROLL | ES_NUMBER

DEFPUSHBUTTON “&Calculate“, IDC_CALC, 4, 28, 56, 12

LTEXT “Answer:“, IDC_STATIC, 68, 30, 30, 8

LTEXT “?“, IDC_ANSWER, 104, 30, 56, 8

END

// 3.图标文件 Summation.ico , 自备. [:D][:D] 字串4 5楼: 再来贴一个,仍然是《Windows核心编程》上的,一个“结束处理”的简单例子,并且在Finally部分判断之前代码是否出现异常,

不过呢,他是利用关键字让C编译器自动加入SEH代码的,而我这个则是手工编码,我想,可能这更有助于我们了解事情的真相吧。

主要的参考文章就是《A Crash Course on the Depths of Win32™ Structured Exception Handling》,另外还有aimingoo大师

的《Delphi源代码分析》中相关章节,给我不少启发,下面的代码可能有不当之处,欢迎指正,非常感谢。[:D][:D]

program SEHTerm;

{$R ‘SEHTerm.res‘ ‘SEHTerm.rc‘}

uses Windows;

type

TExceptionDisposition = DWORD; // 异常回调返回值类型(C语言中的枚举型占4字节, 而PASCAL只占1字节)

PExceptionFrame = ^TExceptionFrame; // 扩展的异常帧结构(增加了FinallyAddress字段来实现Finally)

TExceptionFrame = record

PrevStruct: PExceptionFrame; // 上一节点位置(线程异常帧链表)

ExceptionHandler: Pointer; // 异常回调地址(由操作系统调用)

FinallyAddress: procedure; // 结束执行地址(由我们自己调用)

end;

// 异常回调

function ExceptHandler(var ExceptionRecord: TExceptionRecord; var EstablisherFrame: TExceptionFrame;

var ContextRecord: TContext; DispatcherContext: Pointer): TExceptionDisposition; Cdecl;

const

ExceptionContinueSearch = $00000001;

StatusUnwind = $C0000027;

ExceptionUnwinding = $00000002;

ExceptionUnwindingForExit = $00000004;

begin

if (ExceptionRecord.ExceptionCode = StatusUnwind) or

(ExceptionRecord.ExceptionFlags = ExceptionUnwinding) or

(ExceptionRecord.ExceptionFlags = ExceptionUnwindingForExit) then

begin

EstablisherFrame.FinallyAddress();

end;

Result := ExceptionContinueSearch;

end;

// 程序入口

const

lpText1: PChar = ‘Perform invalid memory access?‘;

lpCaption1: PChar = ‘SEHTerm: In try block‘;

lpText2: PChar = ‘Abnormal termination‘;

lpText3: PChar = ‘Normal termination‘;

lpCaption2: PChar = ‘SEHTerm: In finally block‘;

lpText4: PChar = ‘Normal process termination‘;

lpCaption4: PChar = ‘SEHTerm: After finally block‘;

asm

// 构造TExceptionFrame并将其插入链表

PUSH OFFSET @@Finally // TExceptionFrame.FinallyAddress

PUSH OFFSET ExceptHandler // TExceptionFrame.ExceptionHandler

PUSH FS:[0] // TExceptionFrame.PrevStruct

MOV FS:[0], ESP // FS:[0]乃TIB.ExceptionList(链表头)

// MessageBox(0, ‘Perform invalid memory access?‘, ‘SEHTerm: In try block‘, MB_YESNO);

PUSH $00000004 // MB_YESNO

PUSH lpCaption1 // ‘SEHTerm: In try block‘

PUSH lpText1 // ‘Perform invalid memory access?‘

PUSH $00000000

CALL MessageBox

// if (n = IDYES) then PByte(0)^ := 5;

CMP EAX, $00000006 // (n = ID_YES) ???

JNZ @@NoAccess // 返回值不是IDYES

MOV [0], $00000005 // 故意访问非法地址

@@NoAccess:

// 如果能执行到这里说明没有出错, ^.^

POP FS:[0] // 将我们节点从异常帧链表摘除

ADD ESP, $00000008 // 修改栈顶指针(出栈简化动作)

PUSH OFFSET @@Other // 准备后面RET指令的跳转地址

@@Finally:

// 1.如果没有出错, 则会顺序执行到这里, :-)

// 2.如果出错, 也会从ExceptHandler()转过来

// if AbnormalTermination() then .. ;

CMP [ESP], OFFSET @@Other // 根据栈顶内容是否等于@@Other, 判断之前是否出错

JNZ @@Abnormal // (从而使得MessageBox()显示不同内容)

// MessageBox(0, ‘Normal termination‘, ‘SEHTerm: In finally block‘, MB_OK)

PUSH $00000000 // MB_OK

PUSH lpCaption2 // ‘SEHTerm: In finally block‘

PUSH lpText3 // ‘Normal termination‘

PUSH $00000000

CALL MessageBox

JMP @@Return

@@Abnormal:

// MessageBox(0, ‘Abnormal termination‘, ‘SEHTerm: In finally block‘, MB_OK)

PUSH $00000000 // MB_OK

PUSH lpCaption2 // ‘SEHTerm: In finally block‘

PUSH lpText2 // ‘Abnormal termination‘

PUSH $00000000

CALL MessageBox

@@Return:

RET // 没发生异常则顺序执行, 否则返回ExceptHandler()

@@Other:

// MessageBox(0, ‘Normal process termination‘, ‘SEHTerm: After finally block‘, MB_OK);

PUSH $00000000 // MB_OK

PUSH lpCaption4 // ‘SEHTerm: After finally block‘

PUSH lpText4 // ‘Normal process termination‘

PUSH $00000000

CALL MessageBox

end. 字串4 6楼: 麻子利害

字串9 7楼: 惭愧惭愧

字串6 8楼: 最后一个, 仍然是《Windows核心编程》上的, 利用异常保护, 实现稀疏提交的数组.

// ------------------ 1. VMArray.pas ------------------

unit VMArray;

interface

uses Windows;

// TopLevelExceptionFilter返回值含义

const

EXCEPTION_EXECUTE_HANDLER = 1; // 已处理异常, 请结束程序, (不要显示出错对话框)

EXCEPTION_CONTINUE_SEARCH = 0; // 未处理异常, 作默认处理, (通常显示出错对话框)

EXCEPTION_CONTINUE_EXECUTION = -1; // 已修复问题, 线程按ContextRecord继续执行

// 扩展了的异常帧(系统只使用前两个域)

type

PExceptionRegistration = ^TExceptionRegistration;

TExceptionRegistration = record

PrevStruct: PExceptionRegistration; // 上一节点位置

ExceptionHandler: Pointer; // 异常回调地址

ExceptionAddress: Pointer; // except块地址

end;

// (虚拟内存+异常保护实现的)稀疏数组

type

TVMArray = class(TObject)

private

m_pNext: TVMArray; // 下一个对象

m_pArray: Pointer; // 数组首地址

m_cbReserve: DWORD; // 数组尺寸(in bytes)

m_dwTypeSize: DWORD; // 组员尺寸(in bytes)

protected

function OnAccessViolation(pvAddrTouched: Pointer; fAttemptedRead: BOOL; // 负责修复内存访问错误,可覆盖

var pep: TExceptionPointers; fRetryUntilSuccessful: BOOL): LongInt; virtual;

public

constructor Create(dwTypeSize, dwReserveElements: DWORD);

destructor Destroy(); override;

class procedure RemoveCurrentThreadOtherSEH(); // 用作摘除SetExceptionHandler()增加的SEH回调

function VMPointer(): Pointer; // 返回数组首地址

function ExceptionFilter(var pep: TExceptionPointers; // 可在except块中作为“过滤器“函数调用,

fRetryUntilSuccessful: BOOL = FALSE): LongInt; // 若没有except块,将由顶层异常回调调用

end;

implementation

type

PfnTopLevelExceptionFilter = function (var pep: TExceptionPointers): LongInt; stdcall;

var

sm_pHead: TVMArray = nil; // 第一个TVMArray对象地址

sm_pfnUnhandledExceptionFilterPrev: PfnTopLevelExceptionFilter; // 以前的顶层异常回调

// 顶层异常回调

function TopLevelExceptionFilter(var pep: TExceptionPointers): LongInt; stdcall;

var

p: TVMArray;

begin

Result := EXCEPTION_CONTINUE_SEARCH;

// 我们只修复内存访问错误

if (pep.ExceptionRecord.ExceptionCode = EXCEPTION_ACCESS_VIOLATION) then

begin

// 遍历TVMArray对象链表, 一一调用

p := sm_pHead;

while (p <> nil) do

begin

// 询问当前节点对象是否修复错误

Result := p.ExceptionFilter(pep, TRUE);

// 这个对象修复了问题, 停止循环

if (Result <> EXCEPTION_CONTINUE_SEARCH) then Break;

// 单链表上的下一个TVMArray对象

p := p.m_pNext;

// 注: 若均不处理则程序就会退出

end;

end;

// 调用以前的顶层异常回调

if (Result = EXCEPTION_CONTINUE_SEARCH) then

Result := sm_pfnUnhandledExceptionFilterPrev(pep);

end;

// 负责修复内存访问错误(被ExceptionFilter调用)

function TVMArray.OnAccessViolation(pvAddrTouched: Pointer; fAttemptedRead: BOOL;

var pep: TExceptionPointers; fRetryUntilSuccessful: BOOL): LongInt;

var

fCommittedStorage: BOOL;

begin

repeat

// 为出错地址提交物理内存

fCommittedStorage := (VirtualAlloc(pvAddrTouched, m_dwTypeSize, MEM_COMMIT, PAGE_READWRITE) <> nil);

// 分配内存失败, 通知用户

if (not fCommittedStorage) and (fRetryUntilSuccessful) then

begin

MessageBox(0, ‘Please close some other applications and Press OK.‘,

‘Insufficient Memory Available‘, MB_ICONWARNING or MB_OK);

end;

until (fCommittedStorage) or (not fRetryUntilSuccessful);

// 1.分配成功, 从出错指令处继续执行

// 2.分配失败, 让系统安静地关闭程序(or 跳至except块执行)

if fCommittedStorage then

Result := EXCEPTION_CONTINUE_EXECUTION

else

Result := EXCEPTION_EXECUTE_HANDLER;

end;

// TVMArray对象初始化

constructor TVMArray.Create(dwTypeSize, dwReserveElements: DWORD);

begin

// 第一个类建立时安装顶层异常回调

if (sm_pHead = nil) then

sm_pfnUnhandledExceptionFilterPrev := SetUnhandledExceptionFilter(@TopLevelExceptionFilter);

// 将自己接到TVMArray对象链表表头

m_pNext := sm_pHead;

sm_pHead := Self;

// 需要为数组保留的虚拟内存的长度

m_dwTypeSize := dwTypeSize;

m_cbReserve := m_dwTypeSize * dwReserveElements;

// 为数组保留虚拟内存空间(不提交)

m_pArray := VirtualAlloc(nil, m_cbReserve, MEM_RESERVE or MEM_TOP_DOWN, PAGE_READWRITE);

// chASSERT(m_pArray <> NULL);

end;

// TVMArray对象结束化

destructor TVMArray.Destroy();

var

p: TVMArray;

begin

// 释放数组对应的虚拟内存空间

if (m_pArray <> nil) then VirtualFree(m_pArray, 0, MEM_RELEASE);

// 从TVMArray对象链表摘除自己

if (sm_pHead = Self) then

sm_pHead := sm_pHead.m_pNext

else begin

p := sm_pHead;

while (p.m_pNext <> nil) do

begin

if (p.m_pNext = Self) then

begin

p.m_pNext := p.m_pNext.m_pNext;

Break;

end;

p := p.m_pNext;

end;

end; // END: else

end;

// 返回数组首地址

function TVMArray.VMPointer(): Pointer;

begin

Result := m_pArray;

end;

// 摘除当前线程所有SEH节点, 慎用!

class procedure TVMArray.RemoveCurrentThreadOtherSEH();

asm

// p := TEB.ExceptionList;

MOV EDX, FS:[0]

// if (p = $FFFFFFFF) then Exit;

CMP EDX, -1

JZ @@QUIT

// while (p.PrevStruct <> $FFFFFFFF) do p := p.PrevStruct;

@@LOOP:

MOV EAX, EDX

MOV EDX, [EAX].TExceptionRegistration.PrevStruct

CMP EDX, -1

JNZ @@LOOP

// TEB.ExceptionList := p;

MOV FS:[0], EAX

@@QUIT:

end;

// 异常过滤函数(被TopLevelExceptionFilter或except块调用)

function TVMArray.ExceptionFilter(var pep: TExceptionPointers; fRetryUntilSuccessful: BOOL = FALSE): LongInt;

var

pvAddrTouched: DWORD;

fAttemptedRead: BOOL;

begin

Result := EXCEPTION_CONTINUE_SEARCH;

// 我们只修复内存访问错误

if (pep.ExceptionRecord.ExceptionCode <> EXCEPTION_ACCESS_VIOLATION) then Exit;

// 发生读写异常的内存地址

pvAddrTouched := pep.ExceptionRecord.ExceptionInformation[1];

// 导致异常的动作(读or写)

fAttemptedRead := (pep.ExceptionRecord.ExceptionInformation[0] = 0);

// 如果该地址在数组范围内

if (DWORD(m_pArray) <= pvAddrTouched) and (DWORD(pvAddrTouched) < (DWORD(m_pArray) + m_cbReserve)) then

Result := OnAccessViolation(Pointer(pvAddrTouched), fAttemptedRead, pep, fRetryUntilSuccessful);

end;

end.

// ------------------- 2. Spreadsheet.dpr -------------------

program Spreadsheet;

{$R ‘Spreadsheet.res‘ ‘Spreadsheet.rc‘}

uses Windows, Messages, VMArray in ‘VMArray.pas‘;

const

IDD_SPREADSHEET = 1;

IDI_SPREADSHEET = 102;

IDC_LOG = 101;

IDC_ROW = 1001;

IDC_COLUMN = 1002;

IDC_VALUE = 1003;

IDC_READCELL = 1004;

IDC_WRITECELL = 1005;

g_nNumRows = 256;

g_nNumCols = 1024;

// 界面窗口句柄

var g_hWnd: HWND;

// 电子表格单元

type

PCell = ^TCell;

TCell = packed record

dwValue: DWORD;

bDummy: array[1..1020] of Byte;

end;

// 电子表格数组

type

PSpreadSheet = ^TSpreadSheet;

TSpreadSheet = array[0..g_nNumRows-1] of array[0..g_nNumCols-1] of TCell;

// 电子表格类

type

TVMSpreadsheet = class(TVMArray)

public

constructor Create();

protected

function OnAccessViolation(pvAddrTouched: Pointer; fAttemptedRead: BOOL; // 新的修复动作

var pep: TExceptionPointers; fRetryUntilSuccessful: BOOL): LongInt; override;

end;

constructor TVMSpreadsheet.Create();

begin

inherited Create(SizeOf(TCell), g_nNumRows * g_nNumCols);

end;

function TVMSpreadsheet.OnAccessViolation(pvAddrTouched: Pointer; fAttemptedRead: BOOL;

var pep: TExceptionPointers; fRetryUntilSuccessful: BOOL): LongInt;

begin

if fAttemptedRead then

begin

SetDlgItemText(g_hWnd, IDC_LOG, ‘Violation: Attempting to Read‘);

Result := EXCEPTION_EXECUTE_HANDLER;

end else

begin

SetDlgItemText(g_hWnd, IDC_LOG, ‘Violation: Attempting to Write‘);

Result := inherited OnAccessViolation(pvAddrTouched, fAttemptedRead, pep, fRetryUntilSuccessful);

end;

end;

var

g_ssObject: TVMSpreadsheet; // 电子表格对象

g_ss: PSpreadSheet = nil; // 表格数组首地址

// WM_INITDIALOG

function Dlg_OnInitDialog(hWnd, hWndFocus: HWND; lParam: LPARAM): BOOL;

begin

SendMessage(hWnd, WM_SETICON, ICON_BIG, LoadIcon(HInstance, MakeIntResource(IDI_SPREADSHEET)));

SendMessage(hWnd, WM_SETICON, ICON_SMALL, LoadIcon(HInstance, MakeIntResource(IDI_SPREADSHEET)));

g_hWnd := hWnd;

SendMessage(GetDlgItem(hWnd, IDC_ROW), EM_LIMITTEXT, 3, 0);

SendMessage(GetDlgItem(hWnd, IDC_COLUMN), EM_LIMITTEXT, 4, 0);

SendMessage(GetDlgItem(hWnd, IDC_VALUE), EM_LIMITTEXT, 7, 0);

SetDlgItemInt(hWnd, IDC_ROW, 100, FALSE);

SetDlgItemInt(hWnd, IDC_COLUMN, 100, FALSE);

SetDlgItemInt(hWnd, IDC_VALUE, 12345, FALSE);

Result := TRUE;

end;

// 数值范围判断

function chInRange(const AMin, AValue, AMax: Integer): Boolean;

begin

Result := (AValue >= AMin) and (AValue <= AMax);

end;

// 线程异常回调

function ThreadExceptHandler(var ExceptionRecord: TExceptionRecord; var EstablisherFrame: TExceptionRegistration;

var ContextRecord: TContext; DispatcherContext: Pointer): DWORD; Cdecl;

const

ExceptionContinueExecution = 0;

ExceptionContinueSearch = 1;

var

ExceptionPointers: TExceptionPointers;

FilterResult: LongInt;

begin

ExceptionPointers.ExceptionRecord := @ExceptionRecord;

ExceptionPointers.ContextRecord := @ContextRecord;

FilterResult := g_ssObject.ExceptionFilter(ExceptionPointers, FALSE);

case FilterResult of

EXCEPTION_EXECUTE_HANDLER: // 跳至except块后执行

begin

ContextRecord.Eip := DWORD(EstablisherFrame.ExceptionAddress);

ContextRecord.Esp := DWORD(@EstablisherFrame);

Result := ExceptionContinueExecution;

end;

EXCEPTION_CONTINUE_EXECUTION: // 重新执行出错指令

begin

Result := ExceptionContinueExecution;

end;

EXCEPTION_CONTINUE_SEARCH: // 未作处理

begin

Result := ExceptionContinueSearch;

end;

else Result := ExceptionContinueSearch;

end;

end;

// WM_COMMAND

procedure Dlg_OnCommand(hWnd: HWND; id: Integer; hWndCtl: HWND; codeNotify: UINT);

label

On_Except, No_Except;

var

nRow, nCol: Integer;

begin

case (id) of

IDCANCEL: // 要求关闭

begin

EndDialog(hWnd, id);

end;

IDC_ROW: // 行号(改变通知)

begin

nRow := GetDlgItemInt(hWnd, IDC_ROW, PBOOL(nil)^, FALSE);

EnableWindow(GetDlgItem(hWnd, IDC_READCELL), chInRange(0, nRow, g_nNumRows - 1));

EnableWindow(GetDlgItem(hWnd, IDC_WRITECELL), chInRange(0, nRow, g_nNumRows - 1));

end;

IDC_COLUMN: // 列号(改变通知)

begin

nCol := GetDlgItemInt(hWnd, IDC_COLUMN, PBOOL(nil)^, FALSE);

EnableWindow(GetDlgItem(hWnd, IDC_READCELL), chInRange(0, nCol, g_nNumCols - 1));

EnableWindow(GetDlgItem(hWnd, IDC_WRITECELL), chInRange(0, nCol, g_nNumCols - 1));

end;

IDC_READCELL: // 读数组

begin

SetDlgItemText(g_hWnd, IDC_LOG, ‘No violation raised‘);

nRow := GetDlgItemInt(hWnd, IDC_ROW, PBOOL(nil)^, FALSE);

nCol := GetDlgItemInt(hWnd, IDC_COLUMN, PBOOL(nil)^, FALSE);

// 异常保护

asm

PUSH OFFSET On_Except // TExceptionRegistration.ExceptionAddress := On_Except;

PUSH OFFSET ThreadExceptHandler // TExceptionRegistration.ExceptionHandler := @ThreadExceptHandler;

PUSH FS:[0] // TExceptionRegistration.PrevStruct := TEB.ExceptionList;

MOV FS:[0], ESP // TEB.ExceptionList := @TExceptionRegistration;

end;

// 可能出错

SetDlgItemInt(hWnd, IDC_VALUE, g_ss[nRow][nCol].dwValue, FALSE);

// 没有出错

asm

JMP No_Except

end;

// 异常处理

On_Except:

SetDlgItemText(hWnd, IDC_VALUE, ‘‘); // 清空Edit, 以示此处还未分配物理内存

// 收尾工作

No_Except:

asm

POP FS:[0] // TEB.ExceptionList := TExceptionRegistration.PrevStruct;

ADD ESP, TYPE Pointer * 2 // 恢复栈顶(与前面的PUSH对应)

end;

end;

IDC_WRITECELL: // 写数组

begin

SetDlgItemText(g_hWnd, IDC_LOG, ‘No violation raised‘);

nRow := GetDlgItemInt(hWnd, IDC_ROW, PBOOL(nil)^, FALSE);

nCol := GetDlgItemInt(hWnd, IDC_COLUMN, PBOOL(nil)^, FALSE);

// 若该地址所处页还未分配物理内存, 执行写入指令将导致异常,

// 我们的顶层异常处理回调将: 1.提交 2.从出错指令处重新执行

g_ss[nRow][nCol].dwValue := GetDlgItemInt(hWnd, IDC_VALUE, PBOOL(nil)^, FALSE);

end;

end;

end;

// 对话框回调

function Dlg_Proc(hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): BOOL; stdcall;

begin

case (uMsg) of

WM_INITDIALOG:

begin

Result :=

SetWindowLong(hWnd, DWL_MSGRESULT, Longint(Dlg_OnInitDialog(hWnd, wParam, lParam))) <> 0;

end;

WM_COMMAND:

begin

Dlg_OnCommand(hWnd, LOWORD(wParam), lParam, HIWORD(wParam));

Result := TRUE;

end;

else

Result := FALSE;

end;

end;

// 程序入口

begin

g_ssObject := TVMSpreadsheet.Create();

g_ss := g_ssObject.VMPointer;

if (g_ss = nil) then

MessageBox(0, ‘Reserves a range failure.‘, ‘Spreadsheet‘, MB_OK)

else begin

TVMArray.RemoveCurrentThreadOtherSEH();

DialogBox(HInstance, MakeIntResource(IDD_SPREADSHEET), 0, @Dlg_Proc);

end;

g_ssObject.Free;

end.

// 3. ------------------ Spreadsheet.rc -------------------

// Language

LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED

// Define

#define IDD_SPREADSHEET 1

#define IDC_LOG 101

#define IDI_SPREADSHEET 102

#define IDC_ROW 1001

#define IDC_COLUMN 1002

#define IDC_COLUMN2 1003

#define IDC_VALUE 1003

#define IDC_READCELL 1004

#define IDC_WRITECELL 1005

// Dialog

IDD_SPREADSHEET DIALOG DISCARDABLE 18, 18, 164, 165

STYLE DS_CENTER | WS_MINIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU

CAPTION “Spreadsheet“

FONT 8, “MS Sans Serif“

BEGIN

LTEXT “Cell size:\nRows:\nColumns:\nTotal size:“, IDC_STATIC, 4, 4, 36, 36

LTEXT “1024 bytes\n256\n1024\n256 MB (268,435,456 bytes)“, IDC_STATIC, 44, 4, 104, 36

LTEXT “R&ow (0-255):“, IDC_STATIC, 4, 56, 42, 8

EDITTEXT IDC_ROW, 60, 52, 40, 14, ES_AUTOHSCROLL | ES_NUMBER

LTEXT “&Column (0-1023):“, IDC_STATIC, 4, 76, 54, 8

EDITTEXT IDC_COLUMN, 60, 72, 40, 14, ES_AUTOHSCROLL | ES_NUMBER

PUSHBUTTON “&Read Cell“, IDC_READCELL, 108, 72, 50, 14

LTEXT “&Value:“, IDC_STATIC, 4, 96, 21, 8

EDITTEXT IDC_VALUE, 60, 92, 40, 14, ES_AUTOHSCROLL | ES_NUMBER

PUSHBUTTON “&Write Cell“, IDC_WRITECELL, 108, 92, 50, 14

LTEXT “Execution lo&g:“, IDC_STATIC, 4, 118, 48, 8

EDITTEXT IDC_LOG, 4, 132, 156, 28, ES_MULTILINE | ES_AUTOHSCROLL | ES_READONLY

END 字串3 9楼: ft!麻子厉害,什么时候也能像他那样..哈哈.. 字串4 10楼: 接受答案了. 字串8

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值