Przede wszystkim powinieneś wziąć się za ogarnięcie podstaw. A dopiero później pisać coś, co współpracuje z Cheat Engine tak jak chcesz. Poza tym kod funkcji odczytującej string z pamięci podchodzi mi pod gimbusiarską tibie. Nie lepiej dodać obsługę błedów i zrobić to na przykład tak dla konkretnego PID'u, a nie klasy czy tytułu okna?
function MemReadString(ProcessId, Address : DWORD) : string;
var
I : Integer;
NB : longword;
HProcess : Cardinal;
Temp : array[1..MAX_PATH] of Byte;
begin
Result := '';
HProcess := OpenProcess(PROCESS_ALL_ACCESS, False, ProcessId);
if HProcess > 0 then
begin
ReadProcessMemory(HProcess, Ptr(Address), @Temp[1], Length(Temp), NB);
CloseHandle(HProcess);
for I := Low(Temp) to High(Temp) do
begin
if (Temp[I] < 32) then
begin
Break;
end;
Result := Result + Chr(Temp[i]);
end;
end;
end;
Poza tym Cheat Engine ma otwarty kod, więc lepiej jego sobie pomodyfikować, a nie męczyć się w odczytywanie procesu. Poza tym pytałeś jak wykryć dodawanie do ListBox'a i to kod, który podał @kAzek robi to doskonale. Sprawdziłem. Poniżej masz działający kod z dodanym tylko jednym buttonem dla testów. Natomiast samo czytanie z procesu Cheat Engine robić powinieneś spróbować zrobić według mnie w wątku. Może być z użyciem klasy TThread
. Przykład użycia już podał Ci @kAzek ewentualnie sobie pogooglouj za przykładami i informacjami o wątkach. Ewentualnie można to zrobić w WinAPI z użyciem funkcji CreateThread
. A błąd w kodzie, który wkleiłeś polegal na tym, że zapętlił się on, bo bez sensu wywoływałeś go w kółko z samego z siebie i stąd błąd Stack overflow
.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, MMSystem;
type
TListBoxSetString = procedure(Sender : TObject; Index : Integer; Value : string) of object;
TListBox = class(StdCtrls.TListBox)
private
fOnListBoxSetString : TListBoxSetString;
protected
procedure ListBoxAddString(var Msg : TMessage); message LB_ADDSTRING;
procedure ListBoxInsertString(var Msg : TMessage); message LB_INSERTSTRING;
public
property OnListBoxSetString : TListBoxSetString read fOnListBoxSetString
write fOnListBoxSetString;
end;
TPlaySoundThread = class(TThread)
protected
procedure Execute; override;
public
destructor Destroy; override;
end;
type
TForm1 = class(TForm)
Edit1 : TEdit;
ListBox1 : TListBox;
ListBox2 : TListBox;
Button1 : TButton;
procedure FormCreate(Sender : TObject);
procedure Button1Click(Sender : TObject);
private
fPlaySoundThread : TPlaySoundThread;
fPlayThreadHandle : Cardinal;
procedure OnListBoxSetString(Sender : TObject; Index : Integer; Value : string);
public
end;
var
Form1 : TForm1;
implementation
{$R *.dfm}
procedure TListBox.ListBoxAddString(var Msg : TMessage);
var
s : string;
begin
inherited;
s := PChar(Msg.LParam);
if Assigned(fOnListBoxSetString) then
fOnListBoxSetString(Self, Self.Items.Count - 1, s);
end;
procedure TListBox.ListBoxInsertString(var Msg : TMessage);
var
s : string;
begin
inherited;
s := PChar(Msg.LParam);
if Assigned(fOnListBoxSetString) then
fOnListBoxSetString(Self, Msg.WParam, s);
end;
procedure TPlaySoundThread.Execute;
begin
FreeOnTerminate := True;
PlaySound('alarm.wav', 0, SND_FILENAME);
end;
destructor TPlaySoundThread.Destroy;
begin
PlaySound(nil, 0, 0);
inherited;
end;
procedure TForm1.FormCreate(Sender : TObject);
begin
ListBox1.OnListBoxSetString := OnListBoxSetString;
end;
procedure TForm1.OnListBoxSetString(Sender : TObject; Index : Integer; Value : string);
var
ExitCode : Cardinal;
begin
if Edit1.text = Value then
begin
GetExitCodeThread(fPlayThreadHandle, ExitCode);
if ExitCode <> STILL_ACTIVE then
begin
fPlaySoundThread := TPlaySoundThread.Create(True);
fPlayThreadHandle := fPlaySoundThread.Handle;
fPlaySoundThread.Resume;
end;
end;
end;
procedure TForm1.Button1Click(Sender : TObject);
begin
ListBox1.Items.Add('Test');
end;
Czyli podsumowując. Pomoc i kod, które zaoferował @kAzek pomagają ładnie wykryć zmiany w ListBoxie i wystąpienie jako dodanego lub wstawionego elementu tego z treścią podaną w Edit1. Do tego nie potrzeba wcale jak widzisz Timer'a. Natomiast wykrywanie zmian w procesie Cheat Engine zrób po swojemu. Chociaż jak wspomniałem, zrobił bym to raczej w wątku w pętli while not Terminated do
, a nie Timerze.
Chociaż to trochę zabawa "na około". Ponieważ jak pisałem skoro Cheat Engine ma własny kod źródlowy (starsze wersje jaką częściej używam jak 5.6 ma go w Delphi, a nowsze o ile wiem są pisane i kompilowane pod datmowym Lazarusem). To nic nie stoi na przeszkodzie dodać to, co potrzeba do kodu źródlowego operaując na jego kopii. I sobie to przekompilować.