MSSI
MSSI

Reputation: 194

Find running application of specified name but without any window

I have an application that starts with a regular window and includes a "hide" button. When this button is clicked, the application continues running and is visible in the Task Manager, but it no longer has any visible window.

I've tried several methods to programmatically find this application in Delphi:

  1. Using GetWindowText: This doesn't work because the application doesn't have a window when it's hidden.

  2. Using TProcessEntry32.szExeFile: This is unreliable because:

    • users can save the executable under any name
    • even if the executable has a known name, it doesn't appear in the TProcessEntry32 list
  3. Using WMI queries: this also fails to find the application by the name visible in the Task Manager.

My question is: how can I create a function in Delphi to detect whether this application is running, given the following constraints?

  1. The executable file name is unknown.
  2. The application doesn't have a visible window.
  3. It is hidden in the system but visible in the Task Manager.

=== edit ===

The application that I'm struggling with is designed for dishonestly manipulating user activity tracking. As previously mentioned, it initially appears as a window but can be later hidden from view. Even when hidden, it continues to simulate user activity. As mentioned earlier, users can change its name and start it, so the only constant in this case would be the name visible in the task manager. Unfortunately, I cannot locate it when it's hidden. Below is the code I'm currently using; please let me know if there are any mistakes.

This code works when application is visible (does not work when hidden)

   var WinTitle : array[0..256] of Char;
   begin
     if GetWindowText(Wnd, WinTitle, SizeOf(WinTitle)) > 0 then
     begin
       if pos('GDollar', WinTitle) > 0 then
         PostMessage(Wnd, WM_CLOSE, 0, 0);
     end;
     Result := True;
   end;

procedure TForm1.btn_1Click(Sender: TObject);
begin
  EnumWindows(@EnumWindowsProc, 0);
end;

I tried this - but no luck (and it's - obviously - painfully slow).

procedure TForm1.btn_2Click(Sender: TObject);
var PHandle, FHandle: THandle;
    Process:TProcessEntry32;
    Done, Next: Boolean;
    WinTitle : array[0..256] of Char;
    pn : String;
begin
  FHandle := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
  Process.dwSize := Sizeof(Process);
  Next := Process32First(FHandle, Process);
  while Next do
  begin
    pn := GetProcessNameByPID(GetHWndByPID(process.th32ProcessID));
    if pos('GDollar', pn) > 0 then
      ShowMessage(pn);


    Next := Process32Next(FHandle,Process);
  end;
  CloseHandle(FHandle);
end;

function TForm1.GetHWndByPID(const hPID: THandle): THandle;
  type
    PEnumInfo = ^TEnumInfo;
    TEnumInfo = record
    ProcessID: DWORD;
    HWND: THandle;
  end;

  function EnumWindowsProc(Wnd: DWORD; var EI: TEnumInfo): Bool; stdcall;
  var PID: DWORD;
  begin
    GetWindowThreadProcessID(Wnd, @PID);
    Result := (PID <> EI.ProcessID) or (not IsWindowVisible(WND)) or (not IsWindowEnabled(WND));
    if not Result then EI.HWND := WND;
  end;

  function FindMainWindow(PID: DWORD): DWORD;
  var EI: TEnumInfo;
  begin
    EI.ProcessID := PID;
    EI.HWND := 0;
    EnumWindows(@EnumWindowsProc, Integer(@EI));
    Result := EI.HWND;
  end;

begin
  if hPID <> 0
    then Result:=FindMainWindow(hPID)
    else Result:=0;
end;

function TForm1.GetProcessNameByPID(const PID: DWORD): string;
var
  objSWbemLocator, objWMIService: OLEVariant;
  colItems: OLEVariant;
  colItem: OLEVariant;
  oEnum: IEnumvariant;
  iValue: LongWord;
begin
  Result := ''; // Initialize result to empty string

  objSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  objWMIService   := objSWbemLocator.ConnectServer('localhost','root\cimv2', '','');
  colItems := objWMIService.ExecQuery(Format('SELECT * FROM Win32_Process WHERE ProcessId=%d', [PID]), 'WQL', 0);
  oEnum := IUnknown(colItems._NewEnum) as IEnumVariant;

  if oEnum.Next(1, colItem, iValue) = 0 then
  begin
    Result := colItem.Name;
    colItem := Unassigned;
  end;
end;

Upvotes: 0

Views: 176

Answers (1)

MSSI
MSSI

Reputation: 194

Well, I must admit that my original question was not precise. It turned out that the application I was searching for was listed by Process32First / Process32Next; however, the challenge was to find it by its name.

Here is my final code for those who might face a similar situation.

procedure KillUnwantedApp;
type
  PEnumInfo = ^TEnumInfo;
  TEnumInfo = record
    PID: DWORD;
    WindowTitle: string;
  end;
var FHandle : THandle;
    Process : TProcessEntry32;
    Next : Boolean;

    function EnumWindowsProc(Wnd: HWND; Info: PEnumInfo): BOOL; stdcall;
    var WindowPID: DWORD;
        Title: array[0..255] of Char;
    begin
      Result := True;

      GetWindowThreadProcessId(Wnd, @WindowPID);
      if WindowPID = Info^.PID then
      begin
        if GetWindowText(Wnd, Title, SizeOf(Title)) > 0 then
        begin
          Info^.WindowTitle := Title;
          Result := False;
        end;
      end;
    end;

    function GetWindowTitleFromPID(PID: DWORD): string;
    var Info: TEnumInfo;
    begin
      Info.PID := PID;
      Info.WindowTitle := '';
      EnumWindows(@EnumWindowsProc, LPARAM(@Info));
      Result := Info.WindowTitle;
    end;

    function TerminateProcessByPID(PID: DWORD; ExitCode: DWORD = 0): Boolean;
    var hProcess: THandle;
    begin
      Result := False;
      hProcess := OpenProcess(PROCESS_TERMINATE, False, PID);
      if hProcess <> 0 then
      begin
        try
          Result := TerminateProcess(hProcess, ExitCode);
        finally
          CloseHandle(hProcess);
        end;
      end;
    end;

begin
  FHandle := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
  Process.dwSize := Sizeof(Process);
  Next := Process32First(FHandle, Process);
  while Next do
  begin
    if pos('searched_application_name', GetWindowTitleFromPID(Process.th32ProcessID)) > 0 then
      TerminateProcessByPID(Process.th32ProcessID);

    Next := Process32Next(FHandle,Process);
  end;
  CloseHandle(FHandle);
end;

Upvotes: 0

Related Questions