HWND of window => process file name (.exe) of  that window

Posted on 2007-03-25
Last Modified: 2008-01-09

I have HWND of the window, and I need the process file name (.exe) of
that window.
I know how to get PID from the HWND (GetWindowThreadProcessId), but
what to do next?

There are some limitations:
1) The solution should not use Psapi.dll
2) The solution should not use Process32First and Process32Next
(simply because this solution is already known).

Question by:JiriNovotny
LVL 86

Accepted Solution

jkr earned 500 total points
ID: 18789521
With all that restrictions imposed, there are only dirty tricks left: Calling the native API 'NtQueryInformationProcess()' (which I will leave out simply because that requires DDK headers) or reading the command line information directly from the running process:

// File:             cmdline.c
// Project:       cmdline
// Desc:            this is a sample program that shows how to
//                        get the command line for almost any process
//                        on the system for WinNT 4 and up.
// Revisions:       Created 12/02/99
// Copyright(C) 1999, Tomas Restrepo. All rights reserved
#define UNICODE
#define _UNICODE

#include <windows.h>
#include <stdio.h>
#include <tchar.h>

#pragma comment(lib, "advapi32.lib")

// found by experimentation this is where the some
// process data block is found in an NT machine.
// On an Intel system, 0x00020000 is the 32
// memory page. At offset 0x0498 is the process
// current directory (or startup directory, not sure yet)
// followed by the system's PATH. After that is the  
// process full command command line, followed by
// the exe name and the window
// station it's running on
#define BLOCK_ADDRESS      (LPVOID)0x00020498
// Additional comments:
// From experimentation I've found
// two notable exceptions where this doesn't seem to apply:
// smss.exe : the page is reserved, but not commited
//                  which will get as an invalid memory address
//                  error
// crss.exe : although we can read the memory, it's filled
//                    with 00 comepletely. No trace of command line
//                    information

// simple macro to handle errors
#define SIGNAL_ERROR() { bError = TRUE; __leave; }
// align pointer
#define ALIGN_DWORD(x) ( (x & 0xFFFFFFFC) ? (x & 0xFFFFFFFC) + sizeof(DWORD) : x )

// Function: GetProcessCmdLine()
// Added: 19/02/99
// Description: Gets the command line for the given process
//                        NOTE: hProcess should be opened with
//                        PROCESS_VM_READ , PROCESS_VM_OPERATION
//                        and PROCESS_QUERY_INFORMATION rights
BOOL GetProcessCmdLine ( HANDLE hProcess, LPWSTR lpszCmdLine )
      LPBYTE                                    lpBuffer = NULL;
      LPBYTE                                    lpPos = NULL; // offset from the start of the buffer
      DWORD                                    dwBytesRead;
      SYSTEM_INFO                              sysinfo;
      BOOL                                    bError = FALSE;

      __try {
            // Get the system page size by using GetSystemInfo()
            GetSystemInfo ( &sysinfo );
            // allocate one on the heap to retrieve a full page
            // of memory
            lpBuffer = (LPBYTE)malloc ( sysinfo.dwPageSize );
            if ( lpBuffer == NULL )
                  SIGNAL_ERROR ();

            // first of all, use VirtualQuery to get the start of the memory
            // block
            if ( VirtualQueryEx ( hProcess, BLOCK_ADDRESS, &mbi, sizeof(mbi) ) == 0 )
                  SIGNAL_ERROR ();
            // read memory begining at the start of the page
            // after that, we know that the env strings block
            // will be 0x498 bytes after the start of the page
            if ( !ReadProcessMemory ( hProcess, mbi.BaseAddress, (LPVOID)lpBuffer,
                                                  sysinfo.dwPageSize, &dwBytesRead ) )
                   SIGNAL_ERROR ();

            // now we've got the buffer on our side of the fence.
            // first, lpPos points to a string containing the current directory
            /// plus the path.
            lpPos = lpBuffer + ((DWORD)BLOCK_ADDRESS - (DWORD)mbi.BaseAddress);
            lpPos = lpPos + (wcslen ( (LPWSTR)lpPos ) + 1) * sizeof(WCHAR);
            // now goes full path an filename, aligned on a DWORD boundary
            // skip it
            lpPos = (LPBYTE)ALIGN_DWORD((DWORD)lpPos);
            lpPos = lpPos + (wcslen ( (LPWSTR)lpPos ) + 1) * sizeof(WCHAR);
            // hack: Sometimes, there will be another '\0' at this position
            // if that's so, skip it
            if ( *lpPos == '\0' ) lpPos += sizeof(WCHAR);
            // now we have the actual command line
            // copy it to the buffer
            wcsncpy  ( lpszCmdLine, (LPWSTR)lpPos, MAX_PATH );
            // make sure the path is null-terminted
            lpszCmdLine[MAX_PATH-1] = L'\0';

      __finally  {
            // clean up
            if ( lpBuffer != NULL ) free ( lpBuffer );
            return bError ? FALSE : TRUE;

// Function: EnableTokenPrivilege()
// Added: 20/02/99
// Description: Enables a specific token privilege
BOOL EnableTokenPrivilege ( LPTSTR privilege )
      HANDLE            hToken;                        // process token
      TOKEN_PRIVILEGES tp;                  // token provileges
      DWORD            dwSize;                        

      // initialize privilege structure
      ZeroMemory (&tp, sizeof (tp));
      tp.PrivilegeCount = 1;

      // open the process token
      if ( !OpenProcessToken ( GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken ) )
            return FALSE;
      // look up the privilege LUID and enable it
      if ( !LookupPrivilegeValue ( NULL, privilege, &tp.Privileges[0].Luid ) )
            CloseHandle ( hToken);
            return FALSE;
      tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

      // adjust token privileges
      if ( !AdjustTokenPrivileges ( hToken, FALSE, &tp, 0, NULL, &dwSize ) )
            CloseHandle ( hToken);
            return FALSE;

      // clean up
      CloseHandle ( hToken );
      return TRUE;

// Function: main()
// Added: 19/02/99
// Description: entry point
int wmain ( int argc, wchar_t *argv[] )
      WCHAR      cmdline[MAX_PATH];
      WCHAR      *stop;
      DWORD      pid; // pid to get command line for
      HANDLE      hProcess = NULL;

      if ( argc != 2 )
            puts ( "Usage: cmdline pid" );
            return 0;

      pid = wcstoul ( argv[1], &stop, 10 );

      // before attemting to open the process,
      // enable the SE_DEBUG_NAME privilege
      // in our own token, so we can open any
      // process in the system.
      if ( !EnableTokenPrivilege ( SE_DEBUG_NAME ) )
            printf ( "Failed o get required privileges, error %lu\n", GetLastError () );
            return 0;

      // open process
                                          FALSE, pid );

      if ( hProcess == NULL )
            printf ( "Failed to open process. pid: %lu, error %lu\n", pid, GetLastError () );
            return 0;
      if ( !GetProcessCmdLine ( hProcess, cmdline ) )
            printf ( "Failed to get process command line, error: %lu\n", GetLastError () );
            return 0;
            printf ( "Command line: %S\n", cmdline );

      // close process
      CloseHandle ( hProcess );
      return 0;
LVL 86

Expert Comment

ID: 18789531
Oh, just in case that was not clear - you need to pass the PID, and the 1st parameter of the command line will be the executable image.
LVL 11

Expert Comment

ID: 18790785
But using psapi is much easier: May I know why not psapi?
Best Regards,
Free Tool: Site Down Detector

Helpful to verify reports of your own downtime, or to double check a downed website you are trying to access.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

LVL 39

Expert Comment

ID: 18792811
What about:

  HINSTANCE hAppInstance = GetWindowWord(hWnd, GWW_HINSTANCE);

   nPathLength = GetModuleFileName(hAppInstance,
                    (LPSTR)szPath, PATH_LENGTH);

Regards, Alex

Author Comment

ID: 18988293
jkr: heh, really doen't exists simplier solution?

DeepuAbrahamK: because psapi functions are not supported on windows 9x

itsmeandnobodyelse: Unfortunatelly, doesn't work.
LVL 86

Expert Comment

ID: 18989605
Nope, not that I would know. That is, except PSAPI. If you need a solution that works on Win9x also, use the Toolhelp APIs - see e.g. ("Taking a Snapshot and Viewing Processes"). Ref.: ("CreateToolhelp32Snapshot"):


Client Requires Windows Vista, Windows XP, Windows 2000 Professional, Windows Me, Windows 98, or Windows 95.

Featured Post

Free Tool: SSL Checker

Scans your site and returns information about your SSL implementation and certificate. Helpful for debugging and validating your SSL configuration.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Article by: SunnyDark
This article's goal is to present you with an easy to use XML wrapper for C++ and also present some interesting techniques that you might use with MS C++. The reason I built this class is to ease the pain of using XML files with C++, since there is…
What is C++ STL?: STL stands for Standard Template Library and is a part of standard C++ libraries. It contains many useful data structures (containers) and algorithms, which can spare you a lot of the time. Today we will look at the STL Vector. …
The goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…
The goal of the video will be to teach the user the concept of local variables and scope. An example of a locally defined variable will be given as well as an explanation of what scope is in C++. The local variable and concept of scope will be relat…

830 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