unit VEHDebugger;

{$mode delphi}

interface

{$ifdef windows}
uses

  jwaNtStatus, Windows,
  Classes, SysUtils,symbolhandler, symbolhandlerstructs,
  VEHDebugSharedMem,cefuncproc, autoassembler,newkernelhandler,DebuggerInterface,
  Clipbrd;

type

  TVEHDebugInterface=class(TDebuggerInterface)
  private
    guid: TGUID; //to indentify this specific debugger
    guidstring: string;

    HasDebugEvent: THandle;
    HasHandledDebugEvent: THandle;

    ConfigFileMapping: THandle;
    VEHDebugView: PVEHDebugSharedMem;

    active: boolean;
    is64bit: boolean; //stored local so it doesn't have to evaluate the property (saves some time)
    hasPausedProcess: boolean;

    fisInjectedEvent: boolean;
    injectedEvents: TList;

    lastthreadlist: TStringList;
    lastthreadpoll: qword;

    Heartbeat: TThread;

    CurrentThread: THandle;

    procedure SynchronizeNoBreakList;
    procedure DoThreadPoll;
  public
    function isInjectedEvent: boolean; override;
    function WaitForDebugEvent(var lpDebugEvent: TDebugEvent; dwMilliseconds: DWORD): BOOL; override;
    function ContinueDebugEvent(dwProcessId: DWORD; dwThreadId: DWORD; dwContinueStatus: DWORD): BOOL; override;
    function SetThreadContext(hThread: THandle; const lpContext: TContext; isFrozenThread: Boolean=false): BOOL; override;
    function GetThreadContext(hThread: THandle; var lpContext: TContext; isFrozenThread: Boolean=false):  BOOL; override;

    function DebugActiveProcess(dwProcessId: DWORD): WINBOOL; override;
    function DebugActiveProcessStop(dwProcessID: DWORD): WINBOOL; override;
    procedure AddToNoBreakList(threadid: integer); override;
    procedure RemoveFromNoBreakList(threadid: integer); override;
    destructor destroy; override;
    constructor create;
  end;

{$endif}

implementation

{$ifdef windows}
uses ProcessHandlerUnit, Globals, dialogs;

resourcestring
  rsErrorWhileTryingToCreateTheConfigurationStructure = 'Error while trying '
    +'to create the configuration structure! (Which effectively renders this '
    +'whole feature useless) Errorcode=%s';
  rsCheatEngineFailedToGetIntoTheConfig = 'Cheat Engine failed to get into '
    +'the config of the selected program. (Error=%s)';
  rsFailureDuplicatingTheEventHandlesToTheOtherProcess = 'Failure duplicating '
    +'the event handles to the other process';
  rsVEHDebugError = 'VEH Debug error';
  rsFailureDuplicatingTheFilemapping = 'Failure duplicating the filemapping';
  rsTheVEHDllSeemsToHaveFailedToLoad = 'The VEH dll seems to have failed to load';
  rsWrongVEHDllVersion = 'The version of the VEH dll inside the target process (%x) does not match what was expected %x';


type
  TInjectedEvent=class
  public
    eventtype: (etThreadCreate,etThreadDestroy); //0=create thread, 1=destroythread
    threadid: integer;
  end;

  THeartBeat=class(TThread)
  private
    owner: TVEHDebugInterface;
    doVersionCheck: boolean;
    procedure invalidVersionMessage;
    procedure startVersionCheck;
  protected
    procedure execute; override;
  end;

procedure THeartBeat.startVersionCheck;
begin
  doVersionCheck:=true;
end;

procedure THeartBeat.invalidVersionMessage;
begin
  if owner.VEHDebugView.VEHVersion=0 then
    MessageDlg(rsTheVEHDllSeemsToHaveFailedToLoad, mtError, [mbok], 0)
  else
    MessageDlg(format(rsWrongVEHDllVersion, [owner.VEHDebugView.VEHVersion, DWORD($cece0000+VEHVERSION)]), mtWarning, [mbok], 0);
end;

procedure THeartBeat.execute;
var invalidversion: integer;
begin
  invalidversion:=0;
  while not terminated do
  begin
    inc(owner.VEHDebugView.heartbeat);
    sleep(250);

    if doVersionCheck and (owner.VEHDebugView.VEHVersion<>$cece0000+VEHVERSION) then
    begin
      inc(invalidversion);
      if invalidversion=10 then //(10*500 ms=5 seconds);
        queue(invalidVersionMessage);
    end;
  end;
end;

constructor TVEHDebugInterface.create;
begin
  inherited create;
  fDebuggerCapabilities:=[dbcSoftwareBreakpoint,dbcHardwareBreakpoint, dbcExceptionBreakpoint];
  name:='VEH Debugger';

  fmaxSharedBreakpointCount:=4;

  InjectedEvents:=Tlist.create;
  LastThreadList:=TStringList.create;

  lastthreadlist.Sorted:=true;
  lastthreadlist.Duplicates:=dupIgnore;
end;


destructor TVEHDebugInterface.destroy;
begin
  if heartbeat<>nil then
  begin
    heartbeat.Terminate;
    heartbeat.WaitFor;
    freeandnil(heartbeat);
  end;

  if HasDebugEvent<>0 then
    closehandle(HasDebugEvent);

  if HasHandledDebugEvent<>0 then
    closehandle(HasHandledDebugEvent);

  if injectedEvents<>nil then
    freeandnil(InjectedEvents);

  inherited destroy;
end;

function TVEHDebugInterface.SetThreadContext(hThread: THandle; const lpContext: TContext; isFrozenThread: Boolean=false): BOOL;
var c: PContext;
{$ifdef cpu64}
    c32: PContext32 absolute c;
{$endif}
begin


  if isFrozenThread then //use the VEHDebugView context
  begin
    result:=true;

    c:=@VEHDebugView.CurrentContext[0];
    {$ifdef cpu64}
    if not is64bit then
    begin
      //the given context needs to be converted to 32-bit

      //c32.ContextFlags:=lpcontext.ContextFlags;
      c32.Dr0:=lpcontext.Dr0;
      c32.Dr1:=lpcontext.Dr1;
      c32.Dr2:=lpcontext.Dr2;
      c32.Dr3:=lpcontext.Dr3;
      c32.Dr6:=lpcontext.Dr6;
      c32.Dr7:=lpcontext.Dr7;

      c32.SegGs:=lpcontext.SegGs;
      c32.SegFs:=lpcontext.SegFs;
      c32.SegEs:=lpcontext.SegEs;
      c32.SegDs:=lpcontext.SegDs;

      c32.edi:=lpcontext.rdi;
      c32.esi:=lpcontext.rsi;
      c32.ebx:=lpcontext.rbx;
      c32.edx:=lpcontext.rdx;
      c32.ecx:=lpcontext.rcx;
      c32.eax:=lpcontext.rax;
      c32.ebp:=lpcontext.rbp;
      c32.eip:=lpcontext.rip;
      c32.SegCs:=lpcontext.SegCs;
      c32.EFlags:=lpcontext.EFlags;
      c32.esp:=lpcontext.rsp;
      c32.SegSs:=lpcontext.SegSs;

      CopyMemory(@c32.ext, @lpContext.fltsave,sizeof(c32.ext));
      CopyMemory(@c32.FloatSave.RegisterArea[0], @lpContext.fltsave.FloatRegisters[0], 10*8);
    end else c^:=lpContext;

   // end;// else lpContext:=c^;
    {$else}
    c^:=lpContext;
    {$endif}

    //Interesting effect: result:=NewKernelHandler.SetThreadContext(hThread,lpContext);
  end
  else
    result:=NewKernelHandler.SetThreadContext(hThread,lpContext);
end;

function TVEHDebugInterface.GetThreadContext(hThread: THandle; var lpContext: TContext; isFrozenThread: Boolean=false):  BOOL;
var c: PContext;
{$ifdef cpu64}
    c32: PContext32 absolute c;
    i: integer;
{$endif}

begin
  if isFrozenThread then //use the VEHDebugView context
  begin
    OutputDebugString('VEH GetThreadContext. From frozen');
    result:=true;
    c:=@VEHDebugView.CurrentContext[0];
    {$ifdef cpu64}
    if not is64bit then
    begin
      ZeroMemory(@lpContext,sizeof(TContext));
      lpContext.ContextFlags:=c32.ContextFlags;
      lpContext.Dr0:=c32.Dr0;
      lpContext.Dr1:=c32.Dr1;
      lpContext.Dr2:=c32.Dr2;
      lpContext.Dr3:=c32.Dr3;
      lpContext.Dr6:=c32.Dr6;
      lpContext.Dr7:=c32.Dr7;


      lpContext.SegGs:=c32.SegGs;
      lpContext.SegFs:=c32.SegFs;
      lpContext.SegEs:=c32.SegEs;
      lpContext.SegDs:=c32.SegDs;

      lpContext.Rdi:=c32.Edi;
      lpContext.Rsi:=c32.Esi;
      lpContext.Rbx:=c32.Ebx;
      lpContext.Rdx:=c32.Edx;
      lpContext.Rcx:=c32.Ecx;
      lpContext.Rax:=c32.Eax;
      lpContext.Rbp:=c32.Ebp;
      lpContext.Rip:=c32.Eip;
      lpContext.SegCs:=c32.SegCs;
      lpContext.EFlags:=c32.EFlags;
      lpContext.Rsp:=c32.Esp;
      lpContext.SegSs:=c32.SegSs;

      CopyMemory(@lpcontext.FltSave, @c32.ext,sizeof(c32.ext));
    end else lpContext:=c^;
    {$else}
    lpContext:=c^;
    {$endif}


  end
  else
  begin
    OutputDebugString('VEH GetThreadContext. not frozen');
    result:=NewKernelHandler.GetThreadContext(hThread,lpContext);
  end;
end;

function TVEHDebugInterface.isInjectedEvent: boolean;
begin
  result:=fisInjectedEvent;
end;

function TVEHDebugInterface.WaitForDebugEvent(var lpDebugEvent: TDebugEvent; dwMilliseconds: DWORD): BOOL;
var i: integer;
    c: PContext;
{$ifdef cpu64}
    c32: PContext32 absolute c;
{$endif}
    inj: TInjectedEvent;

begin
  currentThread:=0;  //just making sure

  if injectedEvents.count>0 then
  begin
    fisInjectedEvent:=true;
    //fill in lpDebugEvent

    inj:=TInjectedEvent(injectedEvents[0]);
    lpDebugEvent.dwProcessId:=processid;
    lpDebugEvent.dwThreadId:=inj.ThreadId;
    lpDebugEvent.Exception.dwFirstChance:=1;
    if inj.eventtype=etThreadCreate then
    begin
      //create thread
      lpDebugEvent.dwDebugEventCode:=CREATE_THREAD_DEBUG_EVENT;
      lpDebugEvent.CreateThread.hThread:=OpenThread(THREAD_ALL_ACCESS,false, inj.ThreadId);

      lpDebugEvent.CreateThread.lpStartAddress:=nil;
      lpDebugEvent.CreateThread.lpThreadLocalBase:=nil;

      CurrentThread:=OpenThread(THREAD_ALL_ACCESS,false, lpDebugEvent.dwThreadId);
      suspendThread(CurrentThread);
    end
    else
    begin
      //destroy thread
      lpDebugEvent.dwDebugEventCode:=EXIT_THREAD_DEBUG_EVENT;
      lpDebugEvent.ExitThread.dwExitCode:=0;
    end;

    inj.free;

    injectedEvents.Delete(0);
    exit(true);
  end
  else
    fisInjectedEvent:=false;

  result:=waitforsingleobject(HasDebugEvent, dwMilliseconds)=WAIT_OBJECT_0;
  if result then
  begin
    ZeroMemory(@lpDebugEvent, sizeof(TdebugEvent));


   // lpDebugEvent.dwDebugEventCode:=EXCEPTION_DEBUG_EVENT; //exception
    lpDebugEvent.dwProcessId:=VEHDebugView.ProcessID;
    lpDebugEvent.dwThreadId:=VEHDebugView.ThreadID;
    lpDebugEvent.Exception.dwFirstChance:=1;



    case VEHDebugView.Exception64.ExceptionCode of
      $ce000000: //create process
      begin
        lpDebugEvent.dwDebugEventCode:=CREATE_PROCESS_DEBUG_EVENT;
        lpDebugEvent.CreateProcessInfo.hFile:=0;
        lpDebugEvent.CreateProcessInfo.hProcess:=processhandle;
        lpDebugEvent.CreateProcessInfo.hThread:=OpenThread(THREAD_ALL_ACCESS,false, lpDebugEvent.dwThreadId);
      end;

      $ce000001: //create thread
      begin
        lpDebugEvent.dwDebugEventCode:=CREATE_THREAD_DEBUG_EVENT;
        lpDebugEvent.CreateThread.hThread:=OpenThread(THREAD_ALL_ACCESS,false, lpDebugEvent.dwThreadId);
        lpDebugEvent.CreateThread.lpStartAddress:=nil;
        lpDebugEvent.CreateThread.lpThreadLocalBase:=nil;
        lastthreadlist.Add(inttohex(lpDebugEvent.dwThreadId,1));
        lastthreadpoll:=GetTickCount64;


      end;

      $ce000002: //destroy thread
      begin
        lpDebugEvent.dwDebugEventCode:=EXIT_THREAD_DEBUG_EVENT;
        lpDebugEvent.ExitThread.dwExitCode:=0;

        i:=lastthreadlist.indexof(inttohex(lpDebugEvent.dwThreadId,1));
        if i<>-1 then
          lastthreadlist.Delete(i);
      end;


      DBG_PRINTEXCEPTION_C:
      begin
        lpDebugEvent.dwDebugEventCode:=OUTPUT_DEBUG_STRING_EVENT;
        lpDebugEvent.DebugString.fUnicode:=0;
        if is64bit then
        begin
          lpDebugEvent.DebugString.nDebugStringLength:=VEHDebugView.Exception64.ExceptionInformation[0];
          lpDebugEvent.DebugString.lpDebugStringData:=pointer(ptrUint(VEHDebugView.Exception64.ExceptionInformation[1]));
        end
        else
        begin
          lpDebugEvent.DebugString.nDebugStringLength:=VEHDebugView.Exception32.ExceptionInformation[0];
          lpDebugEvent.DebugString.lpDebugStringData:=pointer(ptrUint(VEHDebugView.Exception32.ExceptionInformation[1]));
        end;
      end;

      else
      begin
        //unknown event, standard exception
        lpDebugEvent.dwDebugEventCode:=EXCEPTION_DEBUG_EVENT;
        lpDebugEvent.Exception.ExceptionRecord.ExceptionRecord:=nil;
        {$ifdef cpu64}
        if is64bit then
        begin




          lpDebugEvent.Exception.ExceptionRecord.ExceptionCode:=VEHDebugView.Exception64.ExceptionCode;
          lpDebugEvent.Exception.ExceptionRecord.ExceptionFlags:=VEHDebugView.Exception64.ExceptionFlags;
          lpDebugEvent.Exception.ExceptionRecord.ExceptionRecord:=pointer(VEHDebugView.Exception64.ExceptionRecord);
          lpDebugEvent.Exception.ExceptionRecord.ExceptionAddress:=pointer(VEHDebugView.Exception64.ExceptionAddress);
          lpDebugEvent.Exception.ExceptionRecord.NumberParameters:=VEHDebugView.Exception64.NumberParameters;

          for i:=0 to VEHDebugView.Exception64.NumberParameters-1 do
            lpDebugEvent.Exception.ExceptionRecord.ExceptionInformation[i]:=VEHDebugView.Exception64.ExceptionInformation[i];
        end
        else
        {$endif}
        begin
          lpDebugEvent.Exception.ExceptionRecord.ExceptionCode:=VEHDebugView.Exception32.ExceptionCode;
          lpDebugEvent.Exception.ExceptionRecord.ExceptionFlags:=VEHDebugView.Exception32.ExceptionFlags;
          lpDebugEvent.Exception.ExceptionRecord.ExceptionRecord:=pointer(ptrUint(VEHDebugView.Exception32.ExceptionRecord));
          lpDebugEvent.Exception.ExceptionRecord.ExceptionAddress:=pointer(ptrUint(VEHDebugView.Exception32.ExceptionAddress));
          lpDebugEvent.Exception.ExceptionRecord.NumberParameters:=VEHDebugView.Exception32.NumberParameters;

          for i:=0 to VEHDebugView.Exception32.NumberParameters-1 do
            lpDebugEvent.Exception.ExceptionRecord.ExceptionInformation[i]:=VEHDebugView.Exception32.ExceptionInformation[i];
        end;

        if lpdebugEvent.Exception.ExceptionRecord.ExceptionCode=EXCEPTION_BREAKPOINT then
        begin
          c:=@VEHDebugView.CurrentContext[0];
          {$ifdef cpu64}
          if is64bit then
            c.Rip:=c.rip+1
          else
            c32.Eip:=c32.eip+1;
          {$else}
            c.eip:=c.eip+1
          {$endif}



        end;

      end;
    end;

    hasPausedProcess:=true;

  end;

  if (lastthreadpoll>0) and (gettickcount64>lastthreadpoll+500) then
    DoThreadPoll; //adds injected events if needed
end;

function TVEHDebugInterface.ContinueDebugEvent(dwProcessId: DWORD; dwThreadId: DWORD; dwContinueStatus: DWORD): BOOL;
begin
  hasPausedProcess:=false;
  VEHDebugView.ContinueMethod:=dwContinueStatus;

  if fisInjectedEvent then
    fisInjectedEvent:=false
  else
    SetEvent(HasHandledDebugEvent);

  if currentthread<>0 then
  begin
    resumeThread(currentThread);
    closeHandle(currentThread);
    currentThread:=0;
  end;

  result:=true;
end;

function TVEHDebugInterface.DebugActiveProcess(dwProcessId: DWORD): WINBOOL;
var
  s: tstringlist;
  e: integer;
  prefix: string;
  testptr: ptruint;
  mi: tmoduleinfo;
  cfm: THandle;
  err: boolean;
begin
  try
    processhandler.processid:=dwProcessID;
    Open_Process;
    symhandler.reinitialize;

    is64bit:=processhandler.is64Bit;
    if is64bit then
      prefix:='-x86_64'
    else
      prefix:='-i386';


    result:=false;

   { if symhandler.getmodulebyname('vehdebug'+prefix+'.dll',mi) then
      exit; //no reattach supported right now     }




    CreateGUID(guid);
    guidstring:='"'+GUIDToString(guid)+'"';

    //guidstring:='Global\'+GUIDToString(guid);
    OutputDebugString('Creating filemap with name '+pchar(guidstring));

    ConfigFileMapping:=CreateFileMapping(INVALID_HANDLE_VALUE,nil,PAGE_READWRITE,0,sizeof(TVEHDebugSharedMem),pchar(guidstring));

    if ConfigFileMapping=0 then
    begin
      e:=getlasterror;
      OutPutDebugString('Failed:'+inttostr(e));
      raise exception.Create(Format(
        rsErrorWhileTryingToCreateTheConfigurationStructure, [inttostr(e)]));
    end;

    OutPutDebugString('Created the filemap');

    VEHDebugView:=MapViewOfFile(ConfigFileMapping,FILE_MAP_READ or FILE_MAP_WRITE,0,0,sizeof(TVEHDebugSharedMem));
    if (VEHDebugView=nil) then
    begin
      e:=GetLastError;
      closehandle(ConfigFileMapping);

      OutputDebugString('MapViewOfFile failed: '+inttostr(e));

      raise exception.Create(Format(rsCheatEngineFailedToGetIntoTheConfig, [
        inttostr(e)]));
    end;

    ZeroMemory(VEHDebugView,sizeof(TVEHDebugSharedMem));

    Heartbeat:=THeartBeat.Create(true);
    THeartBeat(Heartbeat).owner:=self;
    HeartBeat.Priority:=tpHighest;
    HeartBeat.Start;

    VEHDebugView.ThreadWatchMethod:=0; //vehthreadwatchmethod;
    if VEHRealContextOnThreadCreation then
      VEHDebugView.ThreadWatchMethodConfig:=TPOLL_TCREATEREALCONTEXT
    else
      VEHDebugView.ThreadWatchMethodConfig:=0;;



    HasDebugEvent:=CreateEvent(nil, false, false, nil);
    HasHandledDebugEvent:=CreateEvent(nil, false, false, nil);


    if not DuplicateHandle(GetCurrentProcess, HasDebugEvent, processhandle, @VEHDebugView^.HasDebugEvent, 0, false, DUPLICATE_SAME_ACCESS) then
      raise exception.Create(
        rsFailureDuplicatingTheEventHandlesToTheOtherProcess);

    if not DuplicateHandle(GetCurrentProcess, HasHandledDebugEvent, processhandle, @VEHDebugView^.HasHandledDebugEvent, 0, false, DUPLICATE_SAME_ACCESS) then
      raise exception.Create(
        rsFailureDuplicatingTheEventHandlesToTheOtherProcess);

    if not DuplicateHandle(GetCurrentProcess, ConfigFileMapping, processhandle, @cfm, 0, false, DUPLICATE_SAME_ACCESS) then
      raise exception.Create(rsFailureDuplicatingTheFilemapping);



    symhandler.waitforsymbolsloaded(true,'kernel32.dll');

    testptr:=symhandler.getAddressFromName('"vehdebug'+prefix+'.InitializeVEH"',false,err);
    if err or (testptr=0) then
    begin
      try
        InjectDll(cheatenginedir+'vehdebug'+prefix+'.dll');
      except
      end;
    end;

    symhandler.reinitialize;
    symhandler.waitforsymbolsloaded(true,'vehdebug'+prefix+'.dll');

    testptr:=symhandler.getAddressFromName('"vehdebug'+prefix+'.InitializeVEH"');

    s:=tstringlist.Create;
    try
      s.Clear;
      s.Add('"vehdebug'+prefix+'.ConfigName":');
      s.add('db '''+guidstring+''',0');

      s.Add('"vehdebug'+prefix+'.fm":');
      if processhandler.is64Bit then
        s.add('dq '+inttohex(cfm,8))
      else
        s.add('dd '+inttohex(cfm,8));

      s.Add('CreateThread("vehdebug'+prefix+'.InitializeVEH")');

      //Clipboard.SetTextBuf(pchar(s.text));

      if autoassemble(s,false) then
      begin
        //debugger is attached and ready to go
        active:=true;
        THeartBeat(Heartbeat).StartVersionCheck;
      end
      else
      begin

//        showmessage(s.text);
      end;

      result:=true;
    finally
      s.free;
    end;

  except
    on e: exception do
    begin
      messagebox(0, pchar(e.message), pchar(utf8toansi(rsVEHDebugError)), MB_OK or MB_ICONERROR);
      result:=false;
    end;
  end;
end;

function TVEHDebugInterface.DebugActiveProcessStop(dwProcessID: DWORD): WINBOOL;
var
  prefix: string;
  s: Tstringlist;
begin
  result:=false;

  if active and (processhandler.processid=processhandler.processid) then
  begin
    try
      if is64Bit then
        prefix:='-x86_64'
      else
        prefix:='-i386';

      s:=tstringlist.Create;
      try
        s.Add('CreateThread("vehdebug'+prefix+'.UnloadVEH")');
        if autoassemble(s,false) then
        begin
          active:=false;
          result:=true;
        end;
      finally
        s.free;
      end;
    except
    end;
  end;

  if heartbeat<>nil then
  begin
    heartbeat.Terminate;
    heartbeat.WaitFor;
    freeandnil(heartbeat);
  end;
end;

procedure TVEHDebugInterface.SynchronizeNoBreakList;
var i: integer;
begin
  for i:=0 to min(63, length(noBreakList)-1) do
    VEHDebugView.NoBreakList[i]:=noBreakList[i];

  VEHDebugView.NoBreakListSize:=min(63, length(noBreakList)-1);
end;

procedure TVEHDebugInterface.AddToNoBreakList(threadid: integer);
begin
  inherited AddToNoBreakList(threadid);
  SynchronizeNoBreakList;
end;

procedure TVEHDebugInterface.RemoveFromNoBreakList(threadid: integer);
begin
  inherited RemoveFromNoBreakList(threadid);
  SynchronizeNoBreakList;
end;


procedure TVEHDebugInterface.DoThreadPoll;
var
  currentlist: tstringlist;
  i: integer;

  inj: TInjectedEvent;
begin
  currentlist:=tstringlist.create;

  GetThreadList(currentlist);

  for i:=0 to currentlist.count-1 do
  begin
    if lastthreadlist.IndexOf(currentlist[i])=-1 then
    begin
      //new thread event
      inj:=TInjectedEvent.create;
      inj.eventtype:=etThreadCreate;
      inj.threadid:=strtoint('$'+currentlist[i]);

      injectedEvents.Add(inj);
      lastthreadlist.Add(currentlist[i]);
    end;
  end;

  i:=0;
  while i<=lastthreadlist.count-1 do
  begin
    if currentlist.IndexOf(lastthreadlist[i])=-1 then
    begin
      //destroyed thread event
      inj:=TInjectedEvent.create;
      inj.eventtype:=etThreadDestroy;
      inj.threadid:=strtoint('$'+lastthreadlist[i]);

      injectedEvents.Add(inj);
      lastthreadlist.Delete(i);
    end
    else
      inc(i);
  end;

  currentlist.free;

  lastthreadpoll:=GetTickCount64;
end;
{$endif}

end.

