How do I GetModuleFileName() if I only have a window handle (hWnd)?
Asked Answered
D

5

11

I'm trying to get the name of the executable of a window that is outside my C# 2.0 application. My app currently gets a window handle (hWnd) using the GetForegroundWindow() call from "user32.dll".

From the digging that I've been able to do, I think I want to use the GetModuleFileNameEx() function (from PSAPI) to obtain the name, but GetModuleFileNameEx() requires a handle to a Process, not a Window.

Is it possible to get a process handle from a window handle? (Do I need to get the thread handle of the window first?)

EDITED the first sentence to make it clearer what I'm trying to do.

UPDATE! Here's the C# code that I found worked for me. The only caveat is occasionally it returns a file/path where the drive letter is a "?" instead of the actual drive letter (like "C"). -- Haven't figured out why yet.

[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

[DllImport("kernel32.dll")]
static extern IntPtr OpenProcess(UInt32 dwDesiredAccess, Int32 bInheritHandle, UInt32 dwProcessId);

[DllImport("psapi.dll")]
static extern uint GetModuleFileNameEx(IntPtr hProcess, IntPtr hModule, [Out] StringBuilder lpBaseName, [In] [MarshalAs(UnmanagedType.U4)] int nSize);

[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CloseHandle(IntPtr hObject);

private string GetWindowModuleFileName(IntPtr hWnd)
{
    uint processId = 0;
    const int nChars = 1024;
    StringBuilder filename = new StringBuilder(nChars);
    GetWindowThreadProcessId(hWnd, out processId);
    IntPtr hProcess = OpenProcess(1040, 0, processId);
    GetModuleFileNameEx(hProcess,IntPtr.Zero,filename,nChars);
    CloseHandle(hProcess);
    return (filename.ToString());
}
Daedalus answered 10/11, 2008 at 4:59 Comment(1)
Works okay for me. Needs if ((int)hProcess != 0) after OpenProcess()Barmy
E
6

You can call GetWindowThreadProcessId and that will return you the process associated with the window.

From that, you can call OpenProcess to open the process and get the handle to the process.

Edytheee answered 10/11, 2008 at 5:9 Comment(1)
Hey welcome to stack overflow - I've been a long time reader of yoursTilghman
W
8

Been struggling with the same problem for an hour now, also got the first letter replaced by a ? by using GetModuleFileNameEx. Finaly came up with this solution using the System.Diagnostics.Process class.

[DllImport("user32.dll")]
public static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);

void GetProcessPathFromWindowHandle(IntPtr hwnd)
{
   uint pid = 0;
   Win32.GetWindowThreadProcessId(hwnd, out pid);
   Process p = Process.GetProcessById((int)pid);
   return p.MainModule.FileName;
}
Wellestablished answered 26/11, 2008 at 16:36 Comment(3)
I think this is, by far, the best solution offered here. No strange characters and, most importantly, not relying on the inconsistent GetModuleFileNameEx function, which on Windows 7 is located in another DLL (not psapi but kernel32). Using a .Net class is definitely preferred in this case. Works perfectly.Portamento
I realize this thread is outdated, but a small remark on this suggested solution. This won't work if you try to obtain the filename of a 64bits process from a 32bits process (or vice versa).Oleate
Not applicable for Microsoft Store apps on Windows 10, calls on different app windows constantly return "C:\\WINDOWS\\system32\\ApplicationFrameHost.exe".Loxodromics
E
6

You can call GetWindowThreadProcessId and that will return you the process associated with the window.

From that, you can call OpenProcess to open the process and get the handle to the process.

Edytheee answered 10/11, 2008 at 5:9 Comment(1)
Hey welcome to stack overflow - I've been a long time reader of yoursTilghman
N
2

If you are running on a windows 64 bit platform, you may need to use QueryFullProcessImageName instead. This returns a user style path, compared to GetProcessImageFileName which returns a system style path that would need to be converted using NtQuerySymbolicLinkObject or ZwQuerySymbolicLinkObject.

One mammoth example function - recommend breaking up into re usable bits.

typedef DWORD (__stdcall *PfnQueryFullProcessImageName)(HANDLE hProcess, DWORD dwFlags, LPTSTR lpImageFileName, PDWORD nSize);
typedef DWORD (__stdcall *PfnGetModuleFileNameEx)(HANDLE hProcess, HMODULE hModule, LPTSTR lpImageFileName, DWORD nSize);

std::wstring GetExeName( HWND hWnd ){
// Convert from Window to Process ID
DWORD dwProcessID = 0;
::GetWindowThreadProcessId(hWnd, &dwProcessID);

// Get a handle to the process from the Process ID
HANDLE hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwProcessID);

// Get the process name
if (NULL != hProcess) {
    TCHAR szEXEName[MAX_PATH*2] = {L'\0'};
    DWORD nExeName = sizeof(szEXEName)/sizeof(TCHAR);

    //  the QueryFullProcessImageNameW does not exist on W2K
    HINSTANCE hKernal32dll = LoadLibrary(L"kernel32.dll");
    PfnQueryFullProcessImageName pfnQueryFullProcessImageName = NULL;
    if(hKernal32dll != NULL) {
        pfnQueryFullProcessImageName = (PfnQueryFullProcessImageName)GetProcAddress(hKernal32dll, "QueryFullProcessImageNameW");
        if (pfnQueryFullProcessImageName != NULL) 
            pfnQueryFullProcessImageName(hProcess, 0, szEXEName, &nExeName);
        ::FreeLibrary(hKernal32dll);
    } 

    // The following was not working from 32 querying of 64 bit processes
    // Use as backup for when function above is not available 
    if( pfnQueryFullProcessImageName == NULL ){ 
        HINSTANCE hPsapidll = LoadLibrary(L"Psapi.dll");
        PfnGetModuleFileNameEx pfnGetModuleFileNameEx = (PfnGetModuleFileNameEx)GetProcAddress(hPsapidll, "GetModuleFileNameExW");
        if( pfnGetModuleFileNameEx != NULL )    
            pfnGetModuleFileNameEx(hProcess, NULL, szEXEName, sizeof(szEXEName)/sizeof(TCHAR));
        ::FreeLibrary(hPsapidll);
    }

    ::CloseHandle(hProcess);

    return( szEXEName );
} 
return std::wstring();
}
Needful answered 10/11, 2008 at 4:59 Comment(0)
C
1

What exactly is it that you're trying to do? You can get the process ID of the the process which created a window with GetWindowThreadProcessId(), followed by OpenProcess() to get the process handle. But this seems very kludgy, and I feel like there's a more elegant way to do what you want to do.

Courtmartial answered 10/11, 2008 at 5:10 Comment(5)
Maybe it seems kludgy to you, but someone with reputation of 1 gave the exact same answer just 1 minute before you, and I don't think his reputation is going to stay at 1 for very long.Lengthy
Yes, it feels kludgy to me, too. I'm trying to track which window has focus. Naturally, window handles and title bar names change, but the application exe name doesn't.Daedalus
OK, but anyway Larry Osterman gave the same answer so it's probably less klugy than all of the alternatives. And his reputation isn't 1 any more.Lengthy
Larry Osterman's lifetime reputation in windows programming is right up there with Raymond Chen'sTilghman
Larry Osterman's answer has just been accepted so now his reputation is 46. His lifetime prior to SO doesn't count.Lengthy
M
0

try this to get the file name of the executable :

C#:

string file = System.Windows.Forms.Application.ExecutablePath;

mfg

Marchal answered 31/5, 2010 at 7:52 Comment(1)
This is a bit of an old question now, but at the time, I was trying to get the EXE name of another process based on process ID. (Not the one that was running.) So this isn't helpful answer in this context.Daedalus

© 2022 - 2024 — McMap. All rights reserved.