cheat-engine/Cheat Engine/frmThreadlistunit.pas

910 lines
24 KiB
ObjectPascal

unit frmThreadlistunit;
{$MODE Delphi}
interface
uses
jwawindows, windows, LCLIntf, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, ComCtrls, Menus, StdCtrls, LResources,cefuncproc, CEDebugger, debugHelper,
newkernelhandler, networkInterface, networkInterfaceApi;
type
{ TfrmThreadlist }
TfrmThreadlist = class(TForm)
lblIsWOW64: TLabel;
MenuItem1: TMenuItem;
miClearDebugRegisters: TMenuItem;
miFreezeThread: TMenuItem;
miResumeThread: TMenuItem;
PopupMenu1: TPopupMenu;
miBreak: TMenuItem;
threadTreeview: TTreeView;
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormCreate(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure MenuItem1Click(Sender: TObject);
procedure miBreakClick(Sender: TObject);
procedure miClearDebugRegistersClick(Sender: TObject);
procedure miFreezeThreadClick(Sender: TObject);
procedure miResumeThreadClick(Sender: TObject);
procedure threadTreeviewDblClick(Sender: TObject);
procedure threadTreeviewExpanding(Sender: TObject; Node: TTreeNode;
var AllowExpansion: Boolean);
private
{ Private declarations }
public
{ Public declarations }
procedure FillThreadlist;
end;
var
frmThreadlist: TfrmThreadlist;
implementation
uses debugeventhandler, frmstacktraceunit, DebuggerInterfaceAPIWrapper,
ProcessHandlerUnit, Parsers;
resourcestring
rsPleaseFirstAttachTheDebuggerToThisProcess = 'Please first attach the debugger to this process';
rsCouldnTObtainContext = 'Couldn''t obtain context';
rsCouldnTOpenHandle = 'Couldn''t open handle';
rsTLChangeValue = 'Change value';
rsTLWhatShouldTheNewValueOfThisRegisterBe = 'What should the new value of this register be?';
rsTLFailedErrorcode = 'failed. Errorcode=';
rsTLExecute = 'Execute';
rsTLWrite = 'Write';
rsTLIO = 'I/O';
rsTLAccess = 'Access';
rsTL1Bytes = '1 byte';
rsTL2Bytes = '2 bytes';
rsTL8Bytes = '8 bytes';
rsTL4Bytes = '4 bytes';
procedure TfrmThreadlist.FormClose(Sender: TObject;
var Action: TCloseAction);
begin
action:=cafree;
frmthreadlist:=nil;
end;
procedure TfrmThreadlist.FormCreate(Sender: TObject);
begin
fillthreadlist;
end;
procedure TfrmThreadlist.FormShow(Sender: TObject);
begin
lblIsWOW64.visible:={$ifdef cpu64}processhandler.is64Bit=false{$else}false{$endif};
end;
procedure TfrmThreadlist.MenuItem1Click(Sender: TObject);
var
c: TCONTEXT;
{$ifdef cpu64}
c32: TContext32;
{$endif}
i: integer;
s: TTreeNode;
threadlist: tlist;
tid: dword;
th: THandle;
begin
zeromemory(@c, sizeof(c));
c.ContextFlags:=CONTEXT_FULL;
s:=threadTreeview.Selected;
if s<>nil then
begin
while s.level>0 do
s:=s.Parent;
tid:=strtoint('$'+s.Text);
if frmstacktrace=nil then
frmstacktrace:=tfrmstacktrace.create(application);
if debuggerthread<>nil then
begin
threadlist:=debuggerthread.lockThreadlist;
try
for i:=0 to threadlist.Count-1 do
begin
if TDebugThreadHandler(threadlist[i]).ThreadId=tid then
begin
{$ifdef cpu64}
if processhandler.is64Bit=false then
begin
ZeroMemory(@c32, sizeof(c32));
c32.ContextFlags:=CONTEXT_FULL;
Wow64GetThreadContext(TDebugThreadHandler(threadlist[i]).handle, c32);
c.Rip:=c32.Eip;
c.Rsp:=c32.esp;
c.Rbp:=c32.ebp;
end
else
{$endif}
GetThreadContext(TDebugThreadHandler(threadlist[i]).handle, c);
break;
end;
end;
finally
debuggerthread.unlockThreadlist;
end;
end
else
begin
th:=OpenThread(THREAD_GET_CONTEXT, false, tid);
if th<>0 then
begin
{$ifdef cpu64}
if processhandler.is64Bit=false then
begin
ZeroMemory(@c32, sizeof(c32));
c32.ContextFlags:=CONTEXT_FULL; ;
Wow64GetThreadContext(th, c32);
c.Rip:=c32.Eip;
c.Rsp:=c32.esp;
c.Rbp:=c32.ebp;
end
else
{$endif}
GetThreadContext(th, c);
closehandle(th);
end;
end;
frmstacktrace.stacktrace(th, c);
frmstacktrace.Show;
end;
end;
procedure TFrmthreadlist.FillThreadlist;
var i: integer;
lastselected: integer;
threadlist: tlist;
li: TListitem;
ths: THandle;
te32: TThreadEntry32;
p: PSystemProcesses;
needed: dword;
pp: PSystemProcesses;
begin
if threadTreeview.Selected<>nil then
lastselected:=threadTreeview.selected.index
else
lastselected:=-1;
threadTreeview.BeginUpdate;
threadTreeview.Items.Clear;
if debuggerthread<>nil then
begin
threadlist:=debuggerthread.lockThreadlist;
try
for i:=0 to threadlist.Count-1 do
threadTreeview.Items.Add(nil,inttohex(TDebugThreadHandler(threadlist[i]).ThreadId,1));
finally
debuggerthread.unlockThreadlist;
end;
end
else
begin
//get the list using thread32first/next
ths:=CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD,0);
if ths<>INVALID_HANDLE_VALUE then
begin
zeromemory(@te32,sizeof(te32));
te32.dwSize:=sizeof(te32);
if Thread32First(ths, te32) then
repeat
if te32.th32OwnerProcessID=processid then
threadTreeview.Items.add(nil,inttohex(te32.th32ThreadID,1));
until Thread32Next(ths, te32)=false;
closehandle(ths);
end;
end;
for i:=0 to threadTreeview.Items.Count-1 do
threadTreeview.Items[i].HasChildren:=true;
if (lastselected<>-1) and (threadTreeview.Items.Count>lastselected) then
threadTreeview.Items[lastselected].Selected:=true;
threadTreeview.EndUpdate;
end;
procedure TfrmThreadlist.miBreakClick(Sender: TObject);
var threadlist: tlist;
i: integer;
begin
if debuggerthread<>nil then
begin
if (threadTreeview.Selected<>nil) and (threadTreeview.selected.Level=0) then
begin
threadlist:=debuggerthread.lockThreadlist;
try
for i:=0 to threadlist.Count-1 do
begin
if TDebugThreadHandler(threadlist[i]).ThreadId=strtoint('$'+threadTreeview.selected.Text) then
begin
TDebugThreadHandler(threadlist[i]).breakThread;
break;
end;
end;
finally
debuggerthread.unlockThreadlist;
end;
end;
end
else
raise exception.create(rsPleaseFirstAttachTheDebuggerToThisProcess);
end;
procedure TfrmThreadlist.miClearDebugRegistersClick(Sender: TObject);
var threadlist: tlist;
i,j: integer;
s: ttreenode;
begin
if debuggerthread<>nil then
begin
for j:=0 to threadTreeview.SelectionCount-1 do
begin
s:=threadTreeview.Selections[j];
// if (threadTreeview.Selected<>nil) then
begin
//s:=threadTreeview.Selected;
while s.level>0 do
s:=s.parent;
threadlist:=debuggerthread.lockThreadlist;
try
for i:=0 to threadlist.Count-1 do
begin
if TDebugThreadHandler(threadlist[i]).ThreadId=strtoint('$'+s.Text) then
begin
TDebugThreadHandler(threadlist[i]).clearDebugRegisters;
break;
end;
end;
finally
debuggerthread.unlockThreadlist;
end;
end;
end;
end
else
raise exception.create(rsPleaseFirstAttachTheDebuggerToThisProcess);
end;
procedure TfrmThreadlist.miFreezeThreadClick(Sender: TObject);
var
i: integer;
s: TTreeNode;
threadlist: tlist;
tid: dword;
th: THandle;
begin
s:=threadTreeview.Selected;
if s<>nil then
begin
while s.level>0 do
s:=s.Parent;
tid:=strtoint('$'+s.Text);
if debuggerthread<>nil then
begin
threadlist:=debuggerthread.lockThreadlist;
try
for i:=0 to threadlist.Count-1 do
begin
if TDebugThreadHandler(threadlist[i]).ThreadId=tid then
begin
SuspendThread(TDebugThreadHandler(threadlist[i]).handle);
break;
end;
end;
finally
debuggerthread.unlockThreadlist;
end;
end
else
begin
th:=OpenThread(THREAD_SUSPEND_RESUME, false, tid);
if th<>0 then
begin
SuspendThread(th);
closehandle(th);
end;
end;
end;
end;
procedure TfrmThreadlist.miResumeThreadClick(Sender: TObject);
var
i: integer;
s: TTreeNode;
threadlist: tlist;
tid: dword;
th: Thandle;
begin
s:=threadTreeview.Selected;
if s<>nil then
begin
while s.level>0 do
s:=s.Parent;
tid:=strtoint('$'+s.Text);
if debuggerthread<>nil then
begin
threadlist:=debuggerthread.lockThreadlist;
try
for i:=0 to threadlist.Count-1 do
begin
if TDebugThreadHandler(threadlist[i]).ThreadId=tid then
begin
SuspendThread(TDebugThreadHandler(threadlist[i]).handle);
break;
end;
end;
finally
debuggerthread.unlockThreadlist;
end;
end
else
begin
th:=OpenThread(THREAD_SUSPEND_RESUME, false, tid);
if th<>0 then
begin
ResumeThread(th);
closehandle(th);
end;
end;
end;
end;
procedure TfrmThreadlist.threadTreeviewDblClick(Sender: TObject);
var s: TTreeNode;
th: thandle;
c: tcontext;
regnr: integer;
regaddress: PPtrUInt;
v: ptruint;
input: string;
tid: dword;
ai: integer;
x: boolean;
{$ifdef cpu64}
use32bitcontext: boolean;
c32: TContext32;
{$endif}
begin
//change the selected register
s:=threadTreeview.Selected;
if (s<>nil) and (s.level=1) then //selected a registers
begin
regnr:=s.Index;
ai:=s.AbsoluteIndex;
while s.level>0 do
s:=s.Parent;
tid:=strtoint('$'+s.Text);
th:=OpenThread(THREAD_SUSPEND_RESUME or THREAD_GET_CONTEXT or THREAD_SET_CONTEXT or THREAD_QUERY_INFORMATION, false, tid);
if th<>0 then
begin
suspendthread(th);
x:=false;
ZeroMemory(@c, sizeof(c));
{$ifdef cpu64}
use32bitcontext:=(not processhandler.is64Bit) and (ssctrl in GetKeyShiftState);
if use32bitcontext then
begin
//override, the user wants the 32-bit context
ZeroMemory(@c32, sizeof(c32));
c32.ContextFlags:=CONTEXT_ALL;
x:=WOW64GetThreadContext(th, c32);
if x then
begin
//convert
c.Dr0:=c32.Dr0;
c.Dr1:=c32.Dr1;
c.Dr2:=c32.Dr2;
c.Dr3:=c32.Dr3;
c.Dr6:=c32.Dr6;
c.Dr7:=c32.Dr7;
c.Rax:=c32.eax;
c.Rbx:=c32.ebx;
c.Rcx:=c32.ecx;
c.Rdx:=c32.edx;
c.Rsi:=c32.esi;
c.Rdi:=c32.edi;
c.Rbp:=c32.ebp;
c.Rsp:=c32.esp;
c.Rip:=c32.eip;
end;
end
else
{$endif}
begin
c.ContextFlags:=CONTEXT_ALL or CONTEXT_EXTENDED_REGISTERS;
x:=GetThreadContext(th, c);
end;
if x then
begin
case regnr of
0: regaddress:=@c.Dr0;
1: regaddress:=@c.Dr1;
2: regaddress:=@c.Dr2;
3: regaddress:=@c.Dr3;
4: regaddress:=@c.Dr6;
5: regaddress:=@c.Dr7;
6: regaddress:=@c.{$ifdef cpu64}rax{$else}eax{$endif};
7: regaddress:=@c.{$ifdef cpu64}rbx{$else}ebx{$endif};
8: regaddress:=@c.{$ifdef cpu64}rcx{$else}ecx{$endif};
9: regaddress:=@c.{$ifdef cpu64}rdx{$else}edx{$endif};
10: regaddress:=@c.{$ifdef cpu64}rsi{$else}esi{$endif};
11: regaddress:=@c.{$ifdef cpu64}rdi{$else}edi{$endif};
12: regaddress:=@c.{$ifdef cpu64}rbp{$else}ebp{$endif};
13: regaddress:=@c.{$ifdef cpu64}rsp{$else}esp{$endif};
14: regaddress:=@c.{$ifdef cpu64}rip{$else}eip{$endif};
{$ifdef cpu64}
15: regaddress:=@c.r8;
16: regaddress:=@c.r9;
17: regaddress:=@c.r10;
18: regaddress:=@c.r11;
19: regaddress:=@c.r12;
20: regaddress:=@c.r13;
21: regaddress:=@c.r14;
22: regaddress:=@c.r15;
{$endif}
else
regaddress:=@v; //in case of bugs
end;
if processhandler.is64Bit then
v:=regaddress^
else
v:=pdword(regaddress)^;
input:=inttohex(v,8);
InputQuery(rsTLChangeValue,rsTLWhatShouldTheNewValueOfThisRegisterBe, input);
v:=StrToQWordEx('$'+input);
if processhandler.is64Bit then
regaddress^:=v
else
pdword(regaddress)^:=v;
{$ifdef cpu64}
if use32bitcontext then
begin
c32.ContextFlags:=CONTEXT_ALL;
c32.Dr0:=c.Dr0;
c32.Dr1:=c.Dr1;
c32.Dr2:=c.Dr2;
c32.Dr3:=c.Dr3;
c32.Dr6:=c.Dr6;
c32.Dr7:=c.Dr7;
c32.eax:=c.Rax;
c32.ebx:=c.rbx;
c32.ecx:=c.rcx;
c32.edx:=c.rdx;
c32.esi:=c.rsi;
c32.edi:=c.rdi;
c32.ebp:=c.rbp;
c32.esp:=c.rsp;
c32.eip:=c.rip;
if WOW64SetThreadContext(th, c32)=false then
showmessage(rsTLFailedErrorcode+inttostr(GetLastError));
end
else
{$endif}
begin
c.ContextFlags:=CONTEXT_ALL or CONTEXT_EXTENDED_REGISTERS;
if SetThreadContext(th, c)=false then
showmessage(rsTLFailedErrorcode+inttostr(GetLastError));
end;
end;
resumethread(th);
closehandle(th);
threadTreeviewExpanding(threadTreeview, s,x);
threadTreeview.Items.SelectOnlyThis(threadTreeview.Items[ai]);
threadTreeview.Selected:=threadTreeview.Items[ai];
end;
end;
//suspend the thread
//get the current register value
//show and edit
//convert back to integer
//resume thread
end;
procedure TfrmThreadlist.threadTreeviewExpanding(Sender: TObject;
Node: TTreeNode; var AllowExpansion: Boolean);
function rw2str(x: byte): string;
begin
case x of
0: result:=rsTLExecute;
1: result:=rsTLWrite;
2: result:=rsTLIO;
3: result:=rsTLAccess;
end;
end;
function len2str(x: byte): string;
begin
case x of
0: result:=rsTL1Bytes;
1: result:=rsTL2Bytes;
2: result:=rsTL8Bytes;
3: result:=rsTL4Bytes;
end;
end;
var
c: TContext;
ca: TARMCONTEXT;
tid: dword;
th: thandle;
prefix: char;
s: string;
rw: byte;
len: byte;
drinfo: string;
ldtentry: TLDTEntry;
i: integer;
x: boolean;
{$ifdef cpu64}
use32bitcontext: boolean;
c32: TContext32;
{$endif}
cenet: TCEconnection;
begin
if node.level=0 then
begin
//extract thread info
if node.HasChildren then
Node.DeleteChildren;
tid:=strtoint('$'+Node.text);
cenet:=getConnection;
if cenet<>nil then
begin
if debuggerthread<>nil then
begin
if processhandler.SystemArchitecture=archArm then
begin
GetThreadContextArm(tid, ca);
with threadTreeview.items do
begin
AddChild(node,'R0='+inttohex(ca.R0,8));
AddChild(node,'R1='+inttohex(ca.R1,8));
AddChild(node,'R2='+inttohex(ca.R2,8));
AddChild(node,'R3='+inttohex(ca.R3,8));
AddChild(node,'R4='+inttohex(ca.R4,8));
AddChild(node,'R5='+inttohex(ca.R5,8));
AddChild(node,'R6='+inttohex(ca.R6,8));
AddChild(node,'R7='+inttohex(ca.R7,8));
AddChild(node,'R8='+inttohex(ca.R8,8));
AddChild(node,'R9='+inttohex(ca.R9,8));
AddChild(node,'R10='+inttohex(ca.R10,8));
AddChild(node,'FP='+inttohex(ca.FP,8));
AddChild(node,'IP='+inttohex(ca.IP,8));
AddChild(node,'SP='+inttohex(ca.SP,8));
AddChild(node,'LR='+inttohex(ca.LR,8));
AddChild(node,'PC='+inttohex(ca.PC,8));
AddChild(node,'CPSR='+inttohex(ca.CPSR,8));
AddChild(node,'ORIG_R0='+inttohex(ca.ORIG_R0,8));
end;
end
else
begin
GetThreadContext(tid, c);
if processhandler.is64Bit then
prefix:='r'
else
prefix:='e';
//no dr access yet for x86
with threadTreeview.items do
begin
AddChild(node,prefix+'ax='+inttohex(c.{$ifdef cpu64}rax{$else}eax{$endif},8));
AddChild(node,prefix+'bx='+inttohex(c.{$ifdef cpu64}rbx{$else}ebx{$endif},8));
AddChild(node,prefix+'cx='+inttohex(c.{$ifdef cpu64}rcx{$else}ecx{$endif},8));
AddChild(node,prefix+'dx='+inttohex(c.{$ifdef cpu64}rdx{$else}edx{$endif},8));
AddChild(node,prefix+'si='+inttohex(c.{$ifdef cpu64}rsi{$else}esi{$endif},8));
AddChild(node,prefix+'di='+inttohex(c.{$ifdef cpu64}rdi{$else}edi{$endif},8));
AddChild(node,prefix+'bp='+inttohex(c.{$ifdef cpu64}rbp{$else}ebp{$endif},8));
AddChild(node,prefix+'sp='+inttohex(c.{$ifdef cpu64}rsp{$else}esp{$endif},8));
AddChild(node,prefix+'ip='+inttohex(c.{$ifdef cpu64}rip{$else}eip{$endif},8));
{$ifdef cpu64}
if processhandler.is64bit then
begin
AddChild(node,'r8='+inttohex(c.r8,8));
AddChild(node,'r9='+inttohex(c.r9,8));
AddChild(node,'r10='+inttohex(c.r10,8));
AddChild(node,'r11='+inttohex(c.r11,8));
AddChild(node,'r12='+inttohex(c.r12,8));
AddChild(node,'r13='+inttohex(c.r13,8));
AddChild(node,'r14='+inttohex(c.r14,8));
AddChild(node,'r15='+inttohex(c.r15,8));
end;
{$endif}
threadTreeview.items.AddChild(node,'cs='+inttohex(c.SegCs,8));
{$ifdef cpu64}
//context has room for some extra data
AddChild(node,'fsbase='+inttohex(c.P2Home,8));
AddChild(node,'gsbase='+inttohex(c.P3Home,8));
{$endif}
end;
end;
end
else
begin
//threads can only be inspected when debugging (for now)
beep;
allowexpansion:=false;
exit;
end;
end
else
begin
th:=OpenThread(THREAD_QUERY_INFORMATION or THREAD_GET_CONTEXT, false, tid);
if th<>0 then
begin
zeromemory(@c,SizeOf(c));
c.ContextFlags:=CONTEXT_ALL or CONTEXT_EXTENDED_REGISTERS;
{$ifdef cpu64}
use32bitcontext:=(not processhandler.is64Bit) and (ssctrl in GetKeyShiftState);
if use32bitcontext then
begin
//override, the user wants the 32-bit context
ZeroMemory(@c32, sizeof(c32));
c32.ContextFlags:=CONTEXT_ALL;
x:=WOW64GetThreadContext(th, c32);
if x then
begin
//convert
c.Dr0:=c32.Dr0;
c.Dr1:=c32.Dr1;
c.Dr2:=c32.Dr2;
c.Dr3:=c32.Dr3;
c.Dr6:=c32.Dr6;
c.Dr7:=c32.Dr7;
c.Rax:=c32.eax;
c.Rbx:=c32.ebx;
c.Rcx:=c32.ecx;
c.Rdx:=c32.edx;
c.Rsi:=c32.esi;
c.Rdi:=c32.edi;
c.Rbp:=c32.ebp;
c.Rsp:=c32.esp;
c.Rip:=c32.eip;
c.SegCs:=c32.SegCs;
c.SegGs:=c32.seggs;
c.segfs:=c32.SegFs;
end;
end
else
{$endif}
x:=getThreadContext(th, c);
if x then
begin
threadTreeview.items.AddChild(node,'dr0='+inttohex(c.Dr0,{$ifdef cpu64}16{$else}8{$endif}));
threadTreeview.items.AddChild(node,'dr1='+inttohex(c.Dr1,{$ifdef cpu64}16{$else}8{$endif}));
threadTreeview.items.AddChild(node,'dr2='+inttohex(c.Dr2,{$ifdef cpu64}16{$else}8{$endif}));
threadTreeview.items.AddChild(node,'dr3='+inttohex(c.Dr3,{$ifdef cpu64}16{$else}8{$endif}));
threadTreeview.items.AddChild(node,'dr6='+inttohex(c.Dr6,{$ifdef cpu64}16{$else}8{$endif}));
s:='dr7='+inttohex(c.Dr7,{$ifdef cpu64}16{$else}8{$endif});
if c.dr7 and 1=1 then
begin
rw:=(c.dr7 shr 16) and 3;
len:=(c.dr7 shr 18) and 3;
drinfo:=drinfo+'1('+rw2str(rw)+' - '+len2str(len)+') ';
end;
if c.dr7 and 4=4 then
begin
rw:=(c.dr7 shr 16) and 3;
len:=(c.dr7 shr 18) and 3;
drinfo:=drinfo+'2('+rw2str(rw)+' - '+len2str(len)+') ';
end;
if c.dr7 and 16=16 then
begin
s:=s+'3(';
rw:=(c.dr7 shr 16) and 3;
len:=(c.dr7 shr 18) and 3;
drinfo:=drinfo+'3('+rw2str(rw)+' - '+len2str(len)+') ';
end;
if c.dr7 and 64=64 then
begin
rw:=(c.dr7 shr 16) and 3;
len:=(c.dr7 shr 18) and 3;
drinfo:=drinfo+'4('+rw2str(rw)+' - '+len2str(len)+') ';
end;
if drinfo<>'' then
s:=s+' :'+drinfo;
threadTreeview.items.AddChild(node,s);
if processhandler.is64Bit then
prefix:='r'
else
prefix:='e';
threadTreeview.items.AddChild(node,prefix+'ax='+inttohex(c.{$ifdef cpu64}rax{$else}eax{$endif},8));
threadTreeview.items.AddChild(node,prefix+'bx='+inttohex(c.{$ifdef cpu64}rbx{$else}ebx{$endif},8));
threadTreeview.items.AddChild(node,prefix+'cx='+inttohex(c.{$ifdef cpu64}rcx{$else}ecx{$endif},8));
threadTreeview.items.AddChild(node,prefix+'dx='+inttohex(c.{$ifdef cpu64}rdx{$else}edx{$endif},8));
threadTreeview.items.AddChild(node,prefix+'si='+inttohex(c.{$ifdef cpu64}rsi{$else}esi{$endif},8));
threadTreeview.items.AddChild(node,prefix+'di='+inttohex(c.{$ifdef cpu64}rdi{$else}edi{$endif},8));
threadTreeview.items.AddChild(node,prefix+'bp='+inttohex(c.{$ifdef cpu64}rbp{$else}ebp{$endif},8));
threadTreeview.items.AddChild(node,prefix+'sp='+inttohex(c.{$ifdef cpu64}rsp{$else}esp{$endif},8));
threadTreeview.items.AddChild(node,prefix+'ip='+inttohex(c.{$ifdef cpu64}rip{$else}eip{$endif},8));
{$ifdef cpu64}
if processhandler.is64bit then
begin
threadTreeview.items.AddChild(node,'r8='+inttohex(c.r8,8));
threadTreeview.items.AddChild(node,'r9='+inttohex(c.r9,8));
threadTreeview.items.AddChild(node,'r10='+inttohex(c.r10,8));
threadTreeview.items.AddChild(node,'r11='+inttohex(c.r11,8));
threadTreeview.items.AddChild(node,'r12='+inttohex(c.r12,8));
threadTreeview.items.AddChild(node,'r13='+inttohex(c.r13,8));
threadTreeview.items.AddChild(node,'r14='+inttohex(c.r14,8));
threadTreeview.items.AddChild(node,'r15='+inttohex(c.r15,8));
end;
{$endif}
threadTreeview.items.AddChild(node,'cs='+inttohex(c.SegCs,8));
if GetThreadSelectorEntry(th, c.segFs, ldtentry) then
threadTreeview.items.AddChild(node,'fsbase='+inttohex(ldtentry.BaseLow+ldtentry.HighWord.Bytes.BaseMid shl 16+ldtentry.HighWord.Bytes.BaseHi shl 24,8));
if GetThreadSelectorEntry(th, c.SegGs, ldtentry) then
threadTreeview.items.AddChild(node,'gsbase='+inttohex(ldtentry.BaseLow+ldtentry.HighWord.Bytes.BaseMid shl 16+ldtentry.HighWord.Bytes.BaseHi shl 24,8));
end
else threadTreeview.items.AddChild(node, rsCouldnTObtainContext);
closehandle(th);
end else
threadTreeview.items.AddChild(node, rsCouldnTOpenHandle);
end;
AllowExpansion:=true;
end
else
AllowExpansion:=false;
end;
initialization
{$i frmThreadlistunit.lrs}
end.