Click here to Skip to main content
15,879,326 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
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:

C#
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();
      }
   }
}

// I have the [DllImport("user32.dll")] and the needed Signatures.
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:
C#
IntPtr wndIntPtr = (IntPtr) 0x000D0216;
User32.ShowWindow(parentIntPtr, User32.ShowWindowEnum.ShowNormal);
User32.SetForegroundWindow(wndIntPtr);
or
with the other method from the child:
C#
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
C#
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?
Posted
Updated 22-Sep-20 10:33am
v6
Comments
Nelek 22-Sep-20 15:38pm    
Just in case it is not clear in the question. The minimized to tray is not my app, it is the one I want to find and show to interact with.
honey the codewitch 15-Oct-20 9:49am    
I think you should try your next approach. It's what I would have suggested. Keep in mind you're trying to interact on some level with an app that wasn't designed to interact with your app so anything you do is going to be somewhat ugly. Windows has crappy window finding APIs - that's the bottom line. Usually an app will be designed for interaction and they will publish an API into the Running Object Table, obviating the need for window based interaction. Even if they relied on windowing for IPC the server app would publish its window in some way known to the client so the client knew how to get to it later. You don't have luxury. Given all that, you're on the right track.
Nelek 15-Oct-20 13:27pm    
Thanks for the confirmation, at least I know it is not the wrong direction.

The solution is ugly at all levels (hooking keys and mouse clicks with Win32) but is the only way I could think of, there is no API and I have found no other possibility to interact with it (I even asked them, but nothing).
honey the codewitch 15-Oct-20 13:59pm    
About the best you can hope for is make a facade out of this and expose an API for it so you don't pollute more of your code than absolutely necessary.
Nelek 15-Oct-20 14:15pm    
Not sure I get your point, sorry.

My idea is to have a class that handles all of the Win32 stuff, another one with the necessary logic to handle the correct keys / clicks and a small GUI to choose which tasks are needed. (I.e. timestamps to start something, sleep functionality and a couple of things more). I know the risks about "working" with keys / mouse simulations, but as it is only for private local usage, I think it is worth a try (besides... I learn in the process too ;))

1 solution

Don't minimize to the tray, just hook the tray.

NotifyIcon.DoubleClick Event (System.Windows.Forms) | Microsoft Docs[^]

If the main requirement is to limit instances:

Single Instance Applications in .NET[^]
 
Share this answer
 
v2
Comments
Nelek 18-Sep-20 18:50pm    
I am not minimizing it myself (just now only for the tests). The minimized app is not mine, my app is the scheduler that wants to show them back as window if minimized to close it or to use it (if the only instance left).

I'll check your links tomorrow. Thank you

EDIT: I just edited the text of the question, I hope now is clearer
Nelek 22-Sep-20 15:36pm    
I have had a look to the links. Pity is, it doesn't help me, these are things for your app being involved with the tray. And I want to find and interact with another app from within mine (mine is not minimized, the other app might be).
[no name] 23-Sep-20 20:21pm    
Are you saying the "title changes" are totally random and you can see no pattern? (Task Manager at least shows all Word documents with "... - Word" in the title). And you don't know the .exe name?

If you enumerate all processes, you can get the .exe or dll name (ProcessModule).
Nelek 24-Sep-20 15:20pm    
I know the exe name and the process, they don't change, but the title can be realtivelly random. Like document name in word / notepad.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900