问题
I have handle and pid of main process / window.
I want to get the children of the process / window - only the visual windows with title.
to achieve what I want, I used the following:
[DllImport("user32.dll")] private static extern int EnumWindows(EnumWindowsProc ewp, int lParam);
[DllImport("user32.dll", SetLastError = true)] static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("user32.dll")] private static extern bool IsWindowVisible(int hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto)] private static extern int GetWindowText(int hWnd, StringBuilder title, int size);
public delegate bool EnumWindowsProc(int hWnd, int lParam); //Delegate used for EnumWindows() callback function.
public void GetChildren()
{
//Declare a callback delegate for EnumWindows() API call.
EnumWindowsProc ewp = new EnumWindowsProc(EvalWindow);
//Enumerate all Windows.
EnumWindows(ewp, 0);
}
//EnumWindows CALLBACK function
private bool EvalWindow(int hWnd, int lParam)
{
//Get the ThreadProcessId of the Window.
IntPtr handle = (IntPtr)hWnd;
uint currentThreadPid;
GetWindowThreadProcessId(handle, out currentThreadPid);
//Check if the Window is children of the current Window (if it is, it will have the same pid).
if (currentThreadPid != _threadPid)
return true;
//Check if Window is Visible or not.
if (!IsWindowVisible(hWnd))
return true;
//Get the Window's Title.
StringBuilder title = new StringBuilder(256);
GetWindowText(hWnd, title, 256);
//Check if Window has Title.
if (title.Length == 0)
return true;
//Add new Window to the _childrenhWnd.
_childrenhWnd.Add(handle);
return true;
}
where _threadPid
is the pid of the parent.
It works great but I feel there's a better way of doing it instead of going through all the opened windows and filter them.
Am I right? how can I do it better?
I saw something about EnumChildWindows
but I could achieve the same.
EDIT:
Based on RaymondChen comment, I want to clarify what I mean by saying "children":
1. If you open sticky notes and add notes - the notes are children.
2. If you open Paint.NET and press (f5, f6, f7, f8) to open the panels
- these are child windows.
Every window of the program which is not the main (dialogs also) I call children.
EDIT 2:
Here's my code after applying Hans's method:
[DllImport("user32.dll")] static extern bool EnumThreadWindows(int dwThreadId, EnumThreadDelegate lpfn, IntPtr lParam);
[DllImport("user32.dll")] private static extern bool IsWindowVisible(int hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto)] private static extern int GetWindowText(int hWnd, StringBuilder title, int size);
delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam);
public void GetChildren()
{
foreach (ProcessThread processThread in _process.Threads)
{
EnumThreadWindows(processThread.Id,
(hWnd, lParam) =>
{
//Check if Window is Visible or not.
if (!IsWindowVisible((int)hWnd))
return true;
//Get the Window's Title.
StringBuilder title = new StringBuilder(256);
GetWindowText((int)hWnd, title, 256);
//Check if Window has Title.
if (title.Length == 0)
return true;
_childrenhWnd.Add(hWnd);
return true;
}, IntPtr.Zero);
}
}
didnt need to use GetWindowLongPtr
because all the owned windows must have title and be visible else I dont really care so these conditions already filter the owned windows.
回答1:
Clearly you are not actually talking about child windows, EnumWindows() doesn't enumerate them. These are surely owned windows, they stay on top of their owner and get minimized when the owner is minimized. Used for tool windows and dialogs, like Paint.NET uses. There is no dedicated winapi function to enumerate owned windows.
You can make it more efficient. Enumerate the threads in the process, use Process.Threads. Then for each thread use EnumThreadWindows(). GetWindowLongPtr() with GWLP_HWNDPARENT returns the handle of the owner, non-zero if it is an owned window.
来源:https://stackoverflow.com/questions/22173915/get-children-windows-of-window-by-handle-or-pid