spreadsheet.dpr
来自「< windos核心编程>>是经典中的经典,书中原代码是用de」· DPR 代码 · 共 237 行
DPR
237 行
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.
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?