In one of my programs I need to test if the user is currently focusing the desktop/shell window. Currently I'm using GetShellWindow() from user32.dll and compare the result to GetForegroundWindow().
This approach is working until someone changes the desktop wallpaper, but as soon as the wallpaper is changed the handle from GetShellWindow() doesn't match the one from GetForegroundWindow() anymore and I don't quite get why that is. (OS: Windows 7 32bit)
Is there a better approach to check if the desktop is focused? Preferably one that won't be broken if the user changes the wallpaper?
EDIT: I designed a workaround: I'm testing the handle to have a child of class "SHELLDLL_DefView". If it has, the desktop is on focus. Whilst it's working at my PC that doesn't mean it will work all the timeā¦
The thing changed a little bit since there are slideshows as wallpaper available in Windows 7.
You are right with WorkerW, but this works only with wallpaper is set to slideshow effect.
When there is set the wallpaper mode to slideshow, you have to search for a window of class WorkerW and check the children, whether there is a SHELLDLL_DefView.
If there is no slideshow, you can use the good old GetShellWindow().
I had the same problem some months ago and I wrote a function for getting the right window. Unfortunately I can't find it. But the following should work. Only the Win32 Imports are missing:
public enum DesktopWindow
{
ProgMan,
SHELLDLL_DefViewParent,
SHELLDLL_DefView,
SysListView32
}
public static IntPtr GetDesktopWindow(DesktopWindow desktopWindow)
{
IntPtr _ProgMan = GetShellWindow();
IntPtr _SHELLDLL_DefViewParent = _ProgMan;
IntPtr _SHELLDLL_DefView = FindWindowEx(_ProgMan, IntPtr.Zero, "SHELLDLL_DefView", null);
IntPtr _SysListView32 = FindWindowEx(_SHELLDLL_DefView, IntPtr.Zero, "SysListView32", "FolderView");
if (_SHELLDLL_DefView == IntPtr.Zero)
{
EnumWindows((hwnd, lParam) =>
{
if (GetClassName(hwnd) == "WorkerW")
{
IntPtr child = FindWindowEx(hwnd, IntPtr.Zero, "SHELLDLL_DefView", null);
if (child != IntPtr.Zero)
{
_SHELLDLL_DefViewParent = hwnd;
_SHELLDLL_DefView = child;
_SysListView32 = FindWindowEx(child, IntPtr.Zero, "SysListView32", "FolderView"); ;
return false;
}
}
return true;
}, IntPtr.Zero);
}
switch (desktopWindow)
{
case DesktopWindow.ProgMan:
return _ProgMan;
case DesktopWindow.SHELLDLL_DefViewParent:
return _SHELLDLL_DefViewParent;
case DesktopWindow.SHELLDLL_DefView:
return _SHELLDLL_DefView;
case DesktopWindow.SysListView32:
return _SysListView32;
default:
return IntPtr.Zero;
}
}
In your case you would call GetDesktopWindow(DesktopWindow.SHELLDLL_DefViewParent); to get the top-level window for checking whether it is the foreground window.
Here is a workaround that uses GetClassName() to detect if the desktop is active:
When Windows first starts, the desktop's Class is "Progman"
After changing the wallpaper, the desktop's Class will be "WorkerW"
You can test against these to see if the desktop is focused.
[DllImport("user32.dll")]
static extern int GetForegroundWindow();
[DllImport("user32.dll")]
static extern int GetClassName(int hWnd, StringBuilder lpClassName, int nMaxCount);
public void GetActiveWindow() {
const int maxChars = 256;
int handle = 0;
StringBuilder className = new StringBuilder(maxChars);
handle = GetForegroundWindow();
if (GetClassName(handle, className, maxChars) > 0) {
string cName = className.ToString();
if (cName == "Progman" || cName == "WorkerW") {
// desktop is active
} else {
// desktop is not active
}
}
}
Related
Appreciate guidance and help here please;
I have searched and tried many solutions and it seems non are working with .Net 6 when I tried to center the MessageBox.Show() at the center of the parent Form!
So I have followed this:
Winforms-How can I make MessageBox appear centered on MainForm?
AND
How to Make MessageBoxes Center on their Parent Forms
When I tried to debug the code above and step through, the hook is not being called. Win32 is a wrapper calss of User32.dll
public static DialogResult Show(string msg, string title, MessageBoxButtons btns, MessageBoxIcon icon)
{
// Create a callback delegate
_hookProcDelegate = new Win32.WindowsHookProc(HookCallback);
// Remember the title & message that we'll look for.
// The hook sees *all* windows, so we need to make sure we operate on the right one.
_msg = msg;
_title = title;
// Set the hook.
// Suppress "GetCurrentThreadId() is deprecated" warning.
// It's documented that Thread.ManagedThreadId doesn't work with SetWindowsHookEx()
#pragma warning disable 0618
_hHook = Win32.SetWindowsHookEx(Win32.WH_CBT, _hookProcDelegate, IntPtr.Zero, AppDomain.GetCurrentThreadId());
#pragma warning restore 0618
// Pop a standard MessageBox. The hook will center it.
DialogResult rslt = MessageBox.Show(msg, title, btns, icon);
// Release hook, clean up (may have already occurred)
Unhook();
return rslt;
}
private static int HookCallback(int code, IntPtr wParam, IntPtr lParam)
{
int hHook = _hHook; // Local copy for CallNextHookEx() JIC we release _hHook
// Look for HCBT_ACTIVATE, *not* HCBT_CREATEWND:
// child controls haven't yet been created upon HCBT_CREATEWND.
if (code == Win32.HCBT_ACTIVATE)
{
string cls = Win32.GetClassName(wParam);
if (cls == "#32770") // MessageBoxes are Dialog boxes
{
string title = Win32.GetWindowText(wParam);
string msg = Win32.GetDlgItemText(wParam, 0xFFFF); // -1 aka IDC_STATIC
if ((title == _title) && (msg == _msg))
{
CenterWindowOnParent(wParam);
Unhook(); // Release hook - we've done what we needed
}
}
}
return Win32.CallNextHookEx(hHook, code, wParam, lParam);
}
_hook returns 0 / Last Error Code 87 (ERROR_INVALID_PARAMETER)
_hHook = Win32.SetWindowsHookEx(Win32.WH_CBT, _hookProcDelegate, IntPtr.Zero, AppDomain.GetCurrentThreadId());
int lastErrorCode = Marshal.GetLastWin32Error();
The SetWindowsHookEx:
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int SetWindowsHookEx(int idHook, WindowsHookProc lpfn, IntPtr hInstance, int threadId)
;
I ran the same code with .Net Framework 4.8 and it works fine! is there anything I have to update to make it works with .Net 6
Basically I've written some code that listens for a "save as" dialog box to pop up inside an application, and when it does it presses "save", all through code. This works great, however I need to be able to set the file path to what I'd like before saving.
Here is my code so far:
using System;
using System.Diagnostics;
using System.ComponentModel;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using HWND = System.IntPtr;
using System.Text;
/// <summary>Contains functionality to get all the open windows.</summary>
public static class OpenWindowGetter
{
private const int BN_CLICKED = 245;
/// <summary>Returns a dictionary that contains the handle and title of all the open windows.</summary>
/// <returns>A dictionary that contains the handle and title of all the open windows.</returns>
public static IDictionary<HWND, string> GetOpenWindows()
{
HWND shellWindow = GetShellWindow();
Dictionary<HWND, string> windows = new Dictionary<HWND, string>();
EnumWindows(delegate (HWND hWnd, int lParam)
{
if (hWnd == shellWindow) return true;
if (!IsWindowVisible(hWnd)) return true;
int length = GetWindowTextLength(hWnd);
if (length == 0) return true;
StringBuilder builder = new StringBuilder(length);
GetWindowText(hWnd, builder, length + 1);
if (builder.ToString() == "Export Selection") //Check for the export selection window
{
//Press the "save" button through code here
IntPtr hwndChild = FindWindowEx((IntPtr)hWnd, IntPtr.Zero, "Button", "&Save");
SendMessage((HWND)(int)hwndChild, BN_CLICKED, (HWND)0, IntPtr.Zero);
}
windows[hWnd] = builder.ToString();
return true;
}, 0);
return windows;
}
private delegate bool EnumWindowsProc(HWND hWnd, int lParam);
[DllImport("USER32.DLL")]
private static extern int SetWindowText(HWND hWnd, String lpString);
[DllImport("USER32.DLL")]
private static extern bool EnumWindows(EnumWindowsProc enumFunc, int lParam);
[DllImport("USER32.DLL")]
private static extern int GetWindowText(HWND hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("USER32.DLL")]
private static extern int GetWindowTextLength(HWND hWnd);
[DllImport("USER32.DLL")]
private static extern bool IsWindowVisible(HWND hWnd);
[DllImport("USER32.DLL")]
private static extern IntPtr GetShellWindow();
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string lclassName, string windowTitle);
[DllImport("User32.dll")]
public static extern Int32 SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
}
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
while (true)
{
foreach (KeyValuePair<IntPtr, string> window in OpenWindowGetter.GetOpenWindows())
{
IntPtr handle = window.Key;
string title = window.Value;
//Console.WriteLine("{0}: {1}", handle, title);
}
}
}
}
}
I've been using FindWindowEx and SendMessage for controlling the window handles. This worked perfectly for the Save button, but now I'm trying to access the toolbar part of the window and set its text to the desired directory. I'm not even sure if this approach will work, but it seems the most simple. I've attached a screenshot for reference (the area circled in red is the handle I'm trying to access and change to a given file path before I "press" save)
As seen in the photo I've been using Spy++ to get info about the Window and its handles. If I attempt this for example to get a pointer to that handle:
IntPtr hwndChildToolbar= FindWindowEx((IntPtr)hWnd, IntPtr.Zero, "ToolbarWindow32", "Address: "+"C:\\Windows\\System32");
It doesn't work. The "Caption" value seen in the Spy++ app changes to whatever the current directory name is, so trying to access that doesn't seem to make sense.
This next line of code does give me a pointer in return, but it's not the correct address. Worth noting is there are multiple handles on this window that fall under the ToolbarWindow32 class:
IntPtr hwndChildToolbar= FindWindowEx((IntPtr)hWnd, IntPtr.Zero, "ToolbarWindow32", null);
If I can find a way to get the correct handle, then from here I just want to use SetWindowText if that's possible and set its value to a string that's my intended file path.
To sum it up I need some way to easily set the directory, and I'm not sure if this way is possible. My C# knowledge is limited so anything helps!
Here is a sample .NET Framework Console App that uses UI Automation, opens Notepad, type something and saves it as a file in the temp folder, using the Address bar on top of the Common Dialog.
See here .NET UI Automation Overview or here Native UI Automation for introduction and reference on UI Automation. It's usually better to use UI Automation than hacking windows handles. If you can't do it with UI Automation, there are chances you won't be able to do it with handles anyway. To discover the elements you can use and code against, you can use the Inspect tool from the Windows SDK.
Note I'm using here an interop version of the native UI Automation, as the .NET original wrappers that are provided with Windows have not been updated by Microsoft for many years for some reason.
// this code needs the "Interop.UIAutomationClient" Nuget package and "using Interop.UIAutomationClient"
class Program
{
private static readonly CUIAutomation8 _automation = new CUIAutomation8();
static void Main()
{
// track window open event
var processId = 0;
_automation.AddAutomationEventHandler(UIA_EventIds.UIA_Window_WindowOpenedEventId, _automation.GetRootElement(), TreeScope.TreeScope_Subtree, null,
new AutomationEventHandler((window, id) =>
{
// check the process id we opened
if (window.CurrentProcessId != processId)
return;
// get editor control
var editor = window.FindFirst(TreeScope.TreeScope_Children, _automation.CreatePropertyCondition(UIA_PropertyIds.UIA_ControlTypePropertyId, UIA_ControlTypeIds.UIA_EditControlTypeId));
if (editor == null) // not the window we're looking for
return;
// get editor's value pattern & set some text value
var value = (IUIAutomationValuePattern)editor.GetCurrentPattern(UIA_PatternIds.UIA_ValuePatternId);
value.SetValue("hello world");
// get menu bar
var menuBar = window.FindFirst(TreeScope.TreeScope_Children, _automation.CreatePropertyCondition(UIA_PropertyIds.UIA_ControlTypePropertyId, UIA_ControlTypeIds.UIA_MenuBarControlTypeId));
if (menuBar == null)
{
Console.WriteLine("Can't find menu bar.");
return;
}
// get "File" menu item (beware of localization) & invoke (open)
var file = menuBar.FindFirst(TreeScope.TreeScope_Children, _automation.CreatePropertyCondition(UIA_PropertyIds.UIA_NamePropertyId, "File"));
if (file == null)
{
Console.WriteLine("Can't find 'File' menu item.");
return;
}
// expand "File" menu
var expand = (IUIAutomationExpandCollapsePattern)file.GetCurrentPattern(UIA_PatternIds.UIA_ExpandCollapsePatternId);
expand.Expand();
do
{
// get the "Save" item by name from the window subtree (as the menu that opens is a child of the window)
// do some retry to handle menu opening time
var save = window.FindFirst(TreeScope.TreeScope_Subtree, _automation.CreatePropertyConditionEx(UIA_PropertyIds.UIA_NamePropertyId, "Save", PropertyConditionFlags.PropertyConditionFlags_MatchSubstring));
if (save != null)
{
((IUIAutomationInvokePattern)save.GetCurrentPattern(UIA_PatternIds.UIA_InvokePatternId)).Invoke();
break;
}
}
while (true);
// get the "Save As" dialog
// do some retry to handle dialog opening time
IUIAutomationElement dialog;
do
{
dialog = window.FindFirst(TreeScope.TreeScope_Subtree, _automation.CreatePropertyCondition(UIA_PropertyIds.UIA_LocalizedControlTypePropertyId, "dialog"));
if (dialog != null)
break;
}
while (true);
// get the "Previous locations" to enable the Address edit box
var previous = dialog.FindFirst(TreeScope.TreeScope_Subtree, _automation.CreateAndCondition(
_automation.CreatePropertyCondition(UIA_PropertyIds.UIA_ControlTypePropertyId, UIA_ControlTypeIds.UIA_ButtonControlTypeId),
_automation.CreatePropertyCondition(UIA_PropertyIds.UIA_NamePropertyId, "Previous Locations")));
if (previous == null)
{
Console.WriteLine("Can't find 'Previous Locations' button.");
return;
}
// push "Previous Locations" button
var previousButton = (IUIAutomationInvokePattern)previous.GetCurrentPattern(UIA_PatternIds.UIA_InvokePatternId);
previousButton.Invoke();
// enter the directory path
var address = dialog.FindFirst(TreeScope.TreeScope_Subtree, _automation.CreateAndCondition(
_automation.CreatePropertyCondition(UIA_PropertyIds.UIA_ControlTypePropertyId, UIA_ControlTypeIds.UIA_EditControlTypeId),
_automation.CreatePropertyCondition(UIA_PropertyIds.UIA_NamePropertyId, "Address")));
if (address == null)
{
Console.WriteLine("Can't find 'Address' edit.");
return;
}
// sets the directory (here we use the temp directory)
var edit = (IUIAutomationValuePattern)address.GetCurrentPattern(UIA_PatternIds.UIA_ValuePatternId);
edit.SetValue(System.IO.Path.GetTempPath());
// push "Previous Locations" button again to "commit"
previousButton.Invoke();
// get the "File name:" edit
// do some retry to handle folder refresh
do
{
var fileName = dialog.FindFirst(TreeScope.TreeScope_Subtree, _automation.CreateAndCondition(
_automation.CreatePropertyCondition(UIA_PropertyIds.UIA_ControlTypePropertyId, UIA_ControlTypeIds.UIA_EditControlTypeId),
_automation.CreatePropertyConditionEx(UIA_PropertyIds.UIA_NamePropertyId, "File name", PropertyConditionFlags.PropertyConditionFlags_MatchSubstring)));
if (fileName != null)
{
// sets the file name (some "random" name)
((IUIAutomationValuePattern)fileName.GetCurrentPattern(UIA_PatternIds.UIA_ValuePatternId)).SetValue(#"hello" + Environment.TickCount + ".txt");
break;
}
}
while (true);
// get the "Save" button
var dialogSave = dialog.FindFirst(TreeScope.TreeScope_Subtree, _automation.CreateAndCondition(
_automation.CreatePropertyCondition(UIA_PropertyIds.UIA_ControlTypePropertyId, UIA_ControlTypeIds.UIA_ButtonControlTypeId),
_automation.CreatePropertyCondition(UIA_PropertyIds.UIA_NamePropertyId, "Save")));
if (dialogSave == null)
{
Console.WriteLine("Can't find 'Save' button.");
return;
}
// press the 'Save' button
((IUIAutomationInvokePattern)dialogSave.GetCurrentPattern(UIA_PatternIds.UIA_InvokePatternId)).Invoke();
}));
// start notepad
var process = Process.Start("notepad");
processId = process.Id;
Console.WriteLine("Press any key to quit...");
Console.ReadKey(false);
try
{
process.CloseMainWindow();
}
catch
{
// maybe closed by something else, do nothing
}
}
// helper class
class AutomationEventHandler : IUIAutomationEventHandler
{
public AutomationEventHandler(Action<IUIAutomationElement, int> action)
{
if (action == null)
throw new ArgumentNullException(nameof(action));
Action = action;
}
public Action<IUIAutomationElement, int> Action { get; }
public void HandleAutomationEvent(IUIAutomationElement sender, int eventId) => Action(sender, eventId);
}
}
I want to set the foreground of a window in Visual Studio.
What i've tried:
When i have 2 windows "docked" side by side i can't set the foreground of a window under the mouse in VS.
I'm using GetCursorPos and WindowFromPoint which usually works for standard windows. I also tried to use EnumChildWindows from pinvoke.net (sample code 2 -https://www.pinvoke.net/default.aspx/user32.enumchildwindows) but it returns 0 when i pass the WindowFromPoint or MainWindowHandle of the process.
I assume it's not a typical child window technically or something else that i don't understand?
public static IntPtr GetProcessMainWindowHandle()
{
Process process = null;
if (GetCursorPos(out Point point))
{
IntPtr hWnd = WindowFromPoint(point);
GetWindowThreadProcessId(hWnd, out uint pid);
process = Process.GetProcessById((int)pid);
}
return process.MainWindowHandle;
}
public static IntPtr GetHandle()
{
IntPtr hWnd = IntPtr.Zero;
if (GetCursorPos(out Point point))
{
hWnd = WindowFromPoint(point);
}
return hWnd;
}
As mentioned in the comments from Lars, it could be a WPF window.
I can see that i can only get the handle for the parent window as described for WPF behaviour but i get still null with the following code. Maybe i have something wrong? (Any hint would be great.)
public static void GetWPFObject(IntPtr handle)
{
HwndSource source = HwndSource.FromHwnd(handle) as HwndSource;
}
I have handles to the main form of the Winforms application, and the window that I'm trying to check (which may or may not be part of the application). I've tried iterating using GetParent, but it doesn't seem to work.
What I'm essentially trying to do is detect a modal window (such as a MsgBox), get it's controls, and send a button click message if the controls fulfill some requirements (like be a Button).
Now, while I can detect if a modal window is open, and can find the currently focused window, I have no idea if the currently focused window is the modal window that was detected. Essentially, if I open a model window and then open up a completely different program, it tries to find the controls of that external program.
The code is below:
if (pF.Visible && !pF.CanFocus) //Is a Modal Window
{
///TODO: Check if currently active window is a child of the main window
///Gets information of currently active window
string currentActiveWindow = GetActiveWindowTitle();
IntPtr currentActiveHandle = GetActiveWindowHandle();
///Gets 'children' or controls of currently active window
var hwndChild = EnumAllWindows(currentActiveHandle);
///Iterate over all child windows
foreach (IntPtr element in hwndChild) {
const int nChars = 256;
StringBuilder Buff = new StringBuilder(nChars);
IntPtr handle = GetForegroundWindow();
string windowElementType = GetWindowClassName(element);
///Check if the "windows" are buttons
if (GetWindowText(element, Buff, nChars) > 0 && windowElementType=="Button")
{
string windowElement = Buff.ToString();
if (windowElement.ToLower()=="ok")
{
///Send Button click message
const int BM_CLICK = 0x00F5;
SendMessage(element, BM_CLICK, IntPtr.Zero, IntPtr.Zero);
}
}
}
}
A convenience function (C++) to determine whether two windows identified by their HWND belong to the same process would look like this:
bool OwnedBySameProcess(HWND hWnd1, HWND hWnd2) {
if ( ::IsWindow(hWnd1) && ::IsWindow(hWnd2) ) {
DWORD procId1 = 0x0;
DWORD procId2 = 0x0;
::GetWindowThreadProcessId(hWnd1, &procId1);
::GetWindowThreadProcessId(hWnd2, &procId2);
return ( procId1 == procId2 );
}
return false;
}
The GetWindowThreadProcessId is not subject to UIPI (User Interface Privilege Isolation) and will always succeed given valid input. The return values are IDs and do not need to be cleaned up.
Translated to C#:
public class Helper
{
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool IsWindow(IntPtr hWnd);
[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(IntPtr hWnd,
out uint lpdwProcessId);
public static bool OwnedBySameProcess(IntPtr hWnd1, IntPtr hWnd2)
{
if ( !IsWindow(hWnd1) )
throw new ArgumentException("hWnd1");
if ( !IsWindow(hWnd2) )
throw new ArgumentException("hWnd2");
uint procId1 = 0;
GetWindowThreadProcessId(hWnd1, out procId1);
uint procId2 = 0;
GetWindowThreadProcessId(hWnd2, out procId2);
return ( procId1 == procId2 );
}
}
i "stole" the code from
http://improve.dk/archive/2007/04/07/finding-specific-windows.aspx
but instead of writing the class name , title and handle into the console i want to check if a certain button is visible. and if the button is visible i want to maximize the window.
i changed this part =>
private static bool foundWindow(int handle)
{
bool buttonCheck = false;
IntPtr hButton = FindWindowEx((IntPtr)handle, IntPtr.Zero, "AfxWnd90u21", null);
if (hButton != IntPtr.Zero)
{
buttonCheck = true;
}
if (buttonCheck)
{
ShowWindowAsync(handle, (int)3); // maximize the window
}
return true;
}
the button class is `AfxWnd90u` and the instance is `21`. I wrote this in autoit before and AfxWnd90u21 is 100 % correct.
the problem is that i cant find the button with AfxWnd90u21. if i only use
IntPtr hButton = FindWindowEx((IntPtr)handle, IntPtr.Zero, "AfxWnd90u", null);
all windows get maximized.
It has to be something with the instance.
i hope you can help me,
thanks
Newest Edit
i just tried to find the class name with "GetClassName". I find 190~ classes per handle, but the class that i need is not in there.
iam really desperate
I hope someone can help me,
thanks
private static bool foundWindow(int handle)
{
int i = 0;
IntPtr hWnd = (IntPtr)handle;
// System.Windows.Forms.Control control = System.Windows.Forms.Control.FromHandle(hWnd);
StringBuilder sbClass = new StringBuilder(256);
while (hWnd != IntPtr.Zero)
{
++i;
///////////////////////////////////////////////////
////////////// Compare if the classname exists/////
GetClassName((int)hWnd, sbClass, sbClass.Capacity);
if (sbClass.ToString().Equals("AfxWnd90u21"))
{
MessageBox.Show(sbClass.ToString());
}
///////////////////////////////////////////////////
////// trying to find the correct class with findwindowEX//////////
IntPtr hButton = FindWindowEx(hWnd, IntPtr.Zero, "AfxWnd90u21", null);
if (hButton != IntPtr.Zero)
{
MessageBox.Show("true");
ShowWindowAsync(handle, (int)2); // maximize the window
}
hWnd = FindWindowEx(IntPtr.Zero, hWnd, null, null);
}
MessageBox.Show(""+i);
return true;
}
http://msdn.microsoft.com/en-us/library/windows/desktop/ms633500%28v=vs.85%29.aspx
lpszWindow [in, optional]
Type: LPCTSTR
The window name (the window's title). If this parameter is NULL,
all window names match.
Looks like with this API, in order to match an instance, you need to give your instances unique window names. Or, you could search through all children manually cast to a Control, then check the instances yourself.
But if you go that far, it's easier to cast the parent to a Control, and iterate through it's .Controls member. You can use reflection to check the control's type and so on.
To convert a handle to a control:
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.fromhandle.aspx
Iterate over Control.Controls using whichever loop style you prefer.