How to Get Process Handle from Process Id

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

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

How to give a program's Handle to a process it created?

The easiest way to transmit this handle seems to be by putting the Handle in the commandline arguments of CreateProcess

That is one way to do it, but it is not the only way.

Another simple way is to have Parent send its process ID from GetCurrentProcessId() to Kid, and then Kid can use OpenProcess() to get a handle to Parent.

there is no way I can see to get the Parent's Handle inside Parent.

GetCurrentProcess(), which returns a pseudo-handle representing the calling process. All APIs that accept a process handle will accept this pseudo-handle when used in the context of the calling process.

But, for purposes of passing the calling process's handle to another process, Parent would have to use DuplicateHandle() to convert the pseudo-handle into a real handle (set Parent as both source and target process). This is documented behavior.

GetCurrentProcess returns a weird non-value, and DuplicateHandle doesn't work without having the Kid's Handle

After Parent has duplicated the pseudo-handle from GetProcessHandle() into a real handle, it can then pass that duplicate to Kid on the command line. Just make sure the duplicate is inheritable, and then use bInheritHandles=TRUE in the CreateProcess() call, or pass a STARTUPINFOEX to CreateProcess() containing a PROC_THREAD_ATTRIBUTE_HANDLE_LIST (see Programmatically controlling which handles are inherited by new processes in Win32).

Impossible, since I need to create the Kid to get a Handle to it

If you don't want to use inheritable handles, then Parent can alternatively create Kid without passing any handle on the command line, then duplicate the GetCurrentProcess() pseudo-handle with Kid as the target process, then use an IPC mechanism of your choosing to send the duplicate to Kid after it is already running.

but CreateProcess is also the only chance to send the Parent's Handle to the Kid

No, it is not the only way. IPC is another way.

Get running process given process handle

In plain C#, it looks like you have to loop through them all:

// IntPtr myHandle = ...
Process myProcess = Process.GetProcesses().Single(
p => p.Id != 0 && p.Handle == myHandle);

The above example intentionally fails if the handle isn't found. Otherwise, you could of course use SingleOrDefault. Apparently, it doesn't like you requesting the handle of process ID 0, hence the extra condition.

Using the WINAPI, you can use GetProcessId. I couldn't find it on, but this should do:

static extern int GetProcessId(IntPtr handle);

(signature uses a DWORD, but process IDs are represented by ints in the .NET BCL)

It seems a bit odd that you'd have a handle, but not a process ID however. Process handles are acquired by calling OpenProcess, which takes a process ID.

Is it possible to get a Process object by process handle?

I "solved" the problem by doing the following:

// Construct Process object through private constructor, taking the PID. This also works when the process has already exited.
Process new_process = Activator.CreateInstance(typeof(Process), BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { ".", false, (int)pid, null }, null, null) as Process;

// Wrap the handle and give ownership to wrapper
SafeProcessHandle safe_handle = new SafeProcessHandle((IntPtr)process_handle, true);

// Set interal process handle for Process object through private method. This prevents the .ExitCode accessor to throw an exception.
typeof(Process).GetMethod("SetProcessHandle", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(new_process, new object[] { safe_handle });

This works, but is kinda not great. Because it accesses two private functions through reflection. For more info about the internals of the Process object please have a look here.

Find process id by window's handle

You can use the following Windows API:

[DllImport("user32.dll", SetLastError=true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);

You pass in the HWND and use the out parameter to return the PID.

You can read more on this function here on MSDN.

How to get process id from SYSTEM_PROCESS_INFORMATION struct?

Most of the NT api will contain the following statement:

[Nt*** may be altered or unavailable in future versions of Windows.]

In your case, you can use the following methods instead of NtQuerySystemInformation to retrieve the process of the system:

  • CreateToolhelp32Snapshot + Process32First/Process32Next
  • EnumProcesses
  • WTSEnumerateProcesses
  • WTSEnumerateProcessesEx(WTS_ANY_SESSION)

Related Topics

Leave a reply