Forum > Windows

[Solved] Windows apps info - External Unknown Exception code $C000374

(1/1)

kinnon_2000:
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  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---unit Unit1; //{$mode objfpc}{$H+}{$mode delphi}{$H+}interface uses   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, Buttons, LazLogger,  ExtCtrls, Windows, LCLIntf, ComCtrls; type  TFNWndEnumProc = function(hwnd: HWND; lParam: LPARAM): BOOL; stdcall;  function EnumWindows(lpEnumFunc: TFNWndEnumProc; lParam: LPARAM): BOOL;stdcall; external user32;type   { TForm1 }   TForm1 = class(TForm)    BitBtn1: TBitBtn;    Label1: TLabel;    ListView1: TListView;    Panel1: TPanel;    procedure BitBtn1Click(Sender: TObject);    procedure FormDestroy(Sender: TObject);   private  public  end; var  Form1: TForm1; implementation {$R *.lfm} { TForm1 }function GetProcessImagePath(pid: DWORD): string;var  hProcess: THandle;  path: array of Char;  pathSize: DWORD;begin  Result := '';  // Determine required buffer size  pathSize := MAX_PATH;  SetLength(path, pathSize);  FillChar(path[0], Length(path) * SizeOf(Char), 0); // Initialize path  hProcess := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, False, pid);  if hProcess <> 0 then  begin      try        try          // Query full process image name          if QueryFullProcessImageNameA(hProcess, 0, @path[0], @pathSize) then          begin            // Resize buffer if necessary            SetLength(path, pathSize);            // Convert buffer to string and assign to result            Result := string(path);          end          else            ShowMessage('QueryFullProcessImageNameA failed');        except on e: Exception do               ShowMessage('GetProcessImagePath Error: ' + E.Message);        end;      finally             CloseHandle(hProcess);      end;  end;end; // function uses EnumWindows to populate a listview with running windowed apps and useful info.function EnumWindowsProc(WHandle: HWND; lv: TListView): BOOL;StdCall;var Title,ClassName, FileName:array[0..128] of char;    li:TListItem;    PID:dword;    sPath:String;begin Result:=True; sPath:=''; PID:=0; GetWindowText(wHandle, Title,128); GetClassName(wHandle, ClassName, 128); GetModuleFileName(wHandle, FileName, 128); // not working try    if Title<>'' then    begin // avoid returned programs without titles      if IsWindowVisible(wHandle) then begin // if true, this is a windowed application        li:= TListItem.Create(lv.Items); // create a new listitem        li.Caption:=inttostr(wHandle); // lets see the window handle        li.SubItems.Add('');           // the pid will go here        li.SubItems.Add(string(Title)); // add the window title        li.SubItems.Add(string(ClassName)); // the window class name        // get the pid using the wHandle        if GetWindowThreadProcessId(wHandle, @PID) <> 0 then        begin            li.SubItems[0]:=inttostr(PID); // lets see the pid            sPath:=GetProcessImagePath(PID); // this causes access violation on 2nd run or close of app            li.SubItems.Add(sPath); // add the file path to my list item subitems        end        else            li.SubItems.Add('Failed to retrieve PID for HWND: '+ inttostr(wHandle));        //to-do ExtractIcon(string(fileName));        lv.Items.AddItem(li); // add the row        result:=true;     end; // eo is window visible   end; // eo Title not blank except   on e: Exception do ShowMessage('An exception was raised: ' + E.Message); end;end; procedure TForm1.BitBtn1Click(Sender: TObject);begin  if Assigned(ListView1) and Assigned(ListView1.Items) then  begin    ListView1.Items.BeginUpdate;    try      ListView1.Items.Clear; // Clear existing items      // Populate ListView1 with new items      EnumWindows(@EnumWindowsProc, LPARAM(ListView1));    finally      ListView1.Items.EndUpdate;    end;  end;end; procedure TForm1.FormDestroy(Sender: TObject);begin BitBtn1.OnClick := nil;end; end.  
I've been at this now for a couple of days and hoping someone can help.

All the best,
Al

Fibonacci:
Solution 1: After line 61 add:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---UniqueString(result);
Solution 2: Change lines 58-61 to:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---result := StrPas(@path[0]);

d2010:
Please you not have FormCreate.
This procedure is absolute necesarly.

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

kinnon_2000:

--- Quote from: Fibonacci on February 07, 2024, 05:57:38 pm ---Solution 1: After line 61 add:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---UniqueString(result);
Solution 2: Change lines 58-61 to:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---result := StrPas(@path[0]);
--- End quote ---

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

Navigation

[0] Message Index

Go to full version