Win32 - Get Main Wnd Handle of Application

Win32 - Get Main Wnd Handle of application

The host application may have multiple 'main windows'. To detect them, you could

  1. Call GetCurrentProcessId to get the PID of the current process
  2. Call EnumWindows to iterate over all toplevel windows of the desktop
  3. For each window on the desktop, call GetWindowThreadProcessId to get the PID of the process which created the window
  4. If the PID of the window matches the PID of your own process, memorize the window.

That gives you a list of toplevel windows created by the process which you injected your DLL into. However, please note that this approach may yield windows which have been destroyed by the time you process the constructed list of windows. Hence, when doing something with the windows, make sure to use the IsWindow function to ensure that the window at hand is still valid (this is still prone to race conditions since the window may become invalid between your call to IsWindow and actually accessing the window, but the time window is much smaller).

Here's a C++ function implementing this algorithm. It implements a getToplevelWindows function which yields a std::vector<HWND> containing the handles of all toplevel windows of the current process.

struct EnumWindowsCallbackArgs {
EnumWindowsCallbackArgs( DWORD p ) : pid( p ) { }
const DWORD pid;
std::vector<HWND> handles;
};

static BOOL CALLBACK EnumWindowsCallback( HWND hnd, LPARAM lParam )
{
EnumWindowsCallbackArgs *args = (EnumWindowsCallbackArgs *)lParam;

DWORD windowPID;
(void)::GetWindowThreadProcessId( hnd, &windowPID );
if ( windowPID == args->pid ) {
args->handles.push_back( hnd );
}

return TRUE;
}

std::vector<HWND> getToplevelWindows()
{
EnumWindowsCallbackArgs args( ::GetCurrentProcessId() );
if ( ::EnumWindows( &EnumWindowsCallback, (LPARAM) &args ) == FALSE ) {
// XXX Log error here
return std::vector<HWND>();
}
return args.handles;
}

UPDATE: These days (about four years after I gave the answer) I would also consider traversing the list of threads of the application and then using EnumThreadWindows on each thread. I noticed that this is considerably faster in many cases.

pywin32: how to get window handle from process handle and vice versa

Enumerate windows and then get the process handle for each window

You need these APIs:

  • win32gui.EnumWindows() to enumerate all top-level windows (that is no child windows aka controls)
  • win32process.GetWindowThreadProcessId() to get process ID from window handle
  • win32api.OpenProcess() to get process handle from process ID

Enumerate processes and then get the main application window handle
for each process

You need these APIs:

  • win32process.EnumProcesses() to enumerate all processes
  • win32api.GetWindowLong() with argument GWL_STYLE to get window styles and GWL_EXSTYLE to get extended window styles
  • win32gui.GetParent() to determine unowned windows

By filtering the result of EnumWindows() using GetWindowThreadProcessId() you can get all windows that belong to a given process.

Determining the main window can be tricky as there is no single window style that would designate a window as the main window. After all, an application might have multiple main windows.

Best you could do is to use the same rules that the taskbar uses to determine application windows, because that's what the user perceives as main windows:

The Shell places a button on the taskbar whenever an application
creates an unowned window—that is, a window that does not have a
parent and that has the appropriate extended style bits.

To ensure that the window button is
placed on the taskbar, create an unowned window with the
WS_EX_APPWINDOW extended style. To prevent the window button from
being placed on the taskbar, create the unowned window with the
WS_EX_TOOLWINDOW extended style. As an alternative, you can create a
hidden window and make this hidden window the owner of your visible
window.

Use GetParent() and GetWindowLong() to determine the unowned windows that have the right window styles according to these rules.

Getting HWND of current Process

You are (incorrectly) assuming that a process has only a single HWND. This is not generally true, and therefore Windows can't offer an API to get it. A program could create two windows, and have two HWNDs as a result. OTOH, if your program creates only a single window, it can store that HWND in a global variable.

How to get HANDLE for an application

You need to get the process id of the process you want to get the handle for and use OpenProcess.

Here's an example for this as well: http://msdn.microsoft.com/en-us/library/ms686701(v=VS.85).aspx

C++: Best way to get Window Handle of the only window from a process by process id, process handle and title name

Using FindWindow requires that you either know the window class or the window title. Both of these are not necessarily unique. Since you alread have the process handle (and its ID) you can implement a robust solution using EnumWindows.

First, declare a structure used for communication. It passes a process ID to the enumeration procedure and returns the window handle back.

// Structure used to communicate data from and to enumeration procedure
struct EnumData {
DWORD dwProcessId;
HWND hWnd;
};

Next, we need a callback procedure that retrieves the process ID (GetWindowThreadProcessId) for any given window and compares it to the one we are looking for:

// Application-defined callback for EnumWindows
BOOL CALLBACK EnumProc( HWND hWnd, LPARAM lParam ) {
// Retrieve storage location for communication data
EnumData& ed = *(EnumData*)lParam;
DWORD dwProcessId = 0x0;
// Query process ID for hWnd
GetWindowThreadProcessId( hWnd, &dwProcessId );
// Apply filter - if you want to implement additional restrictions,
// this is the place to do so.
if ( ed.dwProcessId == dwProcessId ) {
// Found a window matching the process ID
ed.hWnd = hWnd;
// Report success
SetLastError( ERROR_SUCCESS );
// Stop enumeration
return FALSE;
}
// Continue enumeration
return TRUE;
}

What's left is the public interface. It populates the structure used for communication with the process ID, triggers the enumeration of top-level windows, and returns the window handle. The calls to SetLastError and GetLastError are required, since EnumWindows returns FALSE for both error and success in this case:

// Main entry
HWND FindWindowFromProcessId( DWORD dwProcessId ) {
EnumData ed = { dwProcessId };
if ( !EnumWindows( EnumProc, (LPARAM)&ed ) &&
( GetLastError() == ERROR_SUCCESS ) ) {
return ed.hWnd;
}
return NULL;
}

// Helper method for convenience
HWND FindWindowFromProcess( HANDLE hProcess ) {
return FindWindowFromProcessId( GetProcessId( hProcess ) );
}

This will retrieve the first top-level window that matches a given process ID. Since the requirements state that there will only ever be a single window for the given process, the first one that matches is the correct window.

If additional restrictions exist, EnumProc can be expanded to include those. I have marked the spot in the implementation above, where additional filters can be applied.

Get path of a known HWND

To get the result I wanted, I switched to QueryFullProcessImageName (like CherryDT suggested to take a look at), but you have to be careful, you need to run it with Admin rights to get the path for some apps like I encountered with Task Manager, maybe because it is an Windows app, not sure and you'll have to do some research on that if you need more details. Here is a little example:

#include "Windows.h";
#include <iostream>
#include <chrono>
#include <thread>
using namespace std;

int main() {
// 2 seconds delay to have time to switch windows
std::this_thread::sleep_for(std::chrono::milliseconds(2000));

HWND hWnd = GetForegroundWindow();
int lgth = GetWindowTextLength(hWnd) + 1;
wchar_t* title = new wchar_t[lgth];
GetWindowTextW(hWnd, title, lgth);

DWORD id;
GetWindowThreadProcessId(hWnd, &id);

wchar_t* path = new wchar_t[MAX_PATH];
DWORD size = MAX_PATH;
HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, id);
QueryFullProcessImageNameW(hProc, 0, path, &size);
CloseHandle(hProc);

wcout << "ID: " << id << " | Title: " << title << " | Path: " << path << endl << endl;
return 1;
}

Output example: ID: 12580 | Title: Task Manage | Path: C:\Windows\System32\Taskmgr.exe



Related Topics



Leave a reply



Submit