sehterm.dpr

来自「一本已经绝版的好书」· DPR 代码 · 共 232 行

DPR
232
字号

// Module name: SEHTerm.C ->> SEHTerm.dpr
// Notices: Copyright (c) 1995-1997 Jeffrey Richter
// Translator: 刘麻子, Liu_mazi@126.com

program SEHTerm;

{$R 'SEHTerm.res' 'SEHTerm.rc'}
{$DEFINE SIMULATION} // 模拟操作
{$OPTIMIZATION OFF}  // 关闭优化

uses
  Windows, Messages,
  Other in '..\Other.pas', CmnHdr in '..\CmnHdr.pas', WindowsX in '..\WindowsX.pas';

const
  IDD_SEHTERM      =    1;
  IDC_OPENSUCCEEDS =  100;
  IDC_SIZESUCCEEDS =  101;
  IDI_SEHTERM      =  101;
  IDC_MEMSUCCEEDS  =  102;
  IDC_READSUCCEEDS =  103;
  IDC_LOG          =  104;

  // 扩展SEH节点
type
  PEstablisherFrame = ^TEstablisherFrame;
  TEstablisherFrame = record
    Prev: PEstablisherFrame;
    Handler: Pointer;
    SafeEip: DWORD; // 出错跳转地址
    SafeEbp: DWORD; // 原先堆栈指针
  end;

  // 线程异常回调
function ExceptHandler(var ExceptionRecord: TExceptionRecord; var EstablisherFrame: TEstablisherFrame;
  var ContextRecord: TContext; DispatcherContext: Pointer): TExceptionDisposition; cdecl;
begin
  if (ExceptionRecord.ExceptionCode = LeaveException) then
  begin
    ContextRecord.Esp := DWORD(@EstablisherFrame); // 恢复栈顶
    ContextRecord.Ebp := EstablisherFrame.SafeEbp; // 恢复指针
    ContextRecord.Eip := EstablisherFrame.SafeEip; // 跳到后面
    Result := ExceptionContinueExecution; // 已经处理, 线程按ContextRecord继续执行
  end else
    Result := ExceptionContinueSearch;    // 没有处理, 系统继续"询问"后面的SEH节点
end;

  // 统计文件词数
function Dlg_CountWordsInFile(hWndLog: HWND; fOpenSucceeds, fFileSizeSucceeds, fMemSucceeds, fReadSucceeds: BOOL): DWORD;
label
  FinallyDo;
var
  hFile: THandle;
  lpvFileData: Pointer;
  fFileReadOk: BOOL;
  lNumWords, dwLastError: DWORD;
begin
  // 异常保护 (_try)
  asm
    PUSH EBP                   // TEstablisherFrame.SafeEbp := EBP
    PUSH OFFSET FinallyDo      // TEstablisherFrame.SafeEip := @FinallyDo
    PUSH OFFSET ExceptHandler  // TEstablisherFrame.Handler := @ExceptHandler
    PUSH FS:[0]                // TEstablisherFrame.Prev := TEB.ExceptionList
    MOV  FS:[0], ESP           // TEB.ExceptionList := @TEstablisherFrame
  end;

  // 置初始值
  lpvFileData := nil;
  lNumWords := UINT_MAX;

  // 开始执行
  ListBox_ResetContent(hWndLog);
  ListBox_AddString(hWndLog, 'Starting execution');

  // 打开文件
{$IFDEF SIMULATION}
  hFile := IfThen(fOpenSucceeds, not INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE);
{$ELSE}
  hFile := CreateFile(...);
{$ENDIF}
  if (hFile = INVALID_HANDLE_VALUE) then
  begin
    ListBox_AddString(hWndLog, '   File open: Fail');
    _leave;
  end else
    ListBox_AddString(hWndLog, '   File open: OK');

  // 文件大小
{$IFDEF SIMULATION}
  dwLastError := IfThen(fFileSizeSucceeds, NO_ERROR, not NO_ERROR);
{$ELSE}
  dwFileSize := GetFileSize(hFile);
  dwLastError := GetLastError();
{$ENDIF}
  if (dwLastError <> NO_ERROR) then
  begin
    ListBox_AddString(hWndLog, '   File size: Fail');
    _leave;
  end else
    ListBox_AddString(hWndLog, '   File size: OK');

  // 分配内存
{$IFDEF SIMULATION}
  lpvFileData := IfThen(fMemSucceeds, Pointer(1), nil);
{$ELSE}
  lpvFileData := HeapAlloc(GetProcessHeap(), 0, dwFileSize);
{$ENDIF}
  if (lpvFileData = nil) then
  begin
    ListBox_AddString(hWndLog, '   Memory allocation: Fail');
    _leave;
  end else
    ListBox_AddString(hWndLog, '   Memory allocation: OK');

  // 读入文件
{$IFDEF SIMULATION}
  fFileReadOk := fReadSucceeds;
{$ELSE}
  fFileReadOk := ReadFile(hFile, PChar(lpvFileData)^, dwFileSize, PDWORD(nil)^, nil);
{$ENDIF}
  if (fFileReadOk = FALSE) then
  begin
    ListBox_AddString(hWndLog, '   File read: Fail');
    _leave;
  end else
    ListBox_AddString(hWndLog, '   File read: OK');

  // 统计词数
  ListBox_AddString(hWndLog, '   Calculating the number of words');
  lNumWords := 37;

  // 去除保护 (_finally)
FinallyDo:
  asm
    POP  FS:[0]                // TEB.ExceptionList := TEstablisherFrame.Prev;
    ADD  ESP, TYPE Pointer * 3 // 恢复栈顶
  end;

  // 执行完毕
  ListBox_AddString(hWndLog, '   Cleaning up');

  // 释放内存
  if (lpvFileData <> nil) then
  begin
    ListBox_AddString(hWndLog, '   Freeing memory');
{$IFNDEF SIMULATION}
    HeapFree(GetProcessHeap(), 0, lpvFileData);
{$ENDIF}
  end;

  // 关闭文件
  if (hFile <> INVALID_HANDLE_VALUE) then
  begin
    ListBox_AddString(hWndLog, '   Closing file');
{$IFNDEF SIMULATION}
   CloseHandle(hFile);
{$ENDIF}
  end;

  Result := lNumWords;
end;
 
  // WM_INITDIALOG
function Dlg_OnInitDialog(hWnd, hWndFocus: HWND; lParam: LPARAM): BOOL;
begin
  chSETDLGICONS(hWnd, IDI_SEHTERM, IDI_SEHTERM);

  Button_SetCheck(GetDlgItem(hWnd, IDC_OPENSUCCEEDS), BST_CHECKED);
  Button_SetCheck(GetDlgItem(hWnd, IDC_SIZESUCCEEDS), BST_CHECKED);
  Button_SetCheck(GetDlgItem(hWnd, IDC_MEMSUCCEEDS), BST_CHECKED);
  Button_SetCheck(GetDlgItem(hWnd, IDC_READSUCCEEDS), BST_CHECKED);

  Result := TRUE;
end;

  // WM_COMMAND
procedure Dlg_OnCommand(hWnd: HWND; id: Integer; hWndCtl: HWND; codeNotify: UINT);
var
  szBuf: array[0..100] of Char;
  lNumWords: DWORD;
begin
  case (id) of
    IDOK:
      begin
        lNumWords := Dlg_CountWordsInFile(
          GetDlgItem(hWnd, IDC_LOG),
          Button_GetCheck(GetDlgItem(hWnd, IDC_OPENSUCCEEDS)) = BST_CHECKED,
          Button_GetCheck(GetDlgItem(hWnd, IDC_SIZESUCCEEDS)) = BST_CHECKED,
          Button_GetCheck(GetDlgItem(hWnd, IDC_MEMSUCCEEDS))  = BST_CHECKED,
          Button_GetCheck(GetDlgItem(hWnd, IDC_READSUCCEEDS)) = BST_CHECKED);

        if (lNumWords = UINT_MAX) then
          ListBox_AddString(GetDlgItem(hWnd, IDC_LOG), 'Error occurred in function.')
        else begin
          _wvsprintf(szBuf, 'Result: Words in file = %d', [lNumWords]);
          ListBox_AddString(GetDlgItem(hWnd, IDC_LOG), szBuf);
        end;
      end;

    IDCANCEL:
      begin
        EndDialog(hWnd, id);
      end;
  end;
end;

  // 对话框回调
function DlgProc(hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): BOOL; stdcall;
begin
  case (uMsg) of
    WM_INITDIALOG:
      begin
        Result := SetDlgMsgResult(hWnd, LRESULT(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
  chWARNIFUNICODEUNDERWIN95();
  DialogBox(HInstance, MakeIntResource(IDD_SEHTERM), 0, @DlgProc);
end.

⌨️ 快捷键说明

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