Solved

Getting the file path by using a window handle.

Posted on 2004-04-26
21
3,995 Views
Last Modified: 2010-04-05
Hey guys,

I was just wondering if there was a way to get the full file path by using a window handle.  For example if the user selects the Notepad window then it will show "C:\Documents and Settings\SSynan\Start Menu\Programs\Accessories\Notepad.exe" (or whatever window). I tried messing with TLHelp32 but the best I could get is the file name, not the full path.

I know how to do all the necessary code to locate the window, I just don't know how to get the file path. Btw, this will be used on both WindowsXP and Windows 2000.

Best Regards,
- Steven
0
Comment
Question by:PoeticAudio
  • 8
  • 5
  • 2
  • +2
21 Comments
 
LVL 11

Accepted Solution

by:
shaneholmes earned 125 total points
ID: 10919499
You can use GetModuleFileName to retrieve the name of an EXE from a
module handle. However,  using GetModuleFilename only works on Win32 if the window
is part of your own process.

You need to use Toolhelp32 functions (on Win95
only) or PSAPI.DLL (NT 4) to get from a process ID (which you obtain from the
window handle via GetWindowthreadProcessID) to the filename of the EXE.

Delphi
encapsulates Toolhelp32 in the TlHelp32 unit.


procedure GetExePath(WH : HWND; var Exepath : string);
var
  dwActiveProcessId : DWORD;
  Snap   : THandle;
  pe32   : TPROCESSENTRY32;
Begin
  GetWindowThreadProcessId(WH, @dwActiveProcessId );
  try
    Snap := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,
dwActiveProcessId);
    if Snap <> 0 then
    begin
      if Process32First(Snap, pe32) then
      begin
        if pe32.th32ProcessID = dwActiveProcessId then
        begin
          ExePath := String(pe32.szExeFile);
        end
        else
        begin
          while Process32Next(Snap, pe32) do
          begin
            if pe32.th32ProcessID = dwActiveProcessId then
            begin
              ExePath := String(pe32.szExeFile);
              Break;
            end; //of: if pe32.th32ProcessID = dwActiveProcessId
          end; //of while
        end; //of else
      end; //of: if Process32First(Snap, pe32)
    end; //of: if Snap <> 0
  finally
    CloseHandle(Snap);
  end;
end;


Shane
0
 
LVL 11

Expert Comment

by:shaneholmes
ID: 10919530
Or  as a function:

Shane


function GetExePath(WH : HWND): String;
var
  dwActiveProcessId : DWORD;
  Snap   : THandle;
  pe32   : TPROCESSENTRY32;
Begin
 result:= '';
  GetWindowThreadProcessId(WH, @dwActiveProcessId );
  try
    Snap := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,
dwActiveProcessId);
    if Snap <> 0 then
    begin
      if Process32First(Snap, pe32) then
      begin
        if pe32.th32ProcessID = dwActiveProcessId then
        begin
          result := String(pe32.szExeFile);
        end
        else
        begin
          while Process32Next(Snap, pe32) do
          begin
            if pe32.th32ProcessID = dwActiveProcessId then
            begin
              Result := String(pe32.szExeFile);
              Break;
            end; //of: if pe32.th32ProcessID = dwActiveProcessId
          end; //of while
        end; //of else
      end; //of: if Process32First(Snap, pe32)
    end; //of: if Snap <> 0
  finally
    CloseHandle(Snap);
  end;
end;
0
 
LVL 20

Expert Comment

by:Madshi
ID: 10919726
The toolhelp functions can be used everywhere except NT4. The problem is that while the toolhelp functions do return the full file path in win9x, they only return the file NAME in the winNT family. So if you need the full path in the NT family you need to use PsApi.dll. This works in the whole winNT family, but not in win9x.
0
 
LVL 11

Expert Comment

by:shaneholmes
ID: 10919784
Soory, your right, i was typing so fast, i got my self reversed, however, the code does use the psAPI.dll

SMILE

SHane
0
 
LVL 20

Expert Comment

by:Madshi
ID: 10919871
Which code uses the pasApi.dll? Your code uses toolhelp32. Now I'm confused...   :-)
0
 
LVL 11

Expert Comment

by:shaneholmes
ID: 10919983
Yeah, im confusing my self as well.

It is quite complicated, in fact. as quoted from (Peter Below (TeamB))

"Win16:

  use GetWindowWord( hwnd, GWW_INSTANCE ) to get the module handle, with that
  GetModuleFilename gets the exe name. ExtractIcon can be used to fetch an
  icon from the EXE.
 
Win95/98, NT 5.0:
  use GetWindowThreadProcessID to get the process ID from the window handle.
  Use Toolhelp32 to enumerate all running processes and look for one with a
  matching process ID. The process info contains the exe name. Get icon with
  ExtractIconEx or ShGetFileInfo. Delphi comes with an import unit for
  Toolhelp32 (tlhelp32).

NT 4.0:
  use GetWindowThreadProcessID to get the process ID from the window handle.
  Use PSAPI.DLL to enumerate all running processes and look for one with a
  matching process ID. The process info contains the exe name. Get icon with
  ExtractIconEx or ShGetFileInfo. A import unit for PSAPI.DLL can be
  downloaded from www.xapware.com or http://www.wilsonc.demon.co.uk."


Shane
0
 
LVL 20

Expert Comment

by:Madshi
ID: 10920044
But as I said before, the toolhelp functions do NOT give us the full file path when being used in the NT family. And the question is about getting the full file path in the NT family. So that means toolhelp is no go.

You need to use PsApi.dll. That works in NT4, Windows 2000, Windows XP and Windows 2003 - but not in win9x.
0
 
LVL 6

Author Comment

by:PoeticAudio
ID: 10920074
So how would I use psAPI.dll to achieve this?
0
 
LVL 11

Expert Comment

by:shaneholmes
ID: 10920083
Win95/98, NT 5.0:
  use GetWindowThreadProcessID to get the process ID from the window handle.
  Use Toolhelp32 to enumerate all running processes and look for one with a
  matching process ID. The process info contains the exe name. Get icon with
  ExtractIconEx or ShGetFileInfo. Delphi comes with an import unit for
  Toolhelp32 (tlhelp32).

SHane
0
What Security Threats Are You Missing?

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

 
LVL 11

Expert Comment

by:shaneholmes
ID: 10920101
OK Madshi, im listening...... cause i think im confused., hopefully you can set me straight as well....

code please....

Shane
0
 
LVL 20

Assisted Solution

by:Madshi
Madshi earned 125 total points
ID: 10920350
Shane, in Windows 2000, XP and Windows 2003 you can use *both* the toolhelp functions and PsApi.dll. Normally everyone uses the toolhelp functions, that's why Peter Below suggested that. However, in the NT family the toolhelp functions only return the file name of the running processes, NOT the full path. So in order to get the full path you need to use PsApi.dll.

Sorry to say, but I don't have any PsApi.dll code ready to go.

As Shane quoted:

"A import unit for PSAPI.DLL can be downloaded from www.xapware.com or http://www.wilsonc.demon.co.uk."

Alternatively you could also use my madKernel packages, which is free for non-commercial usage (only). But I think it might be a bit overkill, if all you want to do is process enumeration. Anyway, just for the sake of completeness here's the madKernel documentation:

http://help.madshi.net/madKernel.htm

With it you could do this:

uses madKernel;

begin
  ShowMessage( Window(someWindowHandle).OwnerProcess.ExeFile );

That would return the full path in all 32bit Windows operating systems. But the madKernel package is quite big, so perhaps you're better of by using psapi directly.
0
 
LVL 11

Expert Comment

by:shaneholmes
ID: 10920373
THANKS Madshi!

Shane
0
 
LVL 6

Author Comment

by:PoeticAudio
ID: 10920574
I was hoping to keep the program as small as possible, besides I will be using this program at work, so that solution looks like a no-go...hmm this is harder then it should be...
0
 
LVL 20

Expert Comment

by:Madshi
ID: 10920608
Just do a search on google. I'm sure that I've seen some Delphi psapi.dll sources somewhen somewhere. Look for "Delphi psapi".
0
 
LVL 11

Expert Comment

by:shaneholmes
ID: 10920741
Yup, and when I did, i found this great source of yours....

In win9x the code also gives you the full path, but
not in win2k and winXP. In those systems you need
to use psApi.dll to get the full path:

http://www.howtodothings.com/showarticle.asp?article=142

Shane
0
 
LVL 6

Expert Comment

by:DaFox
ID: 10937268
Hi,

just to (hopefully) complete the above comments:

function GetExenameForProcessUsingPSAPI(pid: DWord): String;
var
  i: Integer;
  cbNeeded: DWord;
  modules: array [1..1024] of hInst;
  ProcHandle: THandle;
  filename: array [0..512] of Char;
begin
  SetLastError(0);
  result := '';
  ProcHandle := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, false, pid);
  if (ProcHandle <> 0) then
  try
    if (EnumProcessModules(ProcHandle, @modules[1], SizeOf(modules), cbNeeded)) then
      for i := 1 to cbNeeded div SizeOf(hInst) do
      begin
        if (GetModuleFilenameEx(ProcHandle, modules[i], filename, SizeOf(filename)) > 0) then
          if (AnsiStrIComp(PChar(ExtractFileExt(filename) + #0), '.exe' ) = 0) then
          begin
            Result := filename;
            break;
          end;
      end;
  finally
    CloseHandle(ProcHandle);
  end;
end;

function GetExenameForProcessUsingToolhelp32(pid: DWord): String;
var
  snapshot: THandle;
  procentry: TProcessEntry32;
  ret: bool;
begin
  SetLastError(0);
  Result := '';
  snapshot:= CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  if (snapshot <> INVALID_HANDLE_VALUE) then
  try
    procentry.dwSize := Sizeof(procentry);
    ret := Process32FIRST(snapshot, procentry);
    while ret do
    begin
      if (procentry.th32ProcessID = pid) then
      begin
        Result := procentry.szExeFile;
        break;
      end else
        ret := Process32Next(snapshot, procentry);
    end;
  finally
    CloseHandle(snapshot);
  end;
End;

function GetExenameForProcess(pid: DWord): String;
begin
  if (Win32Platform = VER_PLATFORM_WIN32_NT) and (Win32MajorVersion <= 5)
  then
    Result := GetExenameForProcessUsingPSAPI(pid)
  else
    Result := GetExenameForProcessUsingToolhelp32(pid);
end;

function GetExenameForWindow(wnd: hWnd): String;
var
  pid: DWord;
begin
  Result := '';
  if isWindow(Wnd) then
  begin
    GetWindowThreadProcessID(Wnd, @pid);
    if (pid <> 0) then Result := GetExenameForProcess(pid);
  end;
end;

Regards,
Markus
0
 
LVL 12

Expert Comment

by:Ivanov_G
ID: 11077432
The GetModuleFileName function retrieves the full path and filename for the executable file containing the specified module.

DWORD GetModuleFileName(
    HMODULE hModule,      // handle to module to find filename for
    LPTSTR lpFilename,      // pointer to buffer for module path
    DWORD nSize       // size of buffer, in characters
   );      
 

Parameters:
   hModule - Identifies the module whose executable filename is being requested. If this parameter is NULL, GetModuleFileName returns the path for the file used to create the calling process.
   lpFilename - Points to a buffer that is filled in with the path and filename of the given module.
   nSize - Specifies the length, in characters, of the lpFilename buffer. If the length of the path and filename exceeds this limit, the string is truncated.

Return Values
If the function succeeds, the return value is the length, in characters, of the string copied to the buffer.
If the function fails, the return value is zero. To get extended error information, call GetLastError.
0
 
LVL 12

Expert Comment

by:Ivanov_G
ID: 11136714
procedure TForm1.Button1Click(Sender: TObject);
var
  Buff : array[0..255] of char;
  size : Integer;
  str  : String;
  i, j : Integer;
begin
  size := sizeof(Buff);
  j := GetModuleFileName(hInstance, Buff, size);
  str := '';
  for i := 0 to j do
    str := str + Buff[i];
  ShowMessage(str);
end;
0

Featured Post

What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

Join & Write a Comment

Suggested Solutions

Have you ever had your Delphi form/application just hanging while waiting for data to load? This is the article to read if you want to learn some things about adding threads for data loading in the background. First, I'll setup a general applica…
In my programming career I have only very rarely run into situations where operator overloading would be of any use in my work.  Normally those situations involved math with either overly large numbers (hundreds of thousands of digits or accuracy re…
In this seventh video of the Xpdf series, we discuss and demonstrate the PDFfonts utility, which lists all the fonts used in a PDF file. It does this via a command line interface, making it suitable for use in programs, scripts, batch files — any pl…
Access reports are powerful and flexible. Learn how to create a query and then a grouped report using the wizard. Modify the report design after the wizard is done to make it look better. There will be another video to explain how to put the final p…

758 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

19 Experts available now in Live!

Get 1:1 Help Now