TL;DR; at the end
I am trying to do a tiny scheduler for my wife to automate some tasks and I am having some problems with interacting to another application when minimized to system tray.
As long as I start the other app within my program using
System.Diagnostics.Process.Start(Path\App)
I can get the
MainWindowHandle
and then I don't care where it is later, I can interact with it using the previously saved handle without any problem.
The other App is MDI but I don't want to have more than one instance at the same time to avoid problems (like Notepad writing in different instances to the same file), so I start my checks with:
Process[] processes = Process.GetProcessesByName("AppFriendlyName");
if (processes.Length == 0)
{
Process.Start(@"Path\App.exe");
}
else
{
if (processes.Length > 1)
{
for (int i = 1; i < processes.Length; i++)
{
processes[i].CloseMainWindow())
processes[i].Close();
}
}
}
IntPtr wndIntPtr = User32.FindWindow(null, "Caption");
User32.ShowWindow(parentIntPtr, User32.ShowWindowEnum.ShowNormal);
User32.SetForegroundWindow(wndIntPtr);
My problem is:
If they (the possible multiple instances of the other app) have been used and then minimized to system tray the title of the window might have been changed, so I can't be for 100% sure which string I have to give to find the window.
What I have tried:
To solve the problem with the dynamic caption I have targeted a child button that always is there and is inmmutable, then I can get the handle to the parent with
GetAncestor
from the WinApi.h
But the problem persists when minimized to the system tray.
The returned
Process.MainWindowHandle
is 0x00000000 if in SystemTray.
I have used the Spy++ and I see that the handles don't vary at all (and they are not
IntPtr.Zero
as said in
Processes[]
), no matter how many times I minimize to system tray and restore manually. They are just grayed out when in systemtray. Typing the handle manually as a test in:
IntPtr wndIntPtr = (IntPtr) 0x000D0216;
User32.ShowWindow(parentIntPtr, User32.ShowWindowEnum.ShowNormal);
User32.SetForegroundWindow(wndIntPtr);
or
with the other method from the child:
IntPtr parentIntPtr = User32.GetAncestor((IntPtr)0x000C0556, User32.GetAncestorFlags.GetParent);
Then it works like charm, I can restore and continue using them as I want.
So I started to search for alternatives:
I have tried the
FindWindowExA
too but so far I didn't manage to make
IntPtr childIntPtr = User32.FindWindowExA(IntPtr.Zero, IntPtr.Zero, null, "Help");
return something usable.
In spy++ the process contains two threads and one of them manages the windows,
[+]Process ID AppFriendlyName
[+] Thread ID AppFriendlyName
[] Window Handle1 Caption1 #32770 (DialogField)
[] Window Handle2 Caption2 Button
[] Window Handle3 Help Button <<<< Child for the workaround above
[] Window Handle4 Caption4 Button
...
I thought maybe if I get the ThreadsCollection I can later get the list of the "managed" windows... but no, it doesn't work like this. I can't get any useful info for my case once I have the threads.
I have looked into
ProcessModule
but I didn't found anything usefull for my problem.
I have found the MSDN info for the
EnumThreadWindows
but couldn't test it throughfully yet.
I know it is possible, it must be. But I am struggling to find the correct function to get what I want (or maybe is the FindWindowExA the right one, but I am using it wrong)
TL;DR;
1) If I open the other programm, I save the handle at the beginning and no problem at all
2) If the other program (no matter how many instances) haven't changed the title of the main window, I can still find them and interact with them
3) If the other program have changed the title and is minimized to the tray... this is where I am struggling and need help with
Any hint? Which Method should I focus to / search for?
What I will try next:
I suppose the best option I have is to use the
System.Diagnostics.Process
to get the first list, work with it until only the intances giving me problems remain. Then
Process.Threads
to get all the threads of each process. Then the
EnumThreadWindows
to find all nonchild windows and iterate again with
EnumChildWindows
to try to find my inmutable "XXX - Help" button to break all loops, keeping the active handles to continue working with.
Is this a correct approach? Or am I overengineering it a bit too much?