unit ProcessWindowUnit; {$MODE Delphi} interface uses {$ifdef darwin} macport, {$endif} {$ifdef windows} jwawindows, windows, {$endif} LCLIntf, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, CEFuncProc,CEDebugger, ComCtrls, ImgList, Filehandler, Menus, LResources,{tlhelp32,}{$ifdef windows}vmxfunctions,{$endif} NewKernelHandler, debugHelper{, KIcon}, commonTypeDefs, math,lcltype, syncobjs, Contnrs, betterControls; type TProcesslistlong = class(tthread) private processcount: integer; process: array[0..9] of string; procedure drawprocesses; public processlist: tlistbox; procedure execute; override; end; {$ifdef windows} TIconFetchEntry=record processid: dword; winhandle: hwnd; //optional index: integer; icon: HIcon; //gets filled in end; PIconFetchEntry=^TIconFetchEntry; TIconFetchThread = class(TThread) private hasData: TEvent; requestsList: TList; //just the PID requestsListCS: TCriticalSection; resolvedList: TList; //PID and HICON record resolvedListCS: TCriticalSection; procedure getIcon(e: PIconFetchEntry); public function queueIconFetch(processid: dword; winhandle: hwnd; index: integer): hicon; overload; function queueIconFetch(processid: dword; index: integer): hicon; overload; procedure reset; procedure execute; override; constructor create; destructor destroy; override; end; {$endif} type { TProcessWindow } TProcessWindow = class(TForm) btnNetwork: TButton; btnAttachDebugger: TButton; CancelButton: TButton; FontDialog1: TFontDialog; TabHeader: TPageControl; plImageList: TImageList; MainMenu1: TMainMenu; MenuItem1: TMenuItem; MenuItem2: TMenuItem; MenuItem3: TMenuItem; miConvertPIDToDecimal: TMenuItem; miRefresh: TMenuItem; miCreateProcess: TMenuItem; miOpenFile: TMenuItem; N2: TMenuItem; miChangeFont: TMenuItem; MenuItem4: TMenuItem; MenuItem5: TMenuItem; N1: TMenuItem; miProcessListLong: TMenuItem; miOwnProcessesOnly: TMenuItem; OKButton: TButton; Panel1: TPanel; OpenDialog1: TOpenDialog; OpenDialog2: TOpenDialog; Panel3: TPanel; Panel5: TPanel; PopupMenu1: TPopupMenu; InputPIDmanually1: TMenuItem; Filter1: TMenuItem; ProcessList: TListBox; miShowInvisibleItems: TMenuItem; tsApplications: TTabSheet; tsProcesses: TTabSheet; tsWindows: TTabSheet; Timer1: TTimer; procedure btnNetworkClick(Sender: TObject); procedure Button1Click(Sender: TObject); procedure CancelButtonClick(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure MenuItem5Click(Sender: TObject); procedure miProcessListLongClick(Sender: TObject); procedure miChangeFontClick(Sender: TObject); procedure miOwnProcessesOnlyClick(Sender: TObject); procedure miRefreshClick(Sender: TObject); procedure OKButtonClick(Sender: TObject); procedure btnProcesslistClick(Sender: TObject); procedure btnWindowListClick(Sender: TObject); procedure btnCreateThreadClick(Sender: TObject); procedure btnAttachDebuggerClick(Sender: TObject); procedure btnOpenFileClick(Sender: TObject); procedure InputPIDmanually1Click(Sender: TObject); procedure Filter1Click(Sender: TObject); procedure btnProcessWatchClick(Sender: TObject); procedure FormResize(Sender: TObject); procedure btnProcessListLongClick(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); procedure PopupMenu1Popup(Sender: TObject); procedure ProcessListDrawItem(Control: TWinControl; Index: Integer; Rect: TRect; State: TOwnerDrawState); procedure FormShow(Sender: TObject); procedure ProcessListKeyPress(Sender: TObject; var Key: char); procedure miShowInvisibleItemsClick(Sender: TObject); procedure TabHeaderChange(Sender: TObject); procedure TabHeaderResize(Sender: TObject); procedure Timer1Timer(Sender: TObject); private { Private declarations } currentchar: integer; wantedheight: integer; ffilter: string; {$ifdef windows} IconFetchThread: TIconFetchThread; {$endif} processlistlong: tprocesslistlong; procedure refreshlist; procedure setbuttons; procedure SetFilter(filter:string); property filter:string read ffilter write setfilter; procedure filterlist; {$ifdef windows} procedure iconFetchedEvent(sender: TObject; processid: dword; index: integer; icon: hicon); {$endif} public { Public declarations } procedure PWOP(ProcessIDString:string); published property TabControl1: TPageControl read TabHeader; end; var ProcessWindow: TProcessWindow; commonProcessesList: tstringlist; implementation uses MainUnit, formsettingsunit, advancedoptionsunit,frmProcessWatcherUnit, memorybrowserformunit, networkConfig, ProcessHandlerUnit, processlist, globals, registry, fontSaveLoadRegistry, frmOpenFileAsProcessDialogUnit, networkInterfaceApi, MainUnit2, DebuggerInterfaceAPIWrapper, gdbserverconnectdialog, GDBServerDebuggerInterface, plugin; resourcestring rsIsnTAValidProcessID = '%s isn''t a valid processID'; rsPhysicalMemory = 'Physical Memory'; rsYouCanOnlyLoadEXEFiles = 'You can only load EXE files'; rsCreateProcess = 'Create Process'; rsOptionalLaunchParameters = 'Optional launch parameters'; rsAttachdebuggerornot = 'Are you sure you want to attach the debugger and not just open this process? (You can later on always attach the debugger)'; rsPleaseSelectAnotherProcess = 'Please select another process'; rsFirstSelectAProcess = 'First select a process!'; rsManualPID = 'Manual PID'; rsEnterTheProcessID = 'Enter the ProcessID'; rsFilter = 'Filter'; rsWhatAreYouLookingFor = 'What are you looking for?'; rsScanningClickToStop = 'Scanning (Click to stop)'; rsProcessListLong = 'Process List(long)'; rsProcessList = 'Process List'; rsApplications='Applications'; rsProcesses='Processes'; rsWindows='Windows'; var errortrace: integer; {$IFDEF windows} function SendMessageTimeout(hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM; fuFlags, uTimeout: UINT; var lpdwResult: ptruint): LRESULT; stdcall; external 'user32' name 'SendMessageTimeoutA'; procedure TIconFetchThread.getIcon(e: PIconFetchEntry); var s: string; HI: HICON; tempptruint: ptruint; begin HI:=0; if e^.winhandle<>0 then begin if SendMessageTimeout(e^.winhandle,WM_GETICON,ICON_SMALL,0,SMTO_ABORTIFHUNG, 200, tempptruint )<>0 then begin HI:=tempptruint; if HI=0 then begin if SendMessageTimeout(e^.winhandle,WM_GETICON,ICON_SMALL2,0,SMTO_ABORTIFHUNG, 100, tempptruint )<>0 then HI:=tempptruint; if HI=0 then if SendMessageTimeout(e^.winhandle,WM_GETICON,ICON_BIG,0,SMTO_ABORTIFHUNG, 50, tempptruint )<>0 then HI:=tempptruint; end; end; end; if HI=0 then begin s:=GetFirstModuleName(e^.processid); HI:=ExtractIcon(hinstance,pchar(s),0); end; if HI<>0 then e^.icon:=HI else e^.icon:=HWND(-1); resolvedListCS.Enter; resolvedList.Add(e); resolvedListCS.Leave; end; procedure TIconFetchThread.execute; var wr: TWaitResult; listnotempty: boolean; e: PIconFetchEntry; pid: dword; begin NameThreadForDebugging('TIconFetchThread', ThreadID); while not terminated do begin wr:=hasdata.WaitFor(1000); if terminated then exit; if wr=wrSignaled then begin listnotempty:=true; while listnotempty do begin //fetch an item from the list requestsListCS.enter; e:=requestsList.last; if e<>nil then requestsList.Delete(requestsList.Count-1); listnotempty:=requestsList.Count>0; requestsListCS.leave; //get the icon for this PID and then call the IconFetchedEvent if e<>nil then getIcon(e); end; end else if wr<>wrTimeout then break; end; end; function TIconFetchThread.QueueIconFetch(processid: dword; winhandle: hwnd; index: integer): HIcon; { Queues an processid and window for processing Changes the priority on request Returns the icon if it has already been processed } var found: boolean; i: integer; e: PIconFetchEntry; begin //first check if already in the list result:=0; found:=false; requestsListCS.enter; for i:=0 to requestsList.count-1 do begin e:=requestsList[i]; if (e^.processid=processid) and (e^.index=index) and (e^.winhandle=winhandle) then begin found:=true; requestsList.Delete(i); requestsList.Add(e); break; end; end; requestsListCS.leave; if not found then begin //check if in the resolve queue, and if so, return it now resolvedListCS.enter; for i:=0 to resolvedList.count-1 do begin e:=resolvedList[i]; if (e^.processid=processid) and (e^.index=index) and (e^.winhandle=winhandle) then begin resolvedlist.Delete(i); result:=e^.icon; found:=true; break; end; end; resolvedListCS.leave; end; if not found then begin getmem(e,sizeof(TIconFetchEntry)); e^.processid:=processid; e^.winhandle:=winhandle; e^.index:=index; e^.icon:=0; requestsListCS.enter; requestsList.Add(e); requestsListCS.leave; hasData.SetEvent; end; end; function TIconFetchThread.QueueIconFetch(processid: dword; index: integer): HIcon; begin result:=QueueIconFetch(processid, 0, index); end; procedure TIconFetchThread.reset; var i: integer; begin RemoveQueuedEvents(self); resolvedListCS.enter; for i:=0 to resolvedList.Count-1 do if resolvedList[i]<>nil then freemem(resolvedList[i]); resolvedList.Clear; resolvedListCS.leave; requestsListCS.enter; for i:=0 to requestsList.Count-1 do if requestsList[i]<>nil then freemem(requestsList[i]); requestsList.clear; requestsListCS.leave; end; constructor TIconFetchThread.create; begin hasData:=TEvent.create(nil,false,false,''); requestsList:=Tlist.create; requestsListCS:=TCriticalSection.Create; resolvedList:=TList.create; resolvedListCS:=TCriticalSection.create; inherited create(false); end; destructor TIconFetchThread.Destroy; begin terminate; hasdata.SetEvent; waitfor; reset; hasdata.free; requestsList.Free; requestsListCS.free; resolvedList.free; resolvedListCS.free; inherited destroy; end; {$ENDIF} procedure TProcessListLong.drawprocesses; var i: integer; begin if not terminated then begin processlist.Items.BeginUpdate; for i:=0 to processcount-1 do processlist.Items.Add(process[i]); processlist.Items.EndUpdate; processcount:=0; end; end; procedure TProcessListLong.execute; var i: dword; h: thandle; x: pchar; modulename:string; begin {$ifdef windows} i:=0; while not terminated and (i<$FFFFFFFF) do begin h:=windows.OpenProcess(ifthen(GetSystemType<=6,$1f0fff, process_all_access),false,i); if h<>0 then begin modulename:=getProcessnameFromProcessID(i); process[processcount]:=inttohex(i,8)+'-'+modulename; inc(processcount); if processcount>=10 then synchronize(drawprocesses); closehandle(h); end; if ((i mod 4096)=0) then if processcount>0 then synchronize(drawprocesses); inc(i); end; if processcount>0 then synchronize(drawprocesses); {$endif} end; procedure loadCommonProcessesList; var s: string; i,j: integer; begin s:=cheatenginedir+'commonProcessesList.txt'; if FileExists(s) then //if the list exists begin if commonProcessesList=nil then commonProcessesList:=tstringlist.create; try commonProcessesList.LoadFromFile(s{$if FPC_FULLVERSION >= 030200}, true{$endif}); for i:=commonProcessesList.Count-1 downto 0 do begin j:=pos('#', commonProcessesList[i]); if j>0 then commonProcessesList[i]:=copy(commonProcessesList[i], 1, j-1); commonProcessesList[i]:=uppercase(trim(commonProcessesList[i])); if commonProcessesList[i]='' then commonProcessesList.Delete(i); end; except end; end; end; function isInCommonProcessesList(processname: string): boolean; var i:integer; begin if commonProcessesList=nil then exit(false); for i:=0 to commonProcessesList.Count-1 do if commonProcessesList[i]=uppercase(copy(processname,10)) then exit(true); result:=false; end; procedure TProcessWindow.filterlist; var i:integer; {$IFDEF WINDOWS} pli: PProcessListInfo; {$ENDIF} s: string; begin if (filter='') and (commonProcessesList=nil) then exit; ffilter:=uppercase(ffilter); i:=0; while i'') and (pos(ffilter,uppercase(processlist.Items[i]))=0)) or isInCommonProcessesList(processlist.Items[i]) then begin {$IFDEF WINDOWS} if pli<>nil then begin if pli^.processIcon>0 then begin if pli^.processID<>GetCurrentProcessId then DestroyIcon(pli^.processIcon); pli^.processIcon:=0; end; freememandnil(pli); end; {$ENDIF} processlist.Items.Delete(i); end else inc(i); end; end; procedure TProcesswindow.SetFilter(filter:string); begin ffilter:=filter; refreshlist; end; procedure TProcessWindow.CancelButtonClick(Sender: TObject); begin mainform.canceled:=true; //ProcessWindow.close; ModalResult:=mrCancel; end; {$ifdef windows} procedure TProcessWindow.iconFetchedEvent(sender: TObject; processid: dword; index: integer; icon: hicon); var i: integer; pli: PProcessListInfo; begin if (index>=0) and (indexnil then begin if pli^.processID=processid then //making sure the list didn't change begin if pli^.processIcon=0 then begin pli^.processIcon:=icon; end else begin if (icon<>0) and (icon<>HWND(-1)) and (processid<>getcurrentprocessid) then begin DestroyIcon(icon); //not needed anymore (duplicates shouldn't happen...) end; end; end; end; end; end; {$endif} procedure TProcessWindow.FormCreate(Sender: TObject); var x: array of integer; reg: tregistry; begin {$ifdef darwin} {ProcessList.AnchorSideTop:=ProcessWindow.AnchorSideTop; ProcessList.AnchorSideLeft:=TabHeader.AnchorSideLeft; ProcessList.AnchorSideRight:=TabHeader.AnchorSideRight; ProcessList.AnchorSideBottom:=TabHeader.AnchorSideBottom; ProcessList.Anchors:=TabHeader.Anchors; TabHeader.TabIndex:=1; TabHeader.Visible:=false; } tsWindows.TabVisible:=false; tsWindows.Visible:=false; {$endif} {$ifdef windows} IconFetchThread:=TIconFetchThread.create; {$endif} tsApplications.Caption:=rsApplications; tsProcesses.Caption:=rsProcesses; tsWindows.Caption:=rsWindows; setlength(x,0); if LoadFormPosition(self,x) then begin autosize:=false; if length(x)>0 then TabHeader.TabIndex:=x[0]; if length(x)>1 then begin miOwnProcessesOnly.checked:=x[1]<>0; ProcessesCurrentUserOnly:=x[1]<>0; end; end else refreshlist; reg:=tregistry.create; try if reg.OpenKey('\Software\'+strCheatEngine+'\Process Window\Font'+darkmodestring,false) then LoadFontFromRegistry(processlist.Font, reg) else processlist.font.color:=colorset.FontColor; finally reg.free; end; if formSettings.cbUseGDBServer.checked then btnNetwork.Caption:='Connect to GDB'; end; procedure TProcessWindow.FormDestroy(Sender: TObject); var x: array of integer; begin setlength(x,3); x[0]:=TabHeader.TabIndex; x[1]:=ifthen(miOwnProcessesOnly.checked,1,0); SaveFormPosition(self,x); end; procedure TProcessWindow.MenuItem5Click(Sender: TObject); begin end; procedure TProcessWindow.miProcessListLongClick(Sender: TObject); begin btnProcessListLongClick(nil); end; procedure TProcessWindow.miChangeFontClick(Sender: TObject); var reg: tregistry; begin fontdialog1.font.assign(processlist.font); if fontdialog1.execute then begin //apply settings processlist.font.assign(FontDialog1.Font); //processlist.Canvas.Refresh; Timer1Timer(timer1); processlist.Repaint; reg:=tregistry.create; try if reg.OpenKey('\Software\'+strCheatEngine+'\Process Window\Font'+darkmodestring,true) then SaveFontToRegistry(FontDialog1.Font, reg); finally reg.free; end; end; end; procedure TProcessWindow.miOwnProcessesOnlyClick(Sender: TObject); begin ProcessesCurrentUserOnly:=miOwnProcessesOnly.checked; refreshlist; end; procedure TProcessWindow.miRefreshClick(Sender: TObject); begin refreshList; end; procedure TProcessWindow.btnNetworkClick(Sender: TObject); var host: string; port: word; begin if formsettings.cbUseGDBServer.checked then begin if MessageDlg('Connecting to GDB using this option instead of attaching the debugger to the native process will force the use of GDB read and write memory functions which is a lot slower, and you will not have access to local symbol info and some other tools you may be used to. Are you sure?', mtWarning, [mbyes, mbno],0)<>mryes then exit; GDBReadProcessMemory:=true; GDBWriteProcessMemory:=true; SkipVirtualProtectEx:=true; outputdebugstring('Using GDBServer debugger interface'); if CurrentDebuggerInterface<>nil then freeandnil(CurrentDebuggerInterface); if formsettings.cbLaunchGDBServer.Checked then begin port:=strtoint(formsettings.edtGDBPort.Text); CurrentDebuggerInterface:=TGDBServerDebuggerInterface.createAndConnect(formsettings.edtGDBServerCommand.Text, 'localhost', port); end else begin //spawn a dialog asking if getGDBHostAndPort(host, port) then CurrentDebuggerInterface:=TGDBServerDebuggerInterface.connectToExistingServer(host,port); end; if CurrentDebuggerInterface<>nil then begin tabheader.ShowTabs:=false; TabHeaderResize(nil); if TabHeader.TabIndex<>1 then TabHeader.Tabindex:=1; refreshlist; processlist.SetFocus; btnAttachDebugger.Visible:=false; if processlist.Count=0 then begin //this is a processless system MainForm.ProcessLabel.caption:='Remote System'; processhandler.processid:=$FFFFFFFE; processhandler.processhandle:=-2; startdebuggerifneeded(false); onAPIPointerChange:=TGDBServerDebuggerInterface(currentdebuggerinterface).OnApiPointerChange; onAPIPointerChange(nil); modalresult:=mrok; end; end; end else begin if frmNetworkConfig=nil then frmNetworkConfig:=tfrmNetworkConfig.create(self); if frmNetworkConfig.ShowModal=mrok then begin tabheader.ShowTabs:=false; TabHeaderResize(nil); if TabHeader.TabIndex<>1 then TabHeader.Tabindex:=1; refreshlist; processlist.SetFocus; end; end; end; procedure TProcessWindow.Button1Click(Sender: TObject); begin end; procedure TProcessWindow.setbuttons; begin end; procedure TProcessWindow.PWOP(ProcessIDString:string); var i:integer; begin val('$'+ProcessIDString,ProcessHandler.processid,i); if i<>0 then raise exception.Create(Format(rsIsnTAValidProcessID, [processidstring])); if Processhandle<>0 then ProcessHandler.ProcessHandle:=0; with mainform do begin if GetSystemType>=4 then begin cbSpeedhack.checked:=false; cbSpeedhack.Enabled:=true; cbunrandomizer.Checked:=false; cbunrandomizer.Enabled:=true; end; end; {$ifdef windows} if (processid<>0) and (UseFileAsMemory or Usephysical or usephysicaldbvm) then begin //swap back to processmemory UseFileAsMemory:=false; Usephysical:=false; usephysicaldbvm:=false; if formsettings.cbKernelOpenProcess.checked then UseDBKOpenProcess else DONTUseDBKOpenProcess; if formsettings.cbKernelQueryMemoryRegion.checked then UseDBKQueryMemoryRegion else DONTUseDBKQueryMemoryRegion; if formsettings.cbKernelReadWriteProcessMemory.checked then UseDBKReadWriteMemory else DONTUseDBKReadWriteMemory; end; {$endif} if (CurrentDebuggerInterface<>nil) and (CurrentDebuggerInterface is TGDBServerDebuggerInterface) then begin //use the currently selected processid for startdebuggerifneeded (for the vAttach packet) processhandler.processhandle:=-2; startdebuggerifneeded(false); onAPIPointerChange:=TGDBServerDebuggerInterface(currentdebuggerinterface).OnApiPointerChange; onAPIPointerChange(nil); processhandler.processid:=$fffffffe; //the processid is not needed anymore end else Open_Process; if ProcessHandle=0 then begin if not runningAsAdmin then begin raise exception.Create('Failed opening process. Likely due to lack of admin rights'); end; end; ProcessSelected:=true; {$ifdef windows} if (processid=0) and ((formsettings.cbKernelReadWriteProcessMemory.checked) or (dbvm_version>=$ce000004)) then begin ProcessHandler.processid:=$FFFFFFFF; if dbvm_version>=$ce000004 then DBKPhysicalMemoryDBVM else DBKPhysicalMemory; ProcessHandler.ProcessHandle:=$FFFFFFFF; end else begin if usephysical or usephysicaldbvm then DBKProcessMemory; end; {$endif} end; procedure TProcessWindow.OKButtonClick(Sender: TObject); var ProcessIDString: String; begin try Outputdebugstring('OK button click'); if Processlist.ItemIndex>-1 then begin unpause; DetachIfPossible; ProcessIDString:=copy(ProcessList.Items[Processlist.ItemIndex], 1, pos('-',ProcessList.Items[Processlist.ItemIndex])-1); Outputdebugstring('calling PWOP'); PWOP(ProcessIDString); if TabHeader.TabIndex=0 then MainForm.ProcessLabel.caption:=ProcessIDString+'-'+extractfilename(getProcessPathFromProcessID(processid)) else MainForm.ProcessLabel.caption:=ProcessList.Items[Processlist.ItemIndex]; Modalresult:=MROK; //ProcessWindow.close; end; except on e: exception do MessageDlg(e.Message,mtError,[mbok],0); end; //outputdebugstring('After ok click handler'); end; //button1click specific: procedure TProcessWindow.btnProcesslistClick(Sender: TObject); begin end; procedure TProcessWindow.btnWindowListClick(Sender: TObject); begin //miSkipSystemProcesses.visible:=false; end; procedure TProcessWindow.btnCreateThreadClick(Sender: TObject); var parameters: string; begin if Opendialog1.Execute then begin if Uppercase(extractfileext(opendialog1.FileName))<>'.EXE' then raise Exception.Create(rsYouCanOnlyLoadEXEFiles); parameters:=''; if not InputQuery(rsCreateProcess, rsOptionalLaunchParameters, parameters) then exit; unpause; detachIfPossible; Debuggerthread:=TDebuggerThread.MyCreate2(opendialog1.FileName, parameters); if not Debuggerthread.running then exit; mainForm.ProcessLabel.caption:=IntToHex(processid,8)+'-'+ExtractFileName(opendialog1.FileName); mainform.debugproc:=true; memorybrowser.show; modalresult:=mrOk; end; end; procedure TProcessWindow.btnAttachDebuggerClick(Sender: TObject); var ProcessIDString: String; i: Integer; oldpid,newpid: dword; starttime: qword; begin oldpid:=processid; if Processlist.ItemIndex>-1 then begin if MessageDlg(rsAttachdebuggerornot, mtConfirmation, [mbyes, mbno], 0)=mryes then begin unpause; DetachIfPossible; ProcessIDString:=''; i:=1; while ProcessList.Items[Processlist.ItemIndex][i]<>'-' do begin ProcessIDString:=ProcessIDString+ProcessList.Items[Processlist.ItemIndex][i]; inc(i); end; val('$'+ProcessIDString,newpid,i); if (Processhandle<>0) and (oldpid<>newpid) then begin CloseHandle(ProcessHandle); ProcessHandler.ProcessHandle:=0; end; try if processid=GetCurrentProcessId then raise exception.create(rsPleaseSelectAnotherProcess); starttime:=GetTickCount64; Debuggerthread:=TDebuggerThread.MyCreate2(newpid); except on e: exception do begin debuggerthread:=nil; MessageDlg(e.message, mtError,[mbok],0); exit; end; end; OutputDebugString('Debugger attach time='+(GetTickCount64-starttime).ToString); mainform.ProcessLabel.Caption:=ProcessList.Items[Processlist.ItemIndex]; ProcessSelected:=true; mainform.debugproc:=true; modalresult:=mrOK; end end else showmessage(rsFirstSelectAProcess); end; procedure TProcessWindow.btnOpenFileClick(Sender: TObject); begin {$ifdef windows} if opendialog2.execute then begin if frmOpenFileAsProcessDialog=nil then frmOpenFileAsProcessDialog:=tfrmOpenFileAsProcessDialog.create(self); if frmOpenFileAsProcessDialog.showmodal=mrok then begin DBKFileAsMemory(opendialog2.filename, frmOpenFileAsProcessDialog.startaddress); processselected:=true; ProcessHandler.ProcessHandle:=QWORD(-2); MainForm.ProcessLabel.caption:=extractfilename(opendialog2.FileName); MainForm.miSaveFile.visible:=true; ProcessHandler.processid:=$FFFFFFFF; Processhandler.is64Bit:=frmOpenFileAsProcessDialog.rb64.checked; modalresult:=mrok; end; end; {$else} MessageDlg('Not yet implemented', mtError,[mbok],0); {$endif} end; procedure TProcessWindow.InputPIDmanually1Click(Sender: TObject); var pid: string; begin pid:='0'; if InputQuery(rsManualPID, rsEnterTheProcessID+':', pid) then begin unpause; DetachIfPossible; pwop(pid); MainForm.ProcessLabel.caption:=pid; modalresult:=mrok; end; end; procedure TProcessWindow.Filter1Click(Sender: TObject); var fltr: string; begin fltr:=filter; if inputquery(rsFilter, rsWhatAreYouLookingFor, fltr) then filter:=fltr; end; procedure TProcessWindow.btnProcessWatchClick(Sender: TObject); begin if frmprocesswatcher=nil then frmprocesswatcher:=tfrmprocesswatcher.Create(mainform); frmprocesswatcher.show; modalresult:=mrcancel; end; procedure TProcessWindow.FormResize(Sender: TObject); begin //reset the button positions // setbuttons; end; procedure TProcessWindow.btnProcessListLongClick(Sender: TObject); begin if processlistlong=nil then begin processlist.Clear; miProcessListLong.Caption:=rsScanningClickToStop; processlistlong:=tprocesslistlong.create(true); processlistlong.processlist:=processlist; processlistlong.start; end else begin miProcessListLong.Caption:=rsProcessListLong; processlistlong.terminate; processlistlong.WaitFor; processlistlong.Free; processlistlong:=nil; end; end; procedure TProcessWindow.FormClose(Sender: TObject; var Action: TCloseAction); begin if processlistlong<>nil then begin processlistlong.terminate; processlistlong.WaitFor; processlistlong.Free; processlistlong:=nil; miProcessListLong.Caption:=rsProcessListLong; end; position:=poDesigned; end; procedure TProcessWindow.PopupMenu1Popup(Sender: TObject); begin miShowInvisibleItems.visible:=tabheader.TabIndex=2; end; procedure TProcessWindow.ProcessListDrawItem(Control: TWinControl; Index: Integer; Rect: TRect; State: TOwnerDrawState); var i: integer; t: string; sep: integer; pids: string; pid: dword; {$IFDEF WINDOWS} pli: PProcessListInfo; {$ENDIF} begin wantedheight:=ProcessList.canvas.TextHeight('QqJjWwSs')+3; {i:=ProcessList.canvas.TextHeight('QqJjWwSs')+3; if processlist.itemheight0 then begin try pids:=copy(t,1,sep-1); pid:=strtoint('$'+pids); t:=format('%.8d',[pid])+copy(t,sep); except end; end; end; if odSelected in state then processlist.Canvas.font.color:=clHighlightText else processlist.Canvas.font.color:=processlist.font.color; processlist.Canvas.TextOut(rect.Left+rect.Bottom-rect.Top+3,rect.Top,t); if getConnection<>nil then exit; {$ifdef windows} if getprocessicons and (processlist.Items.Objects[index]<>nil) then begin pli:=PProcessListInfo(processlist.Items.Objects[index]); if pli^.processIcon=0 then pli^.processIcon:=IconFetchThread.queueIconFetch(pli^.processID, pli^.winhandle, index); if (pli^.processIcon<>0) and (pli^.processIcon<>HWND(-1)) then DrawIconEx(processlist.Canvas.Handle, rect.left, rect.Top, pli^.processIcon, rect.Bottom-rect.Top,rect.Bottom-rect.Top,0,0,DI_NORMAL); end; {$endif} end; procedure TProcessWindow.FormShow(Sender: TObject); var tr: trect; preferedwidth: integer; tabwidth: integer; pc: integer; s: string; i: integer; begin if getconnection<>nil then tabheader.ShowTabs:=true; OKButton.Constraints.MinHeight:=trunc(1.2*btnAttachDebugger.height); CancelButton.Constraints.MinHeight:=OKButton.Constraints.MinHeight; loadCommonProcessesList; errortrace:=100; try errortrace:=101; processlist.canvas.Refresh; errortrace:=102; ProcessList.ItemHeight:=max(processlist.canvas.TextHeight('QqJjWwSs')+3, canvas.TextHeight('QqJjWwSs')+3); errortrace:=103; currentchar:=1; errortrace:=104; refreshlist; errortrace:=105; if autosize then begin autosize:=false; //first run or no saving positions preferedwidth:=max(clientwidth, canvas.TextWidth(' XXXXXXXX - XXXXXXXXXXXXXXXXXXXXXX ')); pc:=tabheader.PageCount; tabwidth:=0; for i:=0 to pc-1 do begin tr:=tabheader.TabRect(i); tabwidth:=tabwidth+tr.Width; end; tabwidth:=tabwidth+ canvas.TextWidth(' '); if tabwidth>preferedwidth then preferedwidth:=tabwidth; clientwidth:=preferedwidth; height:=mainform.Height-(mainform.height div 3); position:=poDesigned; position:=poMainFormCenter; end; errortrace:=106; processlist.SetFocus; except on e:exception do raise exception.create('FormShow exception ('+e.message+') at section '+inttostr(errortrace)); end; end; procedure TProcessWindow.ProcessListKeyPress(Sender: TObject; var Key: char); begin if key=#8 then filter:=copy(filter, 1, length(filter)-1) else if key in [chr(32)..chr(128)] then filter:=filter+key; end; procedure TProcessWindow.RefreshList; var i: integer; oldselectionindex: integer; oldselection: string; found: boolean; begin {$ifdef windows} IconFetchThread.reset; {$endif} processlist.Items.BeginUpdate; try oldselectionindex:=processlist.ItemIndex; if oldselectionindex<>-1 then oldselection:=processlist.Items[oldselectionIndex]; case TabHeader.TabIndex of 0: begin {$ifdef windows} getwindowlist2(processlist.Items); {$else} getapplicationlist(processlist.items); {$endif} end; 1: begin getprocesslist(processlist.items); end; 2: begin {$ifdef windows} GetWindowList(processlist.Items, miShowInvisibleItems.Checked); processlist.ItemIndex:=processlist.Items.Count-1; {$else} getprocesslist(processlist.items); {$endif} end; end; filterlist; if oldselectionindex=-1 then begin processlist.ItemIndex:=processlist.Items.Count-1; //go to the end end else begin i:=processlist.Items.IndexOf(oldselection); if i>=0 then begin processlist.ItemIndex:=i; end else begin //strip out the processid part and search for a entry with the appropriate processname (e.g restarted game) oldselection:=copy(oldselection,pos('-',oldselection)+1,length(oldselection)); found:=false; for i:=processlist.Items.Count-1 downto 0 do if pos(oldselection, processlist.items[i])>0 then begin processlist.ItemIndex:=i; found:=true; break; end; if not found then processlist.ItemIndex:=processlist.Items.Count-1; end; end; if filter<>'' then caption:=rsProcessList+' : *'+filter+'*' else caption:=rsProcessList; {$ifdef windows} if formsettings.cbKernelReadWriteProcessMemory.checked or (dbvm_version>=$ce000004) then //driver is active begin if TabHeader.TabIndex<=2 then //other script are on their own processlist.Items.Insert(0, '00000000-['+rsPhysicalMemory+']'); end; {$endif} finally processlist.items.EndUpdate; end; end; procedure TProcessWindow.miShowInvisibleItemsClick(Sender: TObject); begin refreshList; end; procedure TProcessWindow.TabHeaderChange(Sender: TObject); begin refreshList; end; procedure TProcessWindow.TabHeaderResize(Sender: TObject); var p: tpoint; begin p:=TabHeader.ClientToParent(point(0,0)); //if TabHeader.ShowTabs=false then // processlist.top:=tabheader.top // else processlist.Top:=p.Y; processlist.Left:=p.X; processlist.Width:=TabHeader.ClientWidth; // if tabheader.ShowTabs=false then processlist.Height:=TabHeader.ClientHeight // else // processlist.Height:=tabheader.Height; end; procedure TProcessWindow.Timer1Timer(Sender: TObject); var i: integer; {$ifdef windows} e: PIconFetchEntry; {$endif} begin try if processlist.itemheight<>wantedheight then begin ProcessList.ItemHeight:=wantedheight; processlist.canvas.Refresh; processlist.Repaint; end; {$ifdef windows} IconFetchThread.resolvedListCS.enter; try e:=nil; for i:=0 to IconFetchThread.resolvedList.count-1 do begin e:=PIconFetchEntry(IconFetchThread.resolvedList[i]); iconFetchedEvent(IconFetchThread, e^.processid, e^.index, e^.icon); freemem(e); end; IconFetchThread.resolvedList.clear; finally IconFetchThread.resolvedListCS.leave; end; if e<>nil then processlist.Repaint; {$endif} except timer1.enabled:=false; showmessage('timer issue'); end; end; initialization {$i ProcessWindowUnit.lrs} end.