Link to home
Start Free TrialLog in
Avatar of André Murta
André MurtaFlag for Brazil

asked on

Process name for the active window in Windows 8/10

Hello,

I need to capture the name of the application that is in foreground during a certain moment at the user's Windows session. I'm trying the following approach in order to get that:

function ProcessName(AID: Cardinal): String;
var
  Snapshot: THandle;
  ProcessEntry: TProcessEntry32;
begin
  Result := '';
  Snapshot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  try
    ProcessEntry.dwSize := SizeOf(TProcessEntry32);

    if not Process32First(Snapshot, ProcessEntry) then
      Exit;

    repeat
      if (ProcessEntry.th32ProcessID = AID) then
      begin
        Result := ProcessEntry.szExeFile;
        break;
      end;
    until not Process32Next(Snapshot, ProcessEntry);
  finally
    CloseHandle(Snapshot);
  end;
end;

Open in new window


What happens is when the foreground application is a Windows 10 app, this function always return 'ApplicationFrameHost.exe' as the captured process name. Doing some Google search I found the following web page that has a C code that solves that problem:

Process name for the active window in Windows 8/10

I'm trying to rewrite the presented code in Delphi (Object Pascal), but without success. The LPARAM argument at the 'EnumChildWindowsCallback' function is always being filled with invalid data. Its a shame, but I admmit that I do not have enough knowledge about Object Pascal Pointers. What I'm trying to do is:

type
  TWindowInfo = record
    ownerpid: DWORD;
    childpid: DWORD;
  end;
  PWindowInfo = ^TWindowInfo;

...

function EnumChildWindowsCallback(hwd: HWND; lp: LPARAM): Boolean; stdcall;
var
  info: PWindowInfo;
  pid: DWORD;
begin
  info := PWindowInfo(lp);
  pid := 0;
  EnumChildWindowsCallback(hwd, pid);
  if (pid <> info^.ownerpid) then
    info^.childpid := pid;
  Result := true;
end;

...

procedure TfrmMain.Timer1Timer(Sender: TObject);
var
  active_window: HWND;
  active_process: THANDLE;
  info: TWindowInfo;
  image_name: array[0..MAX_PATH] of Char;
  bufsize: DWORD;
begin
  active_window := GetForegroundWindow();
  info.ownerpid := 0;
  info.childpid := 0;
  if GetWindowThreadProcessId(active_window, @info.ownerpid) <> 0 then
  begin
    info.childpid := info.ownerpid;
    EnumChildWindows(active_window, @EnumChildWindowsCallback, LPARAM(Addr(info)));
    active_process := OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, info.childpid);
    bufsize := MAX_PATH;
    QueryFullProcessImageName(active_process, 0, image_name, @bufsize);
    AddLog('Timer1Timer()', 'host pID: '+IntToStr(active_process)+', EXE: '+String(image_name));
    CloseHandle(active_process);
  end;
end;

Open in new window


But I'm facing an exception due to the test inside the 'EnumChildWindowsCallback' function never being succeeded, the callback is being called until a "Stack Overflow" exception occurs:

  if (pid <> info^.ownerpid) then
    info^.childpid := pid;

Open in new window


Is there a way to obtain the correct Windows 10 processes names using Delphi? I'm not concerned in fix my "code porting" attempt. If there is already an example code (written in Object Pascal) that returns the correct process name it would be just great, but currently I'm unable to find such example in the web.
Avatar of ZeropointNRG
ZeropointNRG
Flag of Australia image

Can't you use a window spy for that?
Avatar of André Murta

ASKER

Not really. It must be done by my app, since this single app is part of a bigger system that analyzes all the motions of each company user, during it's Windows session.
Yeah I realised this later on, sorry lol ;) Good luck!
This question needs an answer!
Become an EE member today
7 DAY FREE TRIAL
Members can start a 7-Day Free trial then enjoy unlimited access to the platform.
View membership options
or
Learn why we charge membership fees
We get it - no one likes a content blocker. Take one extra minute and find out why we block content.