Nelek Ответов: 1

Восстановление из системного трея, изначально не принадлежащего окну


TL;DR; В конце концов

Я пытаюсь сделать крошечный планировщик для своей жены, чтобы автоматизировать некоторые задачи, и у меня возникают некоторые проблемы с взаимодействием с другим приложением, когда оно свернуто в системный трей.

До тех пор, пока я запускаю другое приложение в своей программе с помощью System.Diagnostics.Process.Start(Path\App) Я могу достать MainWindowHandle и тогда мне все равно, где он будет позже, я могу взаимодействовать с ним, используя ранее сохраненный дескриптор без каких-либо проблем.
Другое приложение-MDI, но я не хочу иметь более одного экземпляра одновременно, чтобы избежать проблем (например, запись Блокнота в разных экземплярах в один и тот же файл), поэтому я начинаю свои проверки с:

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);

Моя проблема в том, что:
Если они (возможные несколько экземпляров другого приложения) были использованы, а затем свернуты в системный трей, название окна может быть изменено, поэтому я не могу быть на 100% уверен, какую строку я должен дать, чтобы найти окно.

Что я уже пробовал:

Чтобы решить проблему с динамическим заголовком, я нацелился на дочернюю кнопку, которая всегда находится там и является неизменяемой, а затем я могу передать дескриптор родительскому элементу с помощью GetAncestor от по WinAPI.ч

Но проблема сохраняется, когда она свернута в системный трей.

Вернувшийся Process.MainWindowHandle это 0x00000000, если в SystemTray.

Я использовал Spy++ и вижу, что дескрипторы вообще не меняются (и это не так IntPtr.Zero как сказано в Processes[]), независимо от того, сколько раз я сворачиваю в системный трей и восстанавливаю вручную. Они просто серые, когда находятся в systemtray. Введя обрабатывать вручную, так как тест в:
IntPtr wndIntPtr = (IntPtr) 0x000D0216;
User32.ShowWindow(parentIntPtr, User32.ShowWindowEnum.ShowNormal);
User32.SetForegroundWindow(wndIntPtr);
или
с помощью другого метода от ребенка:
IntPtr parentIntPtr = User32.GetAncestor((IntPtr)0x000C0556, User32.GetAncestorFlags.GetParent);

Тогда он работает как шарм, я могу восстановить и продолжать использовать их, как я хочу.

Поэтому я начал искать альтернативы:

Я уже пробовал FindWindowExA тоже но пока мне этого сделать не удалось
IntPtr childIntPtr = User32.FindWindowExA(IntPtr.Zero, IntPtr.Zero, null, "Help");
верните что-нибудь полезное.

В spy++ процесс содержит два потока, и один из них управляет окнами,
[+]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
...


Я подумал, что, возможно, если я получу ThreadsCollection, то позже смогу получить список "управляемых" окон... но нет, это не работает так. Я не могу получить никакой полезной информации для моего дела, как только у меня есть нити.

Я заглянул в него. ProcessModule но я не нашел ничего полезного для своей проблемы.

Я нашел информацию MSDN для EnumThreadWindows но пока не мог проверить это насквозь.

Я знаю, что это возможно, так и должно быть. Но я изо всех сил пытаюсь найти правильную функцию, чтобы получить то, что я хочу (или, может быть, FindWindowExA правильный, но я использую его неправильно)


TL;DR;
1) если я открываю другую программу, то сохраняю дескриптор в самом начале и никаких проблем вообще нет
2) если другая программа (независимо от того, сколько экземпляров) не изменила заголовок главного окна, я все равно могу найти их и взаимодействовать с ними
3) Если другая программа изменила название и свернута в трей... вот где я борюсь и нуждаюсь в помощи

Какой-нибудь намек? На каком методе я должен сосредоточиться / искать?


Что я попробую сделать дальше:
Я полагаю, что лучший вариант, который у меня есть, - это использовать System.Diagnostics.Process чтобы получить первый список, работайте с ним до тех пор, пока не останутся только интенции, дающие мне проблемы. Затем Process.Threads чтобы получить все потоки каждого процесса. А потом ... EnumThreadWindows чтобы найти все не дочерние окна и повторить итерацию с помощью EnumChildWindows чтобы попытаться найти мою неизменяемую кнопку "XXX - Help", чтобы разорвать все петли, сохраняя активные ручки для продолжения работы.

Является ли это правильным подходом? Или я чересчур его переоцениваю?

Nelek

На всякий случай это не совсем понятно в вопросе. Свернутый в лоток-это нет мое приложение-это то, что я хочу найти и показать для взаимодействия.

honey the codewitch

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

Спасибо за подтверждение, по крайней мере, я знаю, что это не неправильное направление.

Решение уродливо на всех уровнях (зацепление клавиш и щелчков мыши с помощью Win32), но это единственный способ, который я мог придумать, нет API, и я не нашел никакой другой возможности взаимодействовать с ним (я даже спрашивал их, но ничего).

honey the codewitch

Самое лучшее, на что вы можете надеяться, - это сделать из этого фасад и выставить для него API, чтобы вы не загрязняли больше своего кода, чем это абсолютно необходимо.

Nelek

Не уверен, что я понял вашу точку зрения, извините.

Моя идея состоит в том, чтобы иметь класс, который обрабатывает все вещи Win32, еще один с необходимой логикой для обработки правильных клавиш / щелчков и небольшим графическим интерфейсом для выбора необходимых задач. (То есть временные метки, чтобы начать что-то, функциональность сна и еще несколько вещей). Я знаю риски, связанные с "работой" с моделированием клавиш / мыши, но поскольку это только для частного локального использования, я думаю, что стоит попробовать (кроме того... Я тоже учусь в этом процессе ;))

honey the codewitch

Вы создаете именно то, о чем я говорю. Не беспокойся об этом. =)

Я имел в виду, что лучше всего иметь все хаки за чем-то настолько "чистым", насколько вы можете создать, но похоже, что вы делаете это.

Nelek

По крайней мере, я на это надеюсь.
Не уверен, что мне следует собрать все вместе и написать что-то, когда я закончу, большинство частей на самом деле являются вещами, которые можно найти в сети, но нет места со всей информацией сразу.

honey the codewitch

Я должен добавить, что EnumWindows() и его производные не дают вам условия остановки. Они будут продолжать звать вас обратно с окнами, пока их больше не будет, но вы не узнаете, когда их больше не будет, потому что они никогда не скажут вам. Я устанавливаю тайм-аут для этого.

Nelek

Я уже читал о функции обратного вызова. Я не знаю, правильно ли я его понял, но во время исполнения он доставляет IntPtr hWnd, поэтому я могу попытаться проверить, что это такое, и изменить возврат на false сам, если текущий дескриптор принадлежит кнопке, которую я ищу (игнорируя остальные окна, которые еще не перечислены).

Затем получите его родителя, поработайте с ним, закройте его и начните все сначала, если в списке GetProcess есть еще другие экземпляры.

1 Ответов

Рейтинг:
1

Gerry Schmitz

Не сворачивайте к лотку, просто зацепите лоток.

Уведомь меня об этом.Событие DoubleClick (System.Окна.Формы) | Microsoft Docs[^]

Если главным требованием является ограничение экземпляров:

Приложения с одним экземпляром в .NET[^]


Nelek

Я не преуменьшаю его сам (только сейчас только для тестов). Свернутое приложение не мое, мое приложение-это планировщик, который хочет показать их обратно в виде окна, если свернуто, чтобы закрыть его или использовать его (если остался единственный экземпляр).

Я проверю ваши ссылки завтра. Спасибо

EDIT: я только что отредактировал текст вопроса, надеюсь, теперь он стал более ясным

Nelek

Я посмотрел на ссылки. Жаль, что это мне не помогает, это вещи для меня. твой приложение, связанное с лотком. И я хочу найти и взаимодействовать с другим приложением изнутри моего (мое не свернуто, другое приложение может быть).

Gerry Schmitz

Вы хотите сказать, что "изменения названия" совершенно случайны, и вы не видите никакой закономерности? (Диспетчер задач, по крайней мере, показывает все документы Word с помощью "... - Слово" в названии). И вы не знаете, как называется файл .exe?

Если перечислить все процессы, то можно получить имя .exe или dll (ProcessModule).

Nelek

Я знаю имя exe и процесс, они не меняются, но название может быть действительно случайным. Например, имя документа в word / notepad.