.Net (C#): Getting Child Windows When You Only Have a Process Handle or Pid

.NET (C#): Getting child windows when you only have a process handle or PID?

If you don't mind using the Windows API, you could use EnumWindowsProc, and check each of the handles that that turns up using GetWindowThreadProcessId (to see that it's in your process), and then maybe IsWindowVisible, GetWindowCaption and GetWindowTextLength to determine which hWnd in your process is the one you want.

Though if you haven't used those functions before that approach will be a real pain, so hopefully there's a simpler way.

Get Child window using ProcessID or Window Handle?

I found the soln.

1. Get handle of parent window A.

PerformanceCounter perId = new PerformanceCounter("Process", "Creating Process ID", Process.GetCurrentProcess().ProcessName);


2. Use GetLastActivePopup method to get last active popup (Last popup works in my case :) ) .

Get MS-Word application instance using process handle

Given a window handle, you get an automatable Word application instance using the AccessibleObjectFromWindow function from oleacc.dll.

Here is a sample program showing how to use it (add a reference to Microsoft.Office.Interop.Word.dll):

using Microsoft.Office.Interop.Word;
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;

namespace WordLateBindingSample
{
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("00020400-0000-0000-C000-000000000046")]
public interface IDispatch
{
}

class Program
{
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

[DllImport("Oleacc.dll")]
static extern int AccessibleObjectFromWindow(int hwnd, uint dwObjectID, byte[] riid, out IDispatch ptr);

public delegate bool EnumChildCallback(int hwnd, ref int lParam);

[DllImport("User32.dll")]
public static extern bool EnumChildWindows(int hWndParent, EnumChildCallback lpEnumFunc, ref int lParam);

[DllImport("User32.dll")]
public static extern int GetClassName(int hWnd, StringBuilder lpClassName, int nMaxCount);

public static bool EnumChildProc(int hwndChild, ref int lParam)
{
StringBuilder buf = new StringBuilder(128);
GetClassName(hwndChild, buf, 128);
if (buf.ToString() == "_WwG")
{
lParam = hwndChild;
return false;
}
return true;
}

static void Main(string[] args)
{
// Use the window class name ("OpusApp") to retrieve a handle to Word's main window.
// Alternatively you can get the window handle via the process id:
// int hwnd = (int)Process.GetProcessById(wordPid).MainWindowHandle;
//
int hwnd = (int)FindWindow("OpusApp", null);

if (hwnd != 0)
{
int hwndChild = 0;

// Search the accessible child window (it has class name "_WwG")
// as described in http://msdn.microsoft.com/en-us/library/dd317978%28VS.85%29.aspx
//
EnumChildCallback cb = new EnumChildCallback(EnumChildProc);
EnumChildWindows(hwnd, cb, ref hwndChild);

if (hwndChild != 0)
{
// We call AccessibleObjectFromWindow, passing the constant OBJID_NATIVEOM (defined in winuser.h)
// and IID_IDispatch - we want an IDispatch pointer into the native object model.
//
const uint OBJID_NATIVEOM = 0xFFFFFFF0;
Guid IID_IDispatch = new Guid("{00020400-0000-0000-C000-000000000046}");
IDispatch ptr;

int hr = AccessibleObjectFromWindow(hwndChild, OBJID_NATIVEOM, IID_IDispatch.ToByteArray(), out ptr);

if (hr >= 0)
{
var wordApp = (Application)ptr.GetType().InvokeMember("Application", BindingFlags.GetProperty, null, ptr, null);

var version = wordApp.Version;
Console.WriteLine(string.Format("Word version is: {0}", version));
}
}
}
}
}
}

How to enumerate all windows within a process?

3rd party aplication launched other windows not as child windows.

It is possible to find out what is structure using Spy++ tool which comes with Visual Studio.

After this, I was able to find necessary window using FindWindowEx function using WindowClassName (taken from Spy++):
lastWindows = FindWindowEx(IntPtr.Zero, lastWindows, m.WindowClassName, null);

Kill process tree programmatically in C#

This worked very nicely for me:

/// <summary>
/// Kill a process, and all of its children, grandchildren, etc.
/// </summary>
/// <param name="pid">Process ID.</param>
private static void KillProcessAndChildren(int pid)
{
// Cannot close 'system idle process'.
if (pid == 0)
{
return;
}
ManagementObjectSearcher searcher = new ManagementObjectSearcher
("Select * From Win32_Process Where ParentProcessID=" + pid);
ManagementObjectCollection moc = searcher.Get();
foreach (ManagementObject mo in moc)
{
KillProcessAndChildren(Convert.ToInt32(mo["ProcessID"]));
}
try
{
Process proc = Process.GetProcessById(pid);
proc.Kill();
}
catch (ArgumentException)
{
// Process already exited.
}
}

Update 2016-04-26

Tested on Visual Studio 2015 Update 2 on Win7 x64. Still works as well now as it did 3 years ago.

Update 2017-11-14

Added check for system idle process if (pid == 0)

Update 2018-03-02

Need to add a reference to the System.Management namespace, see comment from @MinimalTech below. If you have ReSharper installed, it will offer to do this for you automatically.

Update 2018-10-10

The most common use case for this is killing any child processes that our own C# process has started.

In this case, a better solution is to use Win32 calls within C# to make any spawned process a child process. This means that when the parent process exits, any child processes are automatically closed by Windows, which eliminates the need for the code above. Please let me know if you want me to post the code.

Set the window state of a hidden window

The problem with notepad is that it has 3 windows (spy++, class names):

1. "Notepad"
2. "MSCTFIME UI"
3. "IME"

you are getting the handle of the second one (I got anyway), MSCTFIME UI, thats why you can't show it. You need to specify the class name Notepad to get the correct handle:

pHandle = FindWindowEx(IntPtr.Zero, pHandle, "Notepad", Nothing)


Related Topics



Leave a reply



Submit