GetModuleFileNameEx, Platform XP, 2000, retrieve Path of calling application

hush021299
hush021299 used Ask the Experts™
on
I still dont find a way to retrieve a path of the calling application (Full file path from a windows handle) with getmodulefilename (or something else).

This function should retrieve the filepath of a window when I pass a handle of this window in the edit1.text

...
{First I did a enumwindows and listed all handles.
then I write a handle in the edit box:}
...
var FN: array[0..256] of Char;
begin
GetWindowModuleFileName( strtoint(edit1.text),
    FN,
    256);
    Showmessage(FN);
...

Result: Allways the path of MY application (same like application.exename)
MS says:
Following exerpt from [http://support.microsoft.com/default.aspx?scid=KB;en-us;q228469]

GetWindowModuleFileName and GetModuleFileName correctly retrieve information about windows and modules in the calling process. In Windows 95 and 98, they return information about windows and modules in other processes. However, in Windows NT 4.0 and Windows 2000, since module handles are no longer shared by all processes as they were on Windows 95 and 98, these APIs do not return information about windows and modules in other processes.

To get more information on Windows 2000, use the Process Status Helper set of APIs (known as PSAPI, see Psapi.h include file), available since Windows NT 4.0. APIs such as GetModuleFileNameEx and GetModuleBaseName offer equivalent functionality.

Hence: I need to work with getmodulefileex, but I can not translate the c example:
#include <windows.h>
#include <stdio.h>
#include "psapi.h"

void PrintModules( DWORD processID )
{
    HMODULE hMods[1024];
    HANDLE hProcess;
    DWORD cbNeeded;
    unsigned int i;

    // Print the process identifier.

    printf( "\nProcess ID: %u\n", processID );

    // Get a list of all the modules in this process.

    hProcess = OpenProcess(  PROCESS_QUERY_INFORMATION |
                                    PROCESS_VM_READ,
                                    FALSE, processID );
    if (NULL == hProcess)
        return;

    if( EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded))
    {
        for ( i = 0; i < (cbNeeded / sizeof(HMODULE)); i++ )
        {
            char szModName[MAX_PATH];

            // Get the full path to the module's file.

            if ( GetModuleFileNameEx( hProcess, hMods[i], szModName,
                                      sizeof(szModName)))
            {
                // Print the module name and handle value.

                printf("\t%s (0x%08X)\n", szModName, hMods[i] );
            }
        }
    }

    CloseHandle( hProcess );
}

void main( )
{
    // Get the list of process identifiers.

    DWORD aProcesses[1024], cbNeeded, cProcesses;
    unsigned int i;

    if ( !EnumProcesses( aProcesses, sizeof(aProcesses), &cbNeeded ) )
        return;

    // Calculate how many process identifiers were returned.

    cProcesses = cbNeeded / sizeof(DWORD);

    // Print the name of the modules for each process.

    for ( i = 0; i < cProcesses; i++ )
        PrintModules( aProcesses[i] );
}
The main function obtains a list of processes by using the EnumProcesses function. For each process, the main function calls the PrintModules function, passing it the process identifier. PrintModules in turn calls the OpenProcess function to obtain the process handle. If OpenProcess fails, the output shows only the process identifier. For example, OpenProcess fails for the Idle and CSRSS processes because their access restrictions prevent user-level code from opening them. Next, PrintModules calls the EnumProcessModules function to obtain the module handles function. Finally, PrintModules calls the GetModuleFileNameEx function, once for each module, to obtain the module names

????

Cheers
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®

Commented:
You need to call GetWindowThreadProcessID to get the process ID to which the window belongs. Then you can use my free unit "enumStuff", which can enumerate all running processes. You can do something like this:

uses enumStuff;

function GetProcessName(processID: dword) : string;
var pl : TProcessList;
begin
  pl := GetProcessList;
  for i1 := 0 to high(pl) do
    if pl[i1].pid = processID then begin
      result := pl[i1].Name;
      exit;
    end;
  result := '';
end;

The unit enumStuff is available here:

http://madshi.net/enumStuff.zip

Regards, Madshi.

Author

Commented:
I got something working as well..

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TForm1 = class(TForm)
    ProcessesListBox: TListBox;
    RefreshBtn: TButton;
    procedure RefreshBtnClick(Sender: TObject);
    procedure FormShow(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

uses
  TLHelp32, PsApi;

function RunningProcessesList(List: TStrings): Boolean;

  function BuildListTH: Boolean;
  var
    SnapProcHandle: THandle;
    ProcEntry: TProcessEntry32;
    NextProc: Boolean;
  begin
    SnapProcHandle := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if SnapProcHandle <> THandle(-1) then
    begin
      Result := True;
      ProcEntry.dwSize := Sizeof(ProcEntry);
      NextProc := Process32First(SnapProcHandle, ProcEntry);
      while NextProc do
      begin
        List.AddObject(ProcEntry.szExeFile, Pointer(ProcEntry.th32ProcessID));
        NextProc := Process32Next(SnapProcHandle, ProcEntry);
      end;
      CloseHandle(SnapProcHandle);
    end else
      Result := False;
  end;

  function BuildListPS: Boolean;
  var
    PIDs: array[0..1024] of DWORD;
    Handle: THandle;
    Needed: DWORD;
    I: Integer;
    ModuleFileName: array[0..MAX_PATH] of Char;
  begin
    Result := EnumProcesses(@PIDs, Sizeof(PIDs), Needed);
    if not Result then Exit;
    for I := 0 to (Needed div Sizeof(DWORD)) - 1 do
      if PIDs[I] <> 0 then
      begin
        Handle := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, False, PIDs[I]);
        if Handle <> 0 then
        begin
          if GetModuleFileNameEx(Handle, 0, ModuleFileName, Sizeof(ModuleFileName)) = 0 then
            List.AddObject('[System]', Pointer(INVALID_HANDLE_VALUE))
          else
            begin
            List.AddObject(ModuleFileName, Pointer(PIDs[I]));
            List.add(string(modulefilename));
            end;
          CloseHandle(Handle);
        end;
      end;
  end;

begin
  Assert(Assigned(List));
  List.BeginUpdate;
  try
    List.Clear;
    //if
  //  (Win32Platform = VER_PLATFORM_WIN32_NT) and (Win32MajorVersion = 4) //then
      Result := BuildListPS;
  //  else
    //  Result := BuildListTH;
  finally
    List.EndUpdate;
  end;
end;

{ TForm1 }

procedure TForm1.RefreshBtnClick(Sender: TObject);
begin
  RunningProcessesList(ProcessesListBox.Items);
end;

procedure TForm1.FormShow(Sender: TObject);
begin
  RefreshBtnClick(nil);
end;

end.

..I'll check out yours now

Author

Commented:
Madshi, what do I need to enter into the processID?
I have a problem to demarcate between process handle and window handle??
Microsoft Azure 2017

Azure has a changed a lot since it was originally introduce by adding new services and features. Do you know everything you need to about Azure? This course will teach you about the Azure App Service, monitoring and application insights, DevOps, and Team Services.

Commented:
A process handle is something totally different than a window handle. And a process ID is something different than a process handle.

Each process has one unique ID. But there can be multiple handles to a process. Each window has only one handle, but no ID. You could say the window handle is for the window what the process ID is for the process.

Anyway, use this to convert a windows handle to a process ID:

function WindowHandleToProcessID(windowHandle: dword) : dword;
begin
  GetWindowThreadProcessID(windowHandle, @result);
end;

Give the result of this function in as the "processID".

Regards, Madshi.

P.S: The code you listed should also work fine.

Author

Commented:
Sorry, one more..
I have listed all processes:
procedure TForm1.Button5Click(Sender: TObject);
var pl : TProcessList;  I:integer;
begin
 pl := GetProcessList;
 for i := 0 to high(pl) do
     listbox1.items.add(pl[i].Name);

end;
This is similar to the task-list. Now I need to apply getmodulefilenameex to receive a path??

Commented:
You mean you need the full file path? enumStuff only gives you the file name in NT/2k/XP. If you need the full path there, too, you have to use psApi. The code you posted above yourself does use psApi, so you might want to use just that...

Author

Commented:
..and one more

with this function I list the path of all processes:
1:

But I want to get a process based on a window handle
2:

can I use GetWindowThreadProcessId to get the module for getmodulefilename from a handle of a window?

======================================================
uses
  TLHelp32, PsApi ;


function BuildListPS: Boolean;
  var
    PIDs: array[0..1024] of DWORD;
    Handle: THandle;
    Needed: DWORD;
    I: Integer;
    ModuleFileName: array[0..MAX_PATH] of Char;
  begin
    Result := EnumProcesses(@PIDs, Sizeof(PIDs), Needed);
    if not Result then Exit;
    for I := 0 to (Needed div Sizeof(DWORD)) - 1 do
      if PIDs[I] <> 0 then
      begin
        Handle := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, False, PIDs[I]);
        if Handle <> 0 then
        begin
          if GetModuleFileNameEx(Handle, 0, ModuleFileName, Sizeof(ModuleFileName)) = 0 then
            Form1.Listbox1.items.AddObject('[System]', Pointer(INVALID_HANDLE_VALUE))
          // Form1.Listbox1.items.add(string(modulefileName));
          else
            begin
            Form1.Listbox1.items.AddObject(ModuleFileName, Pointer(PIDs[I]));
          //  List.add(string(modulefilename));
            end;
          CloseHandle(Handle);
        end;
      end;
  end;
procedure TForm1.Button1Click(Sender: TObject);
begin
   BuildListPS;
end;
//this get the path of all listed processes
==========================================================
2:
var pointer:=WinProcessID
begin
Handle :=  GetWindowThreadProcessId(wparam, WinProcessID);
//wparam comes from a keyhook dll
...
but also this seems to be false
Commented:
Try this code, it will only give you the one process path, which belongs to the window handle you specified:

var pid : dword;
begin
  pid := WindowHandleToProcessID(windowHandle);

  Handle := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, False, pid);
       if Handle <> 0 then
       begin
         if GetModuleFileNameEx(Handle, 0, ModuleFileName, Sizeof(ModuleFileName)) = 0 then
           Form1.Listbox1.items.AddObject('[System]', Pointer(INVALID_HANDLE_VALUE))
         // Form1.Listbox1.items.add(string(modulefileName));
         else
           begin
           Form1.Listbox1.items.AddObject(ModuleFileName, Pointer(PIDs[I]));
         //  List.add(string(modulefilename));
           end;
         CloseHandle(Handle);
       end;

Author

Commented:
Thanks a lot
it works now! Great

Rgds
Hush

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial