Recent

Author Topic: [Solved] Windows apps info - External Unknown Exception code $C000374  (Read 1214 times)

kinnon_2000

  • New Member
  • *
  • Posts: 22
I've built the attached demo which populates a listview with some useful information on open windows.
Its a demo a bit like task manager process view, but just for running application windows.

I'm using EnumWindows to get the window handle (wHandle) of each windowed running application.

I use GetWindowThreadProcessId to get the process ID (PID) based on the window handle.

Then I can use the PID with QueryFullProcessImageNameA to find the complete filename and path to each.

The list is populated on BitBtn1Click. When first clicked everything works perfectly.
Second click - the program crashes with an access violation.
Trying to close the app with the top right close button also causes an access violation, after the button has been clicked once.

Note that if I comment out line 100 (sPath:=GetProcessImagePath(PID)) the access violation stops, although i can then no longer get the application path.

Here's my unit code, I've also attached my project files should anyone care to have a play with it...

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. //{$mode objfpc}{$H+}
  4. {$mode delphi}{$H+}
  5. interface
  6.  
  7. uses
  8.  
  9.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, Buttons, LazLogger,
  10.   ExtCtrls, Windows, LCLIntf, ComCtrls;
  11.  
  12. type
  13.   TFNWndEnumProc = function(hwnd: HWND; lParam: LPARAM): BOOL; stdcall;
  14.   function EnumWindows(lpEnumFunc: TFNWndEnumProc; lParam: LPARAM): BOOL;stdcall; external user32;
  15. type
  16.  
  17.   { TForm1 }
  18.  
  19.   TForm1 = class(TForm)
  20.     BitBtn1: TBitBtn;
  21.     Label1: TLabel;
  22.     ListView1: TListView;
  23.     Panel1: TPanel;
  24.     procedure BitBtn1Click(Sender: TObject);
  25.     procedure FormDestroy(Sender: TObject);
  26.  
  27.   private
  28.   public
  29.   end;
  30.  
  31. var
  32.   Form1: TForm1;
  33.  
  34. implementation
  35.  
  36. {$R *.lfm}
  37.  
  38. { TForm1 }
  39. function GetProcessImagePath(pid: DWORD): string;
  40. var
  41.   hProcess: THandle;
  42.   path: array of Char;
  43.   pathSize: DWORD;
  44. begin
  45.   Result := '';
  46.   // Determine required buffer size
  47.   pathSize := MAX_PATH;
  48.   SetLength(path, pathSize);
  49.   FillChar(path[0], Length(path) * SizeOf(Char), 0); // Initialize path
  50.   hProcess := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, False, pid);
  51.   if hProcess <> 0 then
  52.   begin
  53.       try
  54.         try
  55.           // Query full process image name
  56.           if QueryFullProcessImageNameA(hProcess, 0, @path[0], @pathSize) then
  57.           begin
  58.             // Resize buffer if necessary
  59.             SetLength(path, pathSize);
  60.             // Convert buffer to string and assign to result
  61.             Result := string(path);
  62.           end
  63.           else
  64.             ShowMessage('QueryFullProcessImageNameA failed');
  65.         except on e: Exception do
  66.                ShowMessage('GetProcessImagePath Error: ' + E.Message);
  67.         end;
  68.       finally
  69.              CloseHandle(hProcess);
  70.       end;
  71.   end;
  72. end;
  73.  
  74. // function uses EnumWindows to populate a listview with running windowed apps and useful info.
  75. function EnumWindowsProc(WHandle: HWND; lv: TListView): BOOL;StdCall;
  76. var Title,ClassName, FileName:array[0..128] of char;
  77.     li:TListItem;
  78.     PID:dword;
  79.     sPath:String;
  80. begin
  81.  Result:=True;
  82.  sPath:='';
  83.  PID:=0;
  84.  GetWindowText(wHandle, Title,128);
  85.  GetClassName(wHandle, ClassName, 128);
  86.  GetModuleFileName(wHandle, FileName, 128); // not working
  87.  try
  88.     if Title<>'' then
  89.     begin // avoid returned programs without titles
  90.       if IsWindowVisible(wHandle) then begin // if true, this is a windowed application
  91.         li:= TListItem.Create(lv.Items); // create a new listitem
  92.         li.Caption:=inttostr(wHandle); // lets see the window handle
  93.         li.SubItems.Add('');           // the pid will go here
  94.         li.SubItems.Add(string(Title)); // add the window title
  95.         li.SubItems.Add(string(ClassName)); // the window class name
  96.         // get the pid using the wHandle
  97.         if GetWindowThreadProcessId(wHandle, @PID) <> 0 then
  98.         begin
  99.             li.SubItems[0]:=inttostr(PID); // lets see the pid
  100.             sPath:=GetProcessImagePath(PID); // this causes access violation on 2nd run or close of app
  101.             li.SubItems.Add(sPath); // add the file path to my list item subitems
  102.         end
  103.         else
  104.             li.SubItems.Add('Failed to retrieve PID for HWND: '+ inttostr(wHandle));
  105.         //to-do ExtractIcon(string(fileName));
  106.         lv.Items.AddItem(li); // add the row
  107.         result:=true;
  108.      end; // eo is window visible
  109.    end; // eo Title not blank
  110.  except
  111.    on e: Exception do ShowMessage('An exception was raised: ' + E.Message);
  112.  end;
  113. end;
  114.  
  115. procedure TForm1.BitBtn1Click(Sender: TObject);
  116. begin
  117.   if Assigned(ListView1) and Assigned(ListView1.Items) then
  118.   begin
  119.     ListView1.Items.BeginUpdate;
  120.     try
  121.       ListView1.Items.Clear; // Clear existing items
  122.       // Populate ListView1 with new items
  123.       EnumWindows(@EnumWindowsProc, LPARAM(ListView1));
  124.     finally
  125.       ListView1.Items.EndUpdate;
  126.     end;
  127.   end;
  128. end;
  129.  
  130. procedure TForm1.FormDestroy(Sender: TObject);
  131. begin
  132.  BitBtn1.OnClick := nil;
  133. end;
  134.  
  135. end.
  136.  
  137.  

I've been at this now for a couple of days and hoping someone can help.

All the best,
Al

« Last Edit: February 07, 2024, 07:14:48 pm by kinnon_2000 »

Fibonacci

  • Hero Member
  • *****
  • Posts: 612
  • Internal Error Hunter
Re: Windows apps info - External Unknown Exception code $C000374
« Reply #1 on: February 07, 2024, 05:57:38 pm »
Solution 1: After line 61 add:
Code: Pascal  [Select][+][-]
  1. UniqueString(result);

Solution 2: Change lines 58-61 to:
Code: Pascal  [Select][+][-]
  1. result := StrPas(@path[0]);

d2010

  • Jr. Member
  • **
  • Posts: 65
Re: Windows apps info - External Unknown Exception code $C000374
« Reply #2 on: February 07, 2024, 05:59:46 pm »
Please you not have FormCreate.
This procedure is absolute necesarly.

procedure TForm1.FormCreate(Sender: TObject);
Begin
      EnumWindowsProc(Form1.Handle,ListView1);
end;
       

kinnon_2000

  • New Member
  • *
  • Posts: 22
Re: Windows apps info - External Unknown Exception code $C000374
« Reply #3 on: February 07, 2024, 06:57:58 pm »
Solution 1: After line 61 add:
Code: Pascal  [Select][+][-]
  1. UniqueString(result);

Solution 2: Change lines 58-61 to:
Code: Pascal  [Select][+][-]
  1. result := StrPas(@path[0]);

Ah many thanks, that fixed it!
So I gather the issue is that I was trying to use a variable shared with an external process instead of using a unique copy. Well spotted!

d2010 thanks for taking the time to contribute. FormCreate isn't necessary. If i wanted to populate the listview when the program starts I'd be more inclined to use onActivate, to ensure the listview has been instantiated. I've found referencing visual controls is a bit dodgy from the onCreate method. I tried your suggestion in case I was missing something but it did nothing. Is there a reason to call EnumWindowsProc from onCreate which I've missed?

Thanks again,
Al


 

TinyPortal © 2005-2018