Solved

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

Posted on 2007-03-25
6
558 Views
Last Modified: 2008-01-09
Hello,

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).

Thanks.
0
Comment
Question by:JiriNovotny
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
6 Comments
 
LVL 86

Accepted Solution

by:
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;
      MEMORY_BASIC_INFORMATION      mbi;
      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
      hProcess = OpenProcess ( PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_QUERY_INFORMATION,
                                          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;
      }
      else
            printf ( "Command line: %S\n", cmdline );

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

Expert Comment

by:jkr
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.
0
 
LVL 11

Expert Comment

by:DeepuAbrahamK
ID: 18790785
But using psapi is much easier: May I know why not psapi?
http://www.codeproject.com/cpp/ModuleNameFromWindwHandle.asp
Best Regards,
DeepuAbrahamK
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 18792811
What about:

  HINSTANCE hAppInstance = GetWindowWord(hWnd, GWW_HINSTANCE);

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

Regards, Alex
0
 

Author Comment

by:JiriNovotny
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.
0
 
LVL 86

Expert Comment

by:jkr
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. http://msdn2.microsoft.com/en-us/library/ms686701.aspx ("Taking a Snapshot and Viewing Processes"). Ref.: http://msdn2.microsoft.com/en-us/library/ms682489.aspx ("CreateToolhelp32Snapshot"):

Requirements

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

Featured Post

Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering 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

Title # Comments Views Activity
Create string splitting user defined function in C++ 23 356
C++ get user from AD  (VS6) 7 111
White board coding practice 3 98
c++, dynamic object by json 1 65
When writing generic code, using template meta-programming techniques, it is sometimes useful to know if a type is convertible to another type. A good example of when this might be is if you are writing diagnostic instrumentation for code to generat…
This article shows you how to optimize memory allocations in C++ using placement new. Applicable especially to usecases dealing with creation of large number of objects. A brief on problem: Lets take example problem for simplicity: - I have a G…
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.
The viewer will learn how to clear a vector as well as how to detect empty vectors in C++.

734 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