home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / vp21beta.zip / ARTLSRC.RAR / VPDBGAPI.PAS < prev    next >
Pascal/Delphi Source File  |  2000-08-15  |  45KB  |  1,506 lines

  1. unit VPDbgApi;
  2.  
  3. {&Z-,AlignRec-}
  4.  
  5. interface
  6.  
  7. uses Use32;
  8.  
  9. const
  10.   // Object Pascal Exception Codes
  11.   opecException    = $0EEDFACE;
  12.   opecReRaise      = $0EEDFACF;
  13.   opecExcept       = $0EEDFAD0;
  14.   opecFinally      = $0EEDFAD1;
  15.   opecTerminate    = $0EEDFAD2;
  16.   opecUnhandled    = $0EEDFAD3;
  17.   opecSysException = $0EEDFAD4;
  18.  
  19.   // Debug Event Codes
  20.   decSingleStep    = 0;
  21.   decBreakpoint    = 1;
  22.   decWatchpoint    = 2;
  23.   decException     = 3;
  24.   decStop          = 4;
  25.   decProcessEnded  = 5;
  26.   decError         = 6;
  27.   decLast          = 6;
  28.  
  29. type
  30.   TSysDbgPlatform = (dpOS2, dpWin32);
  31.   TSysDbgPlatforms = set of TSysDbgPlatform;
  32.  
  33.   PSysDbgFlatInfo = ^TSysDbgFlatInfo;
  34.   TSysDbgFlatInfo = record
  35.     Flat_CS: Word;      // Flat code selector
  36.     Flat_DS: Word;      // Flat data selector
  37.   end;
  38.  
  39.   PSysDbgThreadIds = ^TSysDbgThreadIds;
  40.   TSysDbgThreadIds = record
  41.     ThreadID: Longint;
  42.     ThreadHandle: Longint;
  43.     ThreadOrdinal: Longint;
  44.   end;
  45.  
  46.   TSysDbgSegDef = record
  47.     FlatOfs: Longint;
  48.     Size:    Longint;
  49.   end;
  50.  
  51.   PSysDbgEvent = ^TSysDbgEvent;
  52.   TSysDbgEvent = record
  53.     deCode: Integer;
  54.     deError: Integer;
  55.     deThreadID: Integer;
  56.     deXcptCode: Integer;
  57.     deXcptAddress: Longint;
  58.     deXcptParam1: Integer;
  59.     deXcptParam2: Integer;
  60.     deWatchPtMask: Integer;
  61.   end;
  62.  
  63.   PSysDbgInterface = ^TSysDbgInterface;
  64.   TSysDbgInterface = record
  65.     GetThreadParam: function(No: Integer): PSysDbgThreadIds;
  66.     ThreadCreated: procedure(ThreadID,ThreadHandle,ThreadOrdinal: Longint);
  67.     ThreadExited: procedure(ThreadID,ExitCode: Longint);
  68.     DllLoaded: procedure(DllName: PChar; DllHandle,SegCount: Longint; const SegTable: array of TSysDbgSegDef);
  69.     DllUnloaded: procedure(DllHandle: Longint);
  70.     ProcessExited: procedure(ExitCode,ExitType: Integer);
  71.     NotifyException: procedure(const DbgEvent: TSysDbgEvent);
  72.     StopOnException: function(Code: Longint): Boolean;
  73.   end;
  74.  
  75.   PSysDbgCPURegisters = ^TSysDbgCPURegisters;
  76.   TSysDbgCPURegisters = record
  77.     ThreadID: Longint;
  78.     ThreadHandle: Longint;
  79.     ThreadOrdinal: Longint;
  80.     EAX: Longint;
  81.     ECX: Longint;
  82.     EDX: Longint;
  83.     EBX: Longint;
  84.     ESP: Longint;
  85.     EBP: Longint;
  86.     ESI: Longint;
  87.     EDI: Longint;
  88.     EFlags: Longint;
  89.     EIP: Longint;
  90.     CS: SmallWord;
  91.     DS: SmallWord;
  92.     ES: SmallWord;
  93.     FS: SmallWord;
  94.     GS: SmallWord;
  95.     SS: SmallWord;
  96.   end;
  97.  
  98.   PSysDbgFSaveFormat = ^TSysDbgFSaveFormat;
  99.   TSysDbgFSaveFormat = record
  100.     CW: SmallWord;                  // Control Word
  101.     Reserved1: SmallWord;           // Reserved
  102.     SW: SmallWord;                  // Status word
  103.     Reserved2: SmallWord;           // Reserved
  104.     Tag: SmallWord;                 // Tag Word
  105.     Reserved3: SmallWord;           // Reserved
  106.     IPtrOffset: Longint;            // Instruction Pointer Offset
  107.     IPtrSelector: SmallWord;        // Instruction Pointer Selector
  108.     IPtrOpcode: SmallWord;          // Instruction Opcode
  109.     DPtrOffset: Longint;            // Data Pointer Offset
  110.     DPtrSelector: SmallWord;        // Data Pointer Selector
  111.     Reserved4: SmallWord;           // Reserved
  112.     Regs: array [0..7] of Extended; // Floating Point registers
  113.   end;
  114.  
  115.   PSysDbgThreadState = ^TSysDbgThreadState;
  116.   TSysDbgThreadState = record
  117.     IsFrozen: Boolean;
  118.     Schedule:  Byte;
  119.     Priority: SmallWord;
  120.   end;
  121.  
  122.   TSysDbgIDEInterface = record
  123.     SysDbgVersion: Longint;
  124.     SysDbgPlatforms: TSysDbgPlatforms;
  125.     SysDbgInitialize: procedure;
  126.     SysDbgGetFlatInfo: procedure(var FlatInfo: TSysDbgFlatInfo);
  127.     SysDbgSetInterface: procedure(var DbgInt: TSysDbgInterface);
  128.     SysDbgStartProcess: function(const FileName,CmdLine: String; AppType: Longint; var ProcessID,SesID,EntryAddr: Longint): Longint;
  129.     SysDbgTerminateProcess: function: Longint;
  130.     SysDbgSelToFlat: function(Sel,Ofs: Longint): Longint;
  131.     SysDbgReadMemory: function(Sel,Ofs: Longint; Buffer: Pointer; Size: Longint): Longint;
  132.     SysDbgWriteMemory: function(Sel,Ofs: Longint; Buffer: Pointer; Size: Longint): Longint;
  133.     SysDbgReadRegisters: function(Regs: PSysDbgCPURegisters; FPUState: PSysDbgFSaveFormat): Boolean;
  134.     SysDbgWriteRegisters: function(Regs: PSysDbgCPURegisters; FPUState: PSysDbgFSaveFormat): Boolean;
  135.     SysDbgFreezeThread: function(const Regs: TSysDbgCPURegisters): Boolean;
  136.     SysDbgResumeThread: function(const Regs: TSysDbgCPURegisters): Boolean;
  137.     SysDbgGetThreadState: function(const Regs: TSysDbgCPURegisters; var State: TSysDbgThreadState): Boolean;
  138.     SysDbgSetWatchPoint: function(LinAddr: Longint; BkptLen,BkptType: Byte; ThreadID: Longint): Longint;
  139.     SysDbgClearWatchPoint: procedure(Id: Longint);
  140.     SysDbgExecute: procedure(Step: Boolean; Regs: TSysDbgCPURegisters; var DbgEvent: TSysDbgEvent);
  141.     SysDbgWaitUserScreen: procedure(Delay: Longint);
  142.     SysDbgSetHardMode: procedure(Hard: Boolean);
  143.     SysDbgSwitchScreen: procedure(User: Boolean);
  144.   end;
  145.  
  146. procedure SysDbgGetIDEInterface(var IDEInt: TSysDbgIDEInterface); orgname; {$IFDEF VPDBGDLL} export; {$ENDIF}
  147.  
  148. implementation
  149.  
  150. {$IFDEF Win32}
  151.   uses Windows,
  152. {$ELSE}
  153.   uses Os2Def, Os2Base,
  154. {$ENDIF}
  155.     Strings, VPSysLow, ExeHdr;
  156.  
  157. {$IFDEF VPDBGDLL}
  158.  
  159. var
  160.   FlatInfo: TSysDbgFlatInfo;
  161.   DbgInterface: TSysDbgInterface;
  162.  
  163. {$IFNDEF WIN32}
  164.  
  165. type
  166.   KeyPacket = record           // Key packet record for Keyboard Monitor
  167.     MonFlags  : SmallWord;
  168.     ASCII     : Byte;          // Fields that start here are equivalent
  169.     Scan      : Byte;          // to thoses defined in KbdTrans
  170.     IDontCare : array [1..8] of Byte;
  171.     DdFlags   : Word;
  172.   end;
  173.  
  174. var
  175.   ProcessID: Longint;
  176.   SesID: Longint;
  177.  
  178. procedure SysDbgGetFlatInfo(var FlatInfo: TSysDbgFlatInfo);
  179. begin
  180.   with FlatInfo do
  181.   begin
  182.     Flat_CS := CSeg; // GDT selectors, so they are common for all tasks
  183.     Flat_DS := DSeg;
  184.   end;
  185. end;
  186.  
  187. procedure SysDbgWaitUserScreen(Delay: Longint);
  188. var
  189.   RC,MSec,StartMSec: Longint;
  190.   MonHandle,ReadCount: SmallWord;
  191.   Kp: KeyPacket;
  192.   InBuf,OutBuf: array [0..63] of SmallWord;
  193.  
  194. procedure Wait;
  195. begin
  196.   if MSec = 0 then
  197.     MSec := Delay;
  198.   SysCtrlSleep(MSec);
  199. end;
  200.  
  201. begin
  202.   MSec := 0;
  203.   if DosMonOpen('\DEV\KBD$', MonHandle) <> 0 then
  204.     Wait
  205.   else
  206.     begin
  207.       InBuf[0]  := SizeOf(InBuf);
  208.       OutBuf[0] := SizeOf(OutBuf);  {Front}  { Current screen Group }
  209.       if DosMonReg(MonHandle, @InBuf, @OutBuf, 1, SesID) <> 0 then
  210.         begin
  211.           DosMonClose(MonHandle);
  212.           Wait;
  213.         end
  214.       else
  215.         begin
  216.           StartMSec := SysSysMsCount;
  217.           repeat
  218.             ReadCount := SizeOf(KeyPacket);
  219.             RC := DosMonRead(@InBuf, 1, @Kp, ReadCount); // No Wait
  220.             if RC <> 0 then
  221.             begin
  222.               if (RC <> error_Mon_Buffer_Empty) and (MSec = 0) then
  223.                 MSec := Delay;
  224.               Kp.DdFlags := 0;
  225.             end;
  226.           until ((MSec <> 0) and ((SysSysMsCount - StartMSec) >= MSec)) // Time elapsed
  227.             or (((Kp.DdFlags and $40) <> 0) and (Kp.Scan <> 0));        // or any key is pressed
  228.           DosMonClose(MonHandle);
  229.         end;
  230.     end;
  231. end;
  232.  
  233. procedure SysDbgSetHardMode(Hard: Boolean);
  234. var
  235.   HK: HotKey;
  236.   ParmLen,Handle: ULong;
  237. const
  238.   HardDbgMode: Boolean = False;
  239. begin
  240.   if (Hard <> HardDbgMode) and (SysFileOpen('\DEV\KBD$', $40, Handle) = 0) then
  241.   begin
  242.     HardDbgMode := Hard;
  243.     FillChar(HK, SizeOf(HK), 0);
  244.     HK.idHotKey := $FFFF;     // Ctrl-Esc, Alt-Esc, Ctrl-Alt-Del
  245.     ParmLen := SizeOf(HK);
  246.     DosDevIOCtl(Handle, ioctl_Keyboard, kbd_SetSesMgrHotKey, @HK, SizeOf(HK),
  247.       @ParmLen, nil, 0, nil);
  248.     SysFileClose(Handle);
  249.   end;
  250. end;
  251.  
  252. procedure SysDbgSwitchScreen(User: Boolean);
  253. var
  254.   SID: Longint;
  255. begin
  256.   if User then
  257.     SID := SesID
  258.   else
  259.     SID := 0;
  260.   DosSelectSession(SID);
  261. end;
  262.  
  263. procedure HandleNotification(var DB: Debug_Buffer);
  264. var
  265.   I: Integer;
  266.   SegTable: array[0..49] of TSysDbgSegDef;
  267.   Buffer: array[0..255] of Char;
  268.   DB1: Debug_Buffer;
  269. begin
  270.   case DB.Cmd of
  271.     DBG_N_ModuleLoad:
  272.       begin
  273.         Buffer[0] := #0;                // DB.Value = Module Handle
  274.         DosQueryModuleName(DB.Value, SizeOf(Buffer), Buffer);
  275.         I := 0;
  276.         repeat
  277.           DB1.Pid := ProcessID;
  278.           DB1.Value := I+1;             // Object number 1,2...
  279.           DB1.MTE := DB.Value;          // in module
  280.           DB1.Cmd := DBG_C_NumToAddr;   // Get segment address
  281.           if (DosDebug(DB1) <> 0) or (DB1.Cmd <> DBG_N_SUCCESS) then
  282.             Break;
  283.           with SegTable[I] do
  284.           begin
  285.             FlatOfs := DB1.Addr;
  286.             Size := 0;
  287.             DB1.Cmd := DBG_C_AddrToObject;
  288.             if (DosDebug(DB1) = 0) and (DB1.Cmd = DBG_N_SUCCESS) then
  289.               Size := DB1.Len;
  290.           end;
  291.           Inc(I);
  292.         until I >= High(SegTable);
  293.         DbgInterface.DllLoaded(Buffer, DB.Value, I, SegTable);
  294.       end;
  295.  
  296.     DBG_N_ModuleFree:
  297.       DbgInterface.DllUnloaded(DB.Value);
  298.  
  299.     DBG_N_ThreadCreate:
  300.       DbgInterface.ThreadCreated(DB.Tid, DB.Tid, DB.Tid);
  301.  
  302.     DBG_N_ThreadTerm:
  303.       DbgInterface.ThreadExited(DB.Tid, DB.Value);
  304.  
  305.     DBG_N_ProcTerm:
  306.       DbgInterface.ProcessExited(DB.Value, DB.Index);
  307.   end;
  308. end;
  309.  
  310. procedure HandlePendingNotifications;
  311. var
  312.   RC: Longint;
  313.   DB: Debug_Buffer;
  314. begin
  315.   repeat
  316.     DB.Pid := ProcessID;
  317.     DB.Cmd := DBG_C_Stop;
  318.     RC := DosDebug(DB);
  319.     if RC = 0 then
  320.       HandleNotification(DB);
  321.   until (RC <> 0) or (DB.Cmd = DBG_N_SUCCESS);
  322. end;
  323.  
  324. function SysDbgSelToFlat(Sel,Ofs: Longint): Longint;
  325. var
  326.   Selector: SmallWord;
  327.   DB: Debug_Buffer;
  328. begin
  329.   Selector := Sel;
  330.   if (Selector = FlatInfo.Flat_DS) or (Selector = FlatInfo.Flat_CS) then
  331.     Result := Ofs
  332.   else
  333.     begin
  334.       DB.Pid := ProcessID;
  335.       DB.Value := Selector;
  336.       DB.Index := Ofs;
  337.       DB.Cmd := DBG_C_SelToLin;
  338.       DosDebug(DB);
  339.       Result := DB.Addr;
  340.     end;
  341. end;
  342.  
  343. function SysDbgReadMemory(Sel,Ofs: Longint; Buffer: Pointer; Size: Longint): Longint;
  344. var
  345.   DB: Debug_Buffer;
  346. begin
  347.   DB.Addr := SysDbgSelToFlat(Sel, Ofs);
  348.   DB.Pid := ProcessID;
  349.   DB.Buffer := Longint(Buffer);
  350.   DB.Len := Size;
  351.   DB.Cmd := DBG_C_ReadMemBuf;
  352.   if (DosDebug(DB) <> 0) or (DB.Cmd <> DBG_N_SUCCESS) then
  353.     Result := 0
  354.   else
  355.     Result := Size;
  356. end;
  357.  
  358. function SysDbgWriteMemory(Sel,Ofs: Longint; Buffer: Pointer; Size: Longint): Longint;
  359. var
  360.   DB: Debug_Buffer;
  361. begin
  362.   DB.Addr := SysDbgSelToFlat(Sel, Ofs);
  363.   DB.Pid := ProcessID;
  364.   DB.Buffer := Longint(Buffer);
  365.   DB.Len := Size;
  366.   DB.Cmd := DBG_C_WriteMemBuf;
  367.   if (DosDebug(DB) <> 0) or (DB.Cmd <> DBG_N_SUCCESS) then
  368.     Result := 0
  369.   else
  370.     Result := Size;
  371. end;
  372.  
  373. function SysDbgStartProcess(const FileName,CmdLine: String; AppType: Longint; var ProcessID,SesID,EntryAddr: Longint): Longint;
  374. var
  375.   SD: StartData;
  376.   DB: Debug_Buffer;
  377.   TitleBuf: array[0..259] of Char;
  378.   FileNameBuf: array[0..259] of Char;
  379.   CmdLineBuf: array[0..259] of Char;
  380. begin
  381.   EntryAddr := 0; // The IDE determines it itself
  382.   FillChar(SD, SizeOf(StartData), 0);
  383.   SD.Length := 32;                    // So it can start session without PM
  384.   SD.Related := ssf_Related_Child;    // Related
  385.   SD.FgBg := ssf_Fgbg_Back;           // BackGround
  386.   SD.Traceopt  := ssf_TraceOpt_Trace; // Debugging trace
  387.   SD.PgmTitle  := StrPCopy(TitleBuf, 'VP debugging: ' + FileName);
  388.   SD.PgmName   := StrPCopy(FileNameBuf, FileName);
  389.   SD.PgmInputs := StrPCopy(CmdLineBuf, CmdLine);
  390.   SD.TermQ := nil;
  391.   SD.Environment := nil;              // Inherit
  392.   SD.InheritOpt := ssf_InhertOpt_Parent; // Parent environment
  393.   SD.SessionType := AppType;
  394.   Result := DosStartSession(SD, SesID, ProcessID);
  395.   if Result <> 0 then
  396.     begin
  397.       SesID := $FFFFFFFF;
  398.       ProcessID := $FFFFFFFF;
  399.     end
  400.   else
  401.     begin
  402.       DB.Pid   := ProcessID;    // Debuggee PID
  403.       DB.Tid   := 0;            // Reserved, must be 0
  404.       DB.Value := dbg_l_386;    // Debugging Level Number
  405.       DB.Cmd   := DBG_C_Connect;
  406.       Result := DosDebug(DB);
  407.       if (Result = 0) and (DB.Cmd <> DBG_N_SUCCESS) then
  408.         Result := DB.Value;     // Error Code
  409.     end;
  410.   VPDbgAPI.ProcessID := ProcessID;
  411.   VPDbgAPI.SesID := SesID;
  412.   if Result = 0 then
  413.     HandlePendingNotifications;
  414. end;
  415.  
  416. // Restores context of the debuggee (DB.Len should contain context address)
  417.  
  418. procedure RestoreContext(DB: Debug_Buffer);
  419. var
  420.   XcptContext: ContextRecord;
  421. begin
  422.   SysDbgReadMemory(FlatInfo.Flat_DS, DB.Len, @XcptContext, SizeOf(XcptContext));
  423.   DB.EAX := XcptContext.ctx_RegEAX;
  424.   DB.ECX := XcptContext.ctx_RegECX;
  425.   DB.EDX := XcptContext.ctx_RegEDX;
  426.   DB.EBX := XcptContext.ctx_RegEBX;
  427.   DB.ESP := XcptContext.ctx_RegESP;
  428.   DB.EBP := XcptContext.ctx_RegEBP;
  429.   DB.ESI := XcptContext.ctx_RegESI;
  430.   DB.EDI := XcptContext.ctx_RegEDI;
  431.   DB.EFlags := XcptContext.ctx_EFlags;
  432.   DB.EIP := XcptContext.ctx_RegEIP;
  433.   DB.CS := XcptContext.ctx_SegCS;
  434.   DB.DS := XcptContext.ctx_SegDS;
  435.   DB.ES := XcptContext.ctx_SegES;
  436.   DB.FS := XcptContext.ctx_SegFS;
  437.   DB.GS := XcptContext.ctx_SegGS;
  438.   DB.SS := XcptContext.ctx_SegSS;
  439.   DB.Cmd := DBG_C_WriteReg;
  440.   DosDebug(DB);
  441. end;
  442.  
  443. procedure SysDbgExecute(Step: Boolean; Regs: TSysDbgCPURegisters; var DbgEvent: TSysDbgEvent);
  444. var
  445.   Done: Boolean;
  446.   Value,Command,BytesRead: Longint;
  447.   DB: Debug_Buffer;
  448.   XcptReport: ExceptionReportRecord;
  449. begin
  450.   if Step then
  451.     Command := DBG_C_SStep
  452.   else
  453.     Command := DBG_C_Go;
  454.   DB.Pid := ProcessID;
  455.   DB.Tid := Regs.ThreadID;
  456.   Done := False;
  457.   with DbgEvent do
  458.   begin
  459.     deWatchPtMask := 0;
  460.     deCode := decStop;
  461.     case Command of
  462.       DBG_C_Go:   Value := XCPT_CONTINUE_SEARCH;
  463.       DBG_C_Stop: Value := XCPT_CONTINUE_STOP;
  464.     end;
  465.     repeat
  466.       DB.Cmd := Command;
  467.       DB.Value := Value;
  468.       deError := DosDebug(DB);
  469.       if deError <> 0 then
  470.       begin
  471.         DB.Cmd := DBG_N_Error;
  472.         DB.Value := deError;
  473.       end;
  474.       Command := DBG_C_Continue;
  475.       HandleNotification(DB);
  476.       case DB.Cmd of
  477.         DBG_N_ThreadTerm,DBG_N_ThreadCreate,DBG_N_NewProc,DBG_N_AliasFree,
  478.         DBG_N_ModuleLoad,DBG_N_ModuleFree: ;    // Simply skip
  479.  
  480.         DBG_N_ProcTerm:                         // Stop execution
  481.           begin
  482.             Value := XCPT_CONTINUE_STOP;
  483.             deCode := decProcessEnded;
  484.           end;
  485.  
  486.         DBG_N_AsyncStop:
  487.           begin
  488.             deCode := decStop;
  489.             Done := True;                       // Exit
  490.           end;
  491.  
  492.         DBG_N_Watchpoint:                       // Record watchpoint ID
  493.           begin
  494.             deWatchPtMask := deWatchPtMask or (1 shl DB.Index);
  495.             Value := XCPT_CONTINUE_STOP;
  496.             deCode := decWatchpoint;
  497.           end;
  498.  
  499.         DBG_N_Error:
  500.           begin
  501.             deCode := decError;
  502.             deError := DB.Value;
  503.             Done := True;
  504.           end;
  505.  
  506.         DBG_N_Exception:
  507.           begin
  508.             deXcptCode := DB.Buffer;
  509.             deXcptAddress := DB.Addr;
  510.             case DB.Value of // Exception Chance
  511.               0: // Prefirst chance exception
  512.                 if deXcptCode = XCPT_SINGLE_STEP then
  513.                   begin
  514.                     Value := XCPT_CONTINUE_STOP;
  515.                     deCode := decSingleStep;
  516.                   end
  517.                 else
  518.                   if deXcptCode = XCPT_BREAKPOINT then
  519.                   begin
  520.                     Value := XCPT_CONTINUE_STOP;
  521.                     deCode := decBreakpoint;
  522.                   end;
  523.  
  524.               1: // First chance exception
  525.                 begin
  526.                   deCode := decException;
  527.                   SysDbgReadMemory(FlatInfo.Flat_DS, DB.Buffer, @XcptReport, SizeOf(XcptReport));
  528.                   deXcptParam1 := XcptReport.ExceptionInfo[0];
  529.                   deXcptParam2 := XcptReport.ExceptionInfo[1];
  530.                   deXcptCode := XcptReport.ExceptionNum;
  531.                   case deXcptCode of
  532.                     opecReRaise..opecSysException:
  533.                       begin
  534.                         DbgInterface.NotifyException(DbgEvent);
  535.                         Value := XCPT_CONTINUE_EXECUTION;
  536.                       end;
  537.                     else
  538.                       if DbgInterface.StopOnException(deXcptCode) then
  539.                         begin
  540.                           Value := XCPT_CONTINUE_STOP; // Break
  541.                           RestoreContext(DB);
  542.                         end
  543.                       else
  544.                         Value := XCPT_CONTINUE_SEARCH; // Pass to exception handler
  545.                   end;
  546.                 end;
  547.               // Second chance exception
  548.               2: Value := XCPT_CONTINUE_SEARCH; // Perform default system action
  549.               // Invalid stack
  550.               3: Value := XCPT_CONTINUE_SEARCH;
  551.             end; // Case
  552.           end;
  553.  
  554.         else                                    // DBG_N_SUCCESS and others
  555.           Done := True;
  556.       end;
  557.     until Done;
  558.  
  559.     deThreadID := DB.Tid;
  560.   end;
  561. end;
  562.  
  563. function SysDbgTerminateProcess: Longint;
  564. var
  565.   DB: Debug_Buffer;
  566. begin
  567.   DB.Pid := ProcessID;
  568.   DB.Cmd := DBG_C_Term;
  569.   Result := DosDebug(DB);
  570.   if (Result = 0) and (DB.Cmd <> DBG_N_SUCCESS) then
  571.     Result := DB.Value;
  572. end;
  573.  
  574. function SysDbgReadRegisters(Regs: PSysDbgCPURegisters; FPUState: PSysDbgFSaveFormat): Boolean;
  575. var
  576.   DB: Debug_Buffer;
  577. begin
  578.   with Regs^ do
  579.   begin
  580.     DB.PID := ProcessID;
  581.     DB.TID := ThreadID;
  582.     if FPUState = nil then
  583.       begin
  584.         DB.Cmd := DBG_C_ReadReg;
  585.         Result := (DosDebug(DB) = 0) and (DB.Cmd = DBG_N_SUCCESS);
  586.         if Result then
  587.         begin
  588.           EAX := DB.EAX;
  589.           ECX := DB.ECX;
  590.           EDX := DB.EDX;
  591.           EBX := DB.EBX;
  592.           ESP := DB.ESP;
  593.           EBP := DB.EBP;
  594.           ESI := DB.ESI;
  595.           EDI := DB.EDI;
  596.           EFlags:= DB.EFlags;
  597.           EIP := DB.EIP;
  598.           CS := DB.CS;
  599.           DS := DB.DS;
  600.           ES := DB.ES;
  601.           FS := DB.FS;
  602.           GS := DB.GS;
  603.           SS := DB.SS;
  604.         end;
  605.       end
  606.     else
  607.       begin
  608.         DB.Value  := dbg_co_387;             // Coprocessor type 80387
  609.         DB.Len    := SizeOf(TSysDbgFSaveFormat);   // Length of the coprcessor context
  610.         DB.Buffer := Longint(FPUState);      // Address of the Ncp context buffer
  611.         DB.Index  := 0;                      // Reserved, must be 0
  612.         DB.Cmd    := dbg_c_ReadCoRegs;
  613.         Result := (DosDebug(DB) = 0) and (DB.Cmd = DBG_N_SUCCESS);
  614.       end;
  615.   end;
  616. end;
  617.  
  618. function SysDbgWriteRegisters(Regs: PSysDbgCPURegisters; FPUState: PSysDbgFSaveFormat): Boolean;
  619. var
  620.   DB: Debug_Buffer;
  621. begin
  622.   FillChar(DB, SizeOf(DB), 0);
  623.   with Regs^ do
  624.   begin
  625.     DB.PID := ProcessID;
  626.     DB.TID := ThreadID;
  627.     if FPUState = nil then
  628.       begin
  629.         DB.Cmd := DBG_C_WriteReg;
  630.         DB.EAX := EAX;
  631.         DB.ECX := ECX;
  632.         DB.EDX := EDX;
  633.         DB.EBX := EBX;
  634.         DB.ESP := ESP;
  635.         DB.EBP := EBP;
  636.         DB.ESI := ESI;
  637.         DB.EDI := EDI;
  638.         DB.EFlags := EFlags;
  639.         DB.EIP := EIP;
  640.         DB.CS := CS;
  641.         DB.DS := DS;
  642.         DB.ES := ES;
  643.         DB.FS := FS;
  644.         DB.GS := GS;
  645.         DB.SS := SS;
  646.         Result := (DosDebug(DB) = 0) and (DB.Cmd = DBG_N_SUCCESS);
  647.       end
  648.     else
  649.       begin
  650.         DB.Value  := dbg_co_387;            // Coprocessor type 80387
  651.         DB.Len    := SizeOf(TSysDbgFSaveFormat);  // Length of the coprcessor context
  652.         DB.Buffer := Longint(FPUState);     // Address of the Ncp context buffer
  653.         DB.Index  := 0;                     // Reserved, must be 0
  654.         DB.Cmd    := DBG_C_WriteCoRegs;
  655.         Result := (DosDebug(DB) = 0) and (DB.Cmd = DBG_N_SUCCESS);
  656.       end;
  657.   end;
  658. end;
  659.  
  660. function SysDbgFreezeThread(const Regs: TSysDbgCPURegisters): Boolean;
  661. var
  662.   DB: Debug_Buffer;
  663. begin
  664.   DB.Pid := ProcessID;
  665.   DB.Tid := Regs.ThreadID;
  666.   DB.Cmd := DBG_C_Freeze;
  667.   Result := (DosDebug(DB) = 0) and (DB.Cmd = DBG_N_SUCCESS);
  668. end;
  669.  
  670. function SysDbgResumeThread(const Regs: TSysDbgCPURegisters): Boolean;
  671. var
  672.   DB: Debug_Buffer;
  673. begin
  674.   DB.Pid := ProcessID;
  675.   DB.Tid := Regs.ThreadID;
  676.   DB.Cmd := DBG_C_Resume;
  677.   Result := (DosDebug(DB) = 0) and (DB.Cmd = DBG_N_SUCCESS);
  678. end;
  679.  
  680. function SysDbgGetThreadState(const Regs: TSysDbgCPURegisters; var State: TSysDbgThreadState): Boolean;
  681. var
  682.   DB: Debug_Buffer;
  683. begin
  684.   DB.Pid := ProcessID;
  685.   DB.Tid := Regs.ThreadID;
  686.   DB.Buffer := Longint(@State);
  687.   DB.Len := 4;  // IsFrozen db ? ; State db ? ; Priority dw ?
  688.   DB.Cmd := DBG_C_ThrdStat;
  689.   Result := (DosDebug(DB) = 0) and (DB.Cmd = DBG_N_SUCCESS);
  690. end;
  691.  
  692. function SysDbgSetWatchPoint(LinAddr: Longint; BkptLen,BkptType: Byte; ThreadID: Longint): Longint;
  693. var
  694.   DB: Debug_Buffer;
  695. begin
  696.   DB.Pid := ProcessID;
  697.   DB.Addr := LinAddr;
  698.   DB.Len := BkptLen;
  699.   DB.Index := 0;                            // Reserved, must be 0
  700.   DB.Value := 2+Longint(BkptType) shl 16;   // Watchpoint type and Scope
  701.   DB.Cmd := DBG_C_SetWatch;                 // Scope = Local (2)
  702.   if (DosDebug(DB) <> 0) or (DB.Cmd <> DBG_N_SUCCESS) then
  703.     Result := 0
  704.   else
  705.     Result := DB.Index;
  706. end;
  707.  
  708. procedure SysDbgClearWatchPoint(Id: Longint);
  709. var
  710.   DB: Debug_Buffer;
  711. begin
  712.   DB.Pid := ProcessID;
  713.   DB.Index := Id;
  714.   DB.Cmd := DBG_C_ClearWatch;
  715.   DosDebug(DB);
  716. end;
  717.  
  718. {$ELSE}
  719.  
  720. const
  721.   DBG_EXCEPTION_NOT_HANDLED       = $80010001;
  722.   DBG_CONTINUE                    = $00010002;
  723.   DBG_TERMINATE_PROCESS           = $40010004;
  724.  
  725. var
  726.   ProcessInfo: TProcessInformation;
  727.   ProcessName: array[0..259] of Char;
  728.   DebugEvent: TDebugEvent;
  729.   ThreadNumber: Longint;
  730.   ProcessStartAddr: Longint;
  731.  
  732. const
  733.   WatchPtCount: Longint = 0;
  734.   ProcessTerminated: Boolean = True;
  735.  
  736. function SetResult(Success: Boolean): Longint;
  737. begin
  738.   Result := 0;
  739.   if not Success then
  740.     Result := GetLastError;
  741. end;
  742.  
  743. function GethThread(ThreadId: Longint): Longint;
  744. var
  745.   P: PSysDbgThreadIds;
  746.   I: Integer;
  747. begin
  748.   I := 0;
  749.   repeat
  750.     P := DbgInterface.GetThreadParam(I);
  751.     if (P <> nil) and (P^.ThreadId = ThreadId) then
  752.     begin
  753.       Result := P^.ThreadHandle;
  754.       Exit;
  755.     end;
  756.     Inc(I);
  757.   until P = nil;
  758.   Result := 0;
  759. end;
  760.  
  761. function GetIdThread(ThreadHandle: Longint): Longint;
  762. var
  763.   P: PSysDbgThreadIds;
  764.   I: Integer;
  765. begin
  766.   I := 0;
  767.   repeat
  768.     P := DbgInterface.GetThreadParam(I);
  769.     if (P <> nil) and (P^.ThreadHandle = ThreadHandle) then
  770.     begin
  771.       Result := P^.ThreadID;
  772.       Exit;
  773.     end;
  774.     Inc(I);
  775.   until P = nil;
  776.   Result := 0;
  777. end;
  778.  
  779. procedure SysDbgGetFlatInfo(var FlatInfo: TSysDbgFlatInfo);
  780. begin
  781.   FlatInfo := VPDbgAPI.FlatInfo;
  782. end;
  783.  
  784. procedure SetupFlatInfo;
  785. var
  786.   CX: TContext;
  787. begin
  788.   CX.ContextFlags := CONTEXT_SEGMENTS or CONTEXT_CONTROL;
  789.   GetThreadContext(ProcessInfo.hThread, CX);
  790.   FlatInfo.Flat_CS := CX.SegCs;
  791.   FlatInfo.Flat_DS := CX.SegDs;
  792. end;
  793.  
  794.  
  795. procedure SysDbgWaitUserScreen(Delay: Longint);
  796. begin
  797.   SysCtrlSleep(Delay);
  798. end;
  799.  
  800. procedure SysDbgSetHardMode(Hard: Boolean);
  801. begin
  802. end;
  803.  
  804. type
  805.   TWindowTitle = array[0..259] of Char;
  806.   PSearchWindowParam = ^TSearchWindowParam;
  807.   TSearchWindowParam = record
  808.     swpPID: DWord;
  809.     swpWnd: HWnd;
  810.     swpConsoleTitle: TWindowTitle;
  811.   end;
  812.  
  813. function EnumWindowFunc(Wnd: HWnd; Param: PSearchWindowParam): Bool; stdcall;
  814. var
  815.   PID: DWord;
  816.   WindowTitle: TWindowTitle;
  817. begin
  818.   with Param do
  819.   begin
  820.     GetWindowThreadProcessId(Wnd, @PID);
  821.     Result := swpPID <> PID;
  822.     if swpConsoleTitle[0] <> #0 then
  823.     begin
  824.       GetWindowText(Wnd, WindowTitle, SizeOf(WindowTitle));
  825.       Result := StrIComp(swpConsoleTitle, WindowTitle) <> 0;
  826.     end;
  827.     if not Result then
  828.       Param.swpWnd := Wnd;
  829.   end;
  830. end;
  831.  
  832. procedure SysDbgSwitchScreen(User: Boolean);
  833. var
  834.   Param: TSearchWindowParam;
  835. begin
  836.   with Param do
  837.   begin
  838.     swpWnd := 0;
  839.     swpConsoleTitle[0] := #0;
  840.     swpPID := ProcessInfo.dwProcessId;
  841.     if not User then
  842.       swpPID := GetCurrentProcessId;
  843.     EnumWindows(@EnumWindowFunc, DWord(@Param));
  844.     if not User and (swpWnd = 0) then
  845.     begin
  846.       GetConsoleTitle(swpConsoleTitle, SizeOf(swpConsoleTitle));
  847.       EnumWindows(@EnumWindowFunc, DWord(@Param));
  848.     end;
  849.     if Param.swpWnd <> 0 then
  850.     begin
  851.       SetForegroundWindow(Param.swpWnd);
  852.     end;
  853.   end;
  854. end;
  855.  
  856. function SysDbgSelToFlat(Sel,Ofs: Longint): Longint;
  857. var
  858.   Selector: SmallWord;
  859. begin
  860.   Selector := Sel;
  861.   if (Selector = FlatInfo.Flat_DS) or (Selector = FlatInfo.Flat_CS) then
  862.     Result := Ofs
  863.   else
  864.     Result := 0;
  865. end;
  866.  
  867. function SysDbgReadMemory(Sel,Ofs: Longint; Buffer: Pointer; Size: Longint): Longint;
  868. begin
  869.   if not ReadProcessMemory(ProcessInfo.hProcess, Pointer(SysDbgSelToFlat(Sel, Ofs)),
  870.     Buffer, Size, Result) then
  871.       Result := 0;
  872. end;
  873.  
  874. function SysDbgWriteMemory(Sel,Ofs: Longint; Buffer: Pointer; Size: Longint): Longint;
  875. begin
  876.   if not WriteProcessMemory(ProcessInfo.hProcess, Pointer(SysDbgSelToFlat(Sel, Ofs)),
  877.     Buffer, Size, Result) then
  878.       Result := 0;
  879. end;
  880.  
  881. procedure ReadPChar(Ofs: Longint; Buffer: PChar; BufSize: Longint; Unicode: Boolean);
  882. var
  883.   I: Integer;
  884.   WordBuffer: array[0..1024] of SmallWord;
  885. begin
  886.   I := 0;
  887.   if Unicode then
  888.     begin
  889.       while (I < BufSize-1) and (SysDbgReadMemory(FlatInfo.Flat_CS, Ofs+I*2, @WordBuffer[I], 2) = 2) and (WordBuffer[I] <> 0) do
  890.         Inc(I);
  891.       WordBuffer[I] := 0;
  892.       WideCharToMultiByte(0, 0, @WordBuffer, I, Buffer, BufSize, nil, nil);
  893.     end
  894.   else
  895.     begin
  896.       while (I < BufSize-1) and (SysDbgReadMemory(FlatInfo.Flat_CS, Ofs+I, @Buffer[I], 1) = 1) and (Buffer[I] <> #0) do
  897.         Inc(I);
  898.       Buffer[I] := #0;
  899.     end;
  900. end;
  901.  
  902. function ReadDWord(Ofs: Longint): Longint;
  903. begin
  904.   if SysDbgReadMemory(FlatInfo.Flat_CS, Ofs, @Result, 4) <> 4 then
  905.     Result := 0;
  906. end;
  907.  
  908. type
  909.   PDLLData = ^TDLLData;
  910.   TDLLData = record
  911.     // Input
  912.     hFile: THandle;
  913.     BaseOfs: Longint;
  914.     // Output
  915.     Name: array[0..255] of Char;
  916.     Size: Longint;
  917.   end;
  918.  
  919. procedure GetDLLParams(var DLLData: TDllData);
  920. var
  921.   I,Ofs: Longint;
  922.   Actual: Longint;
  923.   ExeHdr: TImageDosHeader;
  924.   PEHdr: record
  925.     Signature: Longint;
  926.     FileHdr: TImageFileHeader;
  927.     OptionalHdr: TImageOptionalHeader;
  928.   end;
  929.   ExpDir: TImageExportDirectory;
  930. begin
  931.   with DLLData do
  932.   begin
  933.     Name[0] := #0;
  934.     Size := 0;
  935.     SysFileSeek(hFile, 0, 0, Actual);
  936.     SysFileRead(hFile, ExeHdr, SizeOf(ExeHdr), Actual);
  937.     if (ExeHdr.e_magic = image_DOS_Signature) and (ExeHdr.e_lfanew <> 0) and (Actual = SizeOf(ExeHdr)) then
  938.     begin
  939.       SysFileSeek(hFile, ExeHdr.e_lfanew, 0, Actual);
  940.       SysFileRead(hFile, PEHdr, SizeOf(PEHdr), Actual);
  941.       if (PEHdr.Signature = image_NT_Signature) and (Actual = SizeOf(PEHdr)) then
  942.       begin
  943.         Ofs := BaseOfs + PEHdr.OptionalHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
  944.         if SysDbgReadMemory(FlatInfo.Flat_CS, Ofs, @ExpDir, SizeOf(ExpDir)) = SizeOf(ExpDir) then
  945.           ReadPChar(BaseOfs + ExpDir.Name, Name, SizeOf(Name), False);
  946.         Ofs := 0;
  947.         with PEHdr.OptionalHdr do
  948.         begin
  949.           I := Low(DataDirectory);
  950.           while I <= High(DataDirectory) do
  951.           with DataDirectory[I] do
  952.           begin
  953.             if Ofs < VirtualAddress + Size then
  954.               Ofs := VirtualAddress + Size;
  955.             Inc(I);
  956.           end;
  957.         end;
  958.         Size := Ofs;
  959.       end;
  960.     end;
  961.   end;
  962. end;
  963.  
  964. procedure HandleEvent(var Event: TDebugEvent);
  965. var
  966.   I: Integer;
  967.   SegEntry: TSysDbgSegDef;
  968.   DLLData: TDLLData;
  969. begin
  970.   with Event do
  971.   case dwDebugEventCode of
  972.     CREATE_THREAD_DEBUG_EVENT:
  973.       begin
  974.         Inc(ThreadNumber);
  975.         DbgInterface.ThreadCreated(dwThreadId, CreateThread.hThread, ThreadNumber);
  976.       end;
  977.  
  978.     EXIT_THREAD_DEBUG_EVENT:
  979.       DbgInterface.ThreadExited(dwThreadId, ExitThread.dwExitCode);
  980.  
  981.     EXIT_PROCESS_DEBUG_EVENT:
  982.       begin
  983.         ProcessTerminated := True;
  984.         DbgInterface.ThreadExited(ProcessInfo.dwThreadId, ProcessInfo.hThread);
  985.         DbgInterface.ProcessExited(ExitProcess.dwExitCode, 0);
  986.         ContinueDebugEvent(DebugEvent.dwProcessId, DebugEvent.dwThreadId, dbg_Continue);
  987.       end;
  988.  
  989.     LOAD_DLL_DEBUG_EVENT:
  990.       begin
  991.         DLLData.hFile := LoadDll.hFile;
  992.         DLLData.BaseOfs := Longint(LoadDll.lpBaseOfDll);;
  993.         GetDLLParams(DLLData);
  994.         SegEntry.FlatOfs := DLLData.BaseOfs;
  995.         SegEntry.Size := DLLData.Size;
  996.         DbgInterface.DllLoaded(DLLData.Name, DLLData.BaseOfs, 1, SegEntry);
  997.         if DLLData.Name[0] = #0 then
  998.           ReadPChar(ReadDWord(Longint(LoadDll.lpImageName)), DLLData.Name,
  999.             SizeOf(DLLData.Name), LoadDll.fUnicode <> 0);
  1000.       end;
  1001.  
  1002.     UNLOAD_DLL_DEBUG_EVENT:
  1003.       DbgInterface.DllUnloaded(Longint(UnloadDll.lpBaseOfDll));
  1004.  
  1005.     CREATE_PROCESS_DEBUG_EVENT:
  1006.       begin
  1007.         DLLData.hFile := CreateProcessInfo.hFile;
  1008.         DLLData.BaseOfs := Longint(CreateProcessInfo.lpBaseOfImage);
  1009.         GetDLLParams(DLLData);
  1010.         SegEntry.FlatOfs := DLLData.BaseOfs;
  1011.         SegEntry.Size := DLLData.Size;
  1012.         StrCopy(DLLData.Name, ProcessName);
  1013.         DbgInterface.DllLoaded(DLLData.Name, DLLData.BaseOfs, 1, SegEntry);
  1014.         ProcessStartAddr := Longint(CreateProcessInfo.lpStartAddress);
  1015.       end;
  1016.   end;
  1017. end;
  1018.  
  1019. // The starting point of the program is not reported properly, because
  1020. // the entry point resides in Pharlap ETS code, not in VP generated code as
  1021. // the VP debugger expects it to be. To workaround this, the following hack
  1022. // is used: the code at the reported starting point is as follows:
  1023. //
  1024. // cs:215669C              pushfd
  1025. // cs:21567 60              pushad
  1026. // cs:21568 06              push   es
  1027. // cs:21569 E8220F0000      call   22490h
  1028. // cs:2156E 07              pop    es
  1029. // cs:2156F 61              popad
  1030. // cs:21570 9D              popfd
  1031. // cs:21571 E981EBFFFF      jmp    200F7h
  1032.  
  1033. // where the last JMP instruction goes to the actual starting entry point
  1034. // of the program. Calculate the target address of JMP and report it
  1035. // as the entry address. Unfortunately, this technique might not work in
  1036. // future if Pharlap decide to change this startup code.
  1037.  
  1038. {$IFDEF PharlapETS}
  1039.  
  1040. procedure PharlapHack;
  1041. var
  1042.   JmpInstr: record
  1043.     Opcode: Byte;
  1044.     Disp:   Longint;
  1045.   end;
  1046. begin
  1047.   if (SysDbgReadMemory(FlatInfo.Flat_CS, ProcessStartAddr+11, @JmpInstr, SizeOf(JmpInstr)) = SizeOf(JmpInstr)) and
  1048.     (JmpInstr.Opcode = $E9) then
  1049.       Inc(ProcessStartAddr, 11 + JmpInstr.Disp + 5);
  1050. end;
  1051.  
  1052. {$ENDIF}
  1053.  
  1054. function SysDbgStartProcess(const FileName,CmdLine: String; AppType: Longint; var ProcessID,SesID,EntryAddr: Longint): Longint;
  1055. var
  1056.   StartupInfo: TStartupInfo;
  1057.   TitleBuf: array[0..259] of Char;
  1058.   FileNameBuf: array[0..259] of Char;
  1059.   CmdLineBuf: array[0..512] of Char;
  1060.   QuotedName: String;
  1061. begin
  1062.   WatchPtCount := 0;
  1063.   ProcessStartAddr := 0;
  1064.   if Pos(' ',FileName) > 0 then
  1065.     QuotedName := '"' + FileName + '"'
  1066.   else
  1067.     QuotedName := FileName;
  1068.   StrPCopy(CmdLineBuf, QuotedName + ' ');
  1069.   StrPCopy(StrEnd(CmdLineBuf), CmdLine);
  1070.   StrPCopy(ProcessName, FileName);
  1071.   FillChar(StartupInfo, SizeOf(TStartupInfo), 0);
  1072.   with StartupInfo do
  1073.   begin
  1074.     cb := SizeOf(TStartupInfo);
  1075.     dwFlags := startf_UseShowWindow;
  1076.     if AppType = 1 then
  1077.       dwFlags := dwFlags or StartF_RunFullScreen;
  1078.     wShowWindow := sw_ShowNormal;
  1079.     lpTitle := StrPCopy(TitleBuf, 'VP debugging: ' + FileName);
  1080.   end;
  1081.   Result := SetResult(CreateProcess(
  1082.     StrPCopy(FileNameBuf, FileName)     , // FileName
  1083.     CmdLineBuf                          , // Command Line
  1084.     nil                                 , // Process attributes
  1085.     nil                                 , // Thread attributes
  1086.     False                               , // Inherit handles
  1087.     debug_Only_This_Process + create_New_Console,
  1088.     nil                                 , // Environment
  1089.     nil                                 , // Current directory
  1090.     StartupInfo                         ,
  1091.     ProcessInfo
  1092.   ));
  1093.   if Result = 0 then
  1094.   begin
  1095.     SetUpFlatInfo;
  1096.     ProcessTerminated := False;
  1097.     ThreadNumber := 1;
  1098.     DbgInterface.ThreadCreated(ProcessInfo.dwThreadId, ProcessInfo.hThread, 1);
  1099.     ProcessID := ProcessInfo.hProcess;
  1100.     SesID := ProcessInfo.hProcess;
  1101.     while WaitForDebugEvent(DebugEvent, Infinite) do
  1102.     begin
  1103.       HandleEvent(DebugEvent);
  1104.       if DebugEvent.dwDebugEventCode = EXCEPTION_DEBUG_EVENT then
  1105.         Break;
  1106.       ContinueDebugEvent(DebugEvent.dwProcessId, DebugEvent.dwThreadId, dbg_Continue);
  1107.     end;
  1108.   end;
  1109. {$IFDEF PharlapETS}
  1110.   PharlapHack;
  1111. {$ENDIF}
  1112.   EntryAddr := ProcessStartAddr;
  1113. end;
  1114.  
  1115. procedure SysDbgExecute(Step: Boolean; Regs: TSysDbgCPURegisters; var DbgEvent: TSysDbgEvent);
  1116. var
  1117.   Done: Boolean;
  1118.   hThread: THandle;
  1119.   DbgContinueFlag: Longint;
  1120.   CX: TContext;
  1121. begin
  1122.   DbgContinueFlag := dbg_Continue;
  1123.   if Step then
  1124.   begin
  1125.     hThread := GethThread(DebugEvent.dwThreadId);
  1126.     CX.ContextFlags := CONTEXT_CONTROL;
  1127.     GetThreadContext(hThread, CX);
  1128.     CX.EFlags := CX.EFlags or $0100; // Set Trap Flag
  1129.     SetThreadContext(hThread, CX);
  1130.   end;
  1131.   with DebugEvent,DbgEvent do
  1132.   repeat
  1133.     Done := True;
  1134.     deCode := decError;
  1135.     deError := SetResult(ContinueDebugEvent(DebugEvent.dwProcessId, DebugEvent.dwThreadId, DbgContinueFlag));
  1136.     DbgContinueFlag := dbg_Continue;
  1137.     if deError <> 0 then
  1138.       Exit;
  1139.     deError := SetResult(WaitForDebugEvent(DebugEvent, Infinite));
  1140.     HandleEvent(DebugEvent);
  1141.     deThreadID := DebugEvent.dwThreadId;
  1142.     if deError <> 0 then
  1143.       Exit;
  1144.     case dwDebugEventCode of
  1145.       EXCEPTION_DEBUG_EVENT:
  1146.         begin
  1147.           deXcptCode := Exception.ExceptionRecord.ExceptionCode;
  1148.           deXcptAddress := Longint(Exception.ExceptionRecord.ExceptionAddress);
  1149.           case deXcptCode of
  1150.             STATUS_SINGLE_STEP:
  1151.               deCode := decSingleStep;
  1152.             STATUS_BREAKPOINT:
  1153.               begin
  1154.                 deCode := decBreakpoint;
  1155.                 CX.ContextFlags := CONTEXT_CONTROL;
  1156.                 hThread := GethThread(DebugEvent.dwThreadId);
  1157.                 GetThreadContext(hThread, CX);
  1158.                 Dec(CX.EIP);
  1159.                 SetThreadContext(hThread, CX);
  1160.               end;
  1161.             else
  1162.               if Exception.dwFirstChance = 0 then
  1163.                 Done := False
  1164.               else
  1165.                 begin
  1166.                   deCode := decException;
  1167.                   deXcptParam1 := Exception.ExceptionRecord.ExceptionInformation[0];
  1168.                   deXcptParam2 := Exception.ExceptionRecord.ExceptionInformation[1];
  1169.                   case deXcptCode of
  1170.                     opecReRaise..opecSysException:
  1171.                       begin
  1172.                         DbgInterface.NotifyException(DbgEvent);
  1173.                         Done := False;
  1174.                       end;
  1175.                     else
  1176.                       if not DbgInterface.StopOnException(deXcptCode) then
  1177.                       begin
  1178.                         DbgContinueFlag := dbg_Exception_Not_Handled;
  1179.                         Done := False;
  1180.                       end;
  1181.                   end;
  1182.                 end;
  1183.           end;
  1184.         end;
  1185.  
  1186.       EXIT_PROCESS_DEBUG_EVENT:
  1187.         deCode := decProcessEnded;
  1188.  
  1189.       RIP_EVENT:
  1190.         deError := RipInfo.dwError
  1191.  
  1192.       else
  1193.         Done := False;
  1194.     end;
  1195.   until Done;
  1196. end;
  1197.  
  1198. {$IFDEF PharlapETS}
  1199. function ETSTerminateProcess(hThread: THandle; ExitCode: Longint): LongBool; StdCall; external 'etsdebug.dll' name '_ETSTerminateProcess@8';
  1200. {$ENDIF}
  1201.  
  1202. function SysDbgTerminateProcess: Longint;
  1203. var
  1204.   Success: Boolean;
  1205.   CX: TContext;
  1206.   Regs: TSysDbgCPURegisters;
  1207.   DbgEvent: TSysDbgEvent;
  1208. begin
  1209.   if not ProcessTerminated then
  1210.   begin
  1211.   {$IFDEF PharlapETS}
  1212.     ETSTerminateProcess(ProcessInfo.hThread, 1);
  1213.   {$ELSE}
  1214.     Regs.ThreadHandle := ProcessInfo.hThread;
  1215.     Regs.ThreadID := ProcessInfo.dwThreadId;
  1216.     CX.ContextFlags := CONTEXT_CONTROL;
  1217.     GetThreadContext(ProcessInfo.hThread, CX);
  1218.     CX.EIP := Longint(GetProcAddress(GetModuleHandle('kernel32.dll'), 'ExitProcess'));
  1219.     SetThreadContext(ProcessInfo.hThread, CX);
  1220.     SysDbgExecute(False, Regs, DbgEvent);
  1221.     if not ProcessTerminated then
  1222.       repeat
  1223.         Success := ContinueDebugEvent(DebugEvent.dwProcessId, DebugEvent.dwThreadId, dbg_Terminate_Process);
  1224.         if Success then
  1225.         begin
  1226.           Success := WaitForDebugEvent(DebugEvent, Infinite);
  1227.           HandleEvent(DebugEvent);
  1228.         end;
  1229.       until not Success or
  1230.         (DebugEvent.dwDebugEventCode = EXIT_PROCESS_DEBUG_EVENT) or
  1231.         (DebugEvent.dwDebugEventCode = RIP_EVENT);
  1232.   {$ENDIF}
  1233.   end;
  1234.   Result := 0;
  1235. end;
  1236.  
  1237. function SysDbgReadRegisters(Regs: PSysDbgCPURegisters; FPUState: PSysDbgFSaveFormat): Boolean;
  1238. var
  1239.   CX: TContext;
  1240. begin
  1241.   if FPUState = nil then
  1242.     CX.ContextFlags := CONTEXT_FULL
  1243.   else
  1244.     CX.ContextFlags := CONTEXT_FLOATING_POINT;
  1245.   with Regs^ do
  1246.   begin
  1247.     Result := GetThreadContext(ThreadHandle, CX);
  1248.     if Result then
  1249.       if FPUState <> nil then
  1250.         Move(CX.FloatSave, FPUState^, SizeOf(FPUState^))
  1251.       else
  1252.         begin
  1253.           GS := CX.SegGs;
  1254.           FS := CX.SegFs;
  1255.           ES := CX.SegEs;
  1256.           DS := CX.SegDs;
  1257.           EDI := CX.Edi;
  1258.           ESI := CX.Esi;
  1259.           EBX := CX.Ebx;
  1260.           EDX := CX.Edx;
  1261.           ECX := CX.Ecx;
  1262.           EAX := CX.Eax;
  1263.           EBP := CX.Ebp;
  1264.           EIP := CX.Eip;
  1265.           CS := CX.SegCs;
  1266.           EFlags := CX.EFlags;
  1267.           ESP := CX.Esp;
  1268.           SS := CX.SegSs;
  1269.         end;
  1270.   end;
  1271. end;
  1272.  
  1273. function SysDbgWriteRegisters(Regs: PSysDbgCPURegisters; FPUState: PSysDbgFSaveFormat): Boolean;
  1274. var
  1275.   CX: TContext;
  1276. begin
  1277.   with Regs do
  1278.   begin
  1279.     if FPUState = nil then
  1280.       begin
  1281.         CX.ContextFlags := CONTEXT_FULL;
  1282.         CX.SegGs := GS;
  1283.         CX.SegFs := FS;
  1284.         CX.SegEs := ES;
  1285.         CX.SegDs := DS;
  1286.         CX.Edi := EDI;
  1287.         CX.Esi := ESI;
  1288.         CX.Ebx := EBX;
  1289.         CX.Edx := EDX;
  1290.         CX.Ecx := ECX;
  1291.         CX.Eax := EAX;
  1292.         CX.Ebp := EBP;
  1293.         CX.Eip := EIP;
  1294.         CX.SegCs := CS;
  1295.         CX.EFlags := EFlags;
  1296.         CX.Esp := ESP;
  1297.         CX.SegSs := SS;
  1298.       end
  1299.     else
  1300.       begin
  1301.         FillChar(CX, SizeOf(CX), 0);
  1302.         CX.ContextFlags := CONTEXT_FLOATING_POINT;
  1303.         Move(FPUState^, CX.FloatSave, SizeOf(FPUState^));
  1304.       end;
  1305.     Result := SetThreadContext(ThreadHandle, CX);
  1306.   end;
  1307. end;
  1308.  
  1309. function SysDbgFreezeThread(const Regs: TSysDbgCPURegisters): Boolean;
  1310. begin
  1311.   Result := SuspendThread(Regs.ThreadHandle) <> $FFFFFFFF;
  1312. end;
  1313.  
  1314. function SysDbgResumeThread(const Regs: TSysDbgCPURegisters): Boolean;
  1315. begin
  1316.   Result := ResumeThread(Regs.ThreadHandle) <> $FFFFFFFF;
  1317. end;
  1318.  
  1319. function SysDbgGetThreadState(const Regs: TSysDbgCPURegisters; var State: TSysDbgThreadState): Boolean;
  1320. var
  1321.   Count: Integer;
  1322. begin
  1323.   with State, Regs do
  1324.   begin
  1325.     Count := SuspendThread(ThreadHandle);
  1326.     Result := Count <> $FFFFFFFF;
  1327.     IsFrozen := Count > 0;
  1328.     Schedule := 0;
  1329.     Priority := GetThreadPriority(ThreadHandle);
  1330.     ResumeThread(ThreadHandle);
  1331.   end;
  1332. end;
  1333.  
  1334. type
  1335.   PDRs = ^TDRs;
  1336.   TDRs = array [0..3] of DWord;
  1337.  
  1338. function SysDbgSetWatchPoint(LinAddr: Longint; BkptLen,BkptType: Byte; ThreadID: Longint): Longint;
  1339. var
  1340.   I,W: Integer;
  1341.   Success: Bool;
  1342.   P: PSysDbgThreadIds;
  1343.   CX: TContext;
  1344. const                         // Execute,Write,Read-Write
  1345.   DR7Types: array[1..3] of Byte = (0, 1, 3);
  1346. begin
  1347.   if WatchPtCount >= 4 then
  1348.     Result := 0
  1349.   else
  1350.     begin
  1351.       I := 0;
  1352.       repeat
  1353.         P := DbgInterface.GetThreadParam(I);
  1354.         if (P <> nil) and ((ThreadID = 0) or (P^.ThreadId = ThreadID)) then
  1355.         begin
  1356.           CX.ContextFlags := CONTEXT_DEBUG_REGISTERS;
  1357.           Success := GetThreadContext(P^.ThreadHandle, CX);
  1358.           if Success then
  1359.           begin
  1360.             W := WatchPtCount;
  1361.             PDRs(@CX.DR0)^[W] := LinAddr;
  1362.             CX.DR7 := (CX.DR7 and not ($F shl (16 + (W*4)))) or ($0001 shl (W*2)) or
  1363.               (DR7Types[BkptType] shl (16 + W*4)) or ((BkptLen-1) shl (18 + W*4));
  1364.             Success := SetThreadContext(P^.ThreadHandle, CX);
  1365.           end;
  1366.           if not Success then
  1367.           begin
  1368.             Result := 0;
  1369.             Exit;
  1370.           end;
  1371.         end;
  1372.         Inc(I);
  1373.       until P = nil;
  1374.       Inc(WatchPtCount);
  1375.       Result := WatchPtCount;
  1376.     end;
  1377. end;
  1378.  
  1379. procedure SysDbgClearWatchPoint(Id: Longint);
  1380. var
  1381.   I: Integer;
  1382.   P: PSysDbgThreadIds;
  1383.   CX: TContext;
  1384. begin
  1385.   Dec(Id);
  1386.   I := 0;
  1387.   repeat
  1388.     P := DbgInterface.GetThreadParam(I);
  1389.     if P <> nil then
  1390.     begin
  1391.       CX.ContextFlags := CONTEXT_DEBUG_REGISTERS;
  1392.       if GetThreadContext(P^.ThreadHandle, CX) then
  1393.       begin
  1394.         PDRs(@CX.DR0)^[Id] := 0;
  1395.         CX.DR7 := CX.DR7 and not (($1 shl (Id*2)) or ($F shl (16 + (Id*4))));
  1396.         SetThreadContext(P^.ThreadHandle, CX);
  1397.       end;
  1398.     end;
  1399.     Inc(I);
  1400.   until P = nil;
  1401.   Dec(WatchPtCount);
  1402. end;
  1403.  
  1404. {$ENDIF}
  1405.  
  1406. procedure SysDbgSetInterface(var DbgInt: TSysDbgInterface);
  1407. begin
  1408.   DbgInterface := DbgInt;
  1409. end;
  1410.  
  1411. procedure SysDbgInitialize;
  1412. begin
  1413.   SysDbgGetFlatInfo(FlatInfo);
  1414. end;
  1415.  
  1416. procedure SysDbgGetIDEInterface(var IDEInt: TSysDbgIDEInterface);
  1417. begin
  1418.   with IDEInt do
  1419.   begin
  1420.     SysDbgVersion           := 3;
  1421.   {$IFDEF WIN32}
  1422.     SysDbgPlatforms         := [dpWin32];
  1423.   {$ELSE}
  1424.     SysDbgPlatforms         := [dpOS2];
  1425.   {$ENDIF}
  1426.     SysDbgInitialize        := VPDbgAPI.SysDbgInitialize;
  1427.     SysDbgGetFlatInfo       := VPDbgAPI.SysDbgGetFlatInfo;
  1428.     SysDbgSetInterface      := VPDbgAPI.SysDbgSetInterface;
  1429.     SysDbgStartProcess      := VPDbgAPI.SysDbgStartProcess;
  1430.     SysDbgTerminateProcess  := VPDbgAPI.SysDbgTerminateProcess;
  1431.     SysDbgSelToFlat         := VPDbgAPI.SysDbgSelToFlat;
  1432.     SysDbgReadMemory        := VPDbgAPI.SysDbgReadMemory;
  1433.     SysDbgWriteMemory       := VPDbgAPI.SysDbgWriteMemory;
  1434.     SysDbgReadRegisters     := VPDbgAPI.SysDbgReadRegisters;
  1435.     SysDbgWriteRegisters    := VPDbgAPI.SysDbgWriteRegisters;
  1436.     SysDbgFreezeThread      := VPDbgAPI.SysDbgFreezeThread;
  1437.     SysDbgResumeThread      := VPDbgAPI.SysDbgResumeThread;
  1438.     SysDbgGetThreadState    := VPDbgAPI.SysDbgGetThreadState;
  1439.     SysDbgSetWatchPoint     := VPDbgAPI.SysDbgSetWatchPoint;
  1440.     SysDbgClearWatchPoint   := VPDbgAPI.SysDbgClearWatchPoint;
  1441.     SysDbgExecute           := VPDbgAPI.SysDbgExecute;
  1442.     SysDbgWaitUserScreen    := VPDbgAPI.SysDbgWaitUserScreen;
  1443.     SysDbgSetHardMode       := VPDbgAPI.SysDbgSetHardMode;
  1444.     SysDbgSwitchScreen      := VPDbgAPI.SysDbgSwitchScreen;
  1445.   end;
  1446. end;
  1447.  
  1448. {$ELSE}
  1449.  
  1450. type
  1451.   TGetIDEInterface = procedure(var IDEInt: TSysDbgIDEInterface);
  1452.  
  1453. var
  1454.   GetIDEInterface: Pointer;
  1455.   DllHandle: HModule;
  1456.   LoadError: Longint;
  1457.  
  1458. procedure SysDbgGetIDEInterface(var IDEInt: TSysDbgIDEInterface);
  1459. begin
  1460.   IDEInt.SysDbgVersion := - LoadError; // Not loaded
  1461.   if Assigned(GetIDEInterface) then
  1462.     TGetIDEInterface(GetIDEInterface)(IDEInt);
  1463. end;
  1464.  
  1465. {$IFDEF WIN32}
  1466. initialization
  1467.   DllHandle := LoadLibrary('VPDBGDLL.DLL');
  1468.   if DllHandle = 0 then
  1469.     LoadError := GetLastError;
  1470.   GetIDEInterface := GetProcAddress(DllHandle, 'SysDbgGetIDEInterface');
  1471. finalization
  1472.   FreeLibrary(DllHandle);
  1473.  
  1474. {$ELSE}
  1475.  
  1476. var
  1477.   FullDllName: array[0..260] of Char;
  1478.   DllNameStr: ShortString absolute FullDllName;
  1479.  
  1480. function MakeFullDllName(FileName: PChar): PChar;
  1481. var
  1482.   I: Integer;
  1483. begin
  1484.   DllNameStr := ParamStr(0);
  1485.   I := Length(DllNameStr);
  1486.   while (I > 1) and not (DllNameStr[I] in [':', '\']) do
  1487.     Dec(I);
  1488.   repeat
  1489.     Inc(I);
  1490.     DllNameStr[I] := FileName^;
  1491.     Inc(FileName);
  1492.   until (FileName-1)^ = #0;
  1493.   Result := @DllNameStr[1];
  1494. end;
  1495.  
  1496. initialization
  1497.   LoadError := DosLoadModule(nil, 0, MakeFullDllName('VPDBGDLL.DLL'), DllHandle);
  1498.   DosQueryProcAddr(DllHandle, 0, 'SysDbgGetIDEInterface', GetIDEInterface);
  1499. finalization
  1500.   DosFreeModule(DllHandle);
  1501. {$ENDIF}
  1502.  
  1503. {$ENDIF}
  1504.  
  1505. end.
  1506.