Свернутые C # окна не возвращаются при вызове System.Diagnostics.Process.GetProcesses () - PullRequest
0 голосов
/ 29 октября 2018

Я пытаюсь найти свернутое окно и показать его.

Программа может быть загружена с Samsung и называется «SideSync». Чтобы полностью повторить мой вопрос, вам необходимо установить его, а также иметь телефон Samsung для подключения к компьютеру.

Вот снимок экрана, на котором он полностью настроен и работает:

enter image description here

Обратите внимание, что есть два окна, A и B. Я использовал инструмент под названием Microsoft Inspect, чтобы определить, что два окна программы являются обычными окнами. У них нет родительских отношений между детьми. Однако, когда я запускаю SideSync, появляется только окно A. Затем я должен нажать «Экран телефона», после чего появится окно B (в дополнение к окну A). Это может быть ключом к решению этой проблемы? Посмотрим.

Вот оба окна, которые отображаются в Microsoft Inspect:

enter image description here

Оба окна имеют заголовки окон. Используя приведенный ниже код, я могу получить Process окна (что и является моей целью).

Код сервера:

public static Process GetProcessByWindowTitle(string windowTitleContains)
{
    foreach (var windowProcess in GetWindowProcesses())
        if (windowProcess.MainWindowTitle.Contains(windowTitleContains))
            return windowProcess;

    return null;
}

Однако происходит странное поведение. GetProcessByWindowTitle() вернет ОДИН, но не ОБА процессов. Я предполагаю, что, поскольку есть два окна, должно быть два процесса.

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

Например, если я последний раз щелкнул Окно A; тогда GetProcessByWindowTitle("SideSync") вернет Process, но тогда GetProcessByWindowTitle("SAMSUNG") вернет void.

... и наоборот, если я в последний раз щелкнул Окно B, GetProcessByWindowTitle("SideSync") вернет void, но GetProcessByWindowTitle("SAMSUNG") вернет Process.

Код клиента:

[Ignore("Requires starting SideSync and clicking one of the windows. Only the last clicked will return a Process.")]
[Test]
public void NonMinimizedWindowProcessIsDetected()
{

    Process p1 = Windows.GetProcessByWindowTitle("SAMSUNG");

    if(p1==null) { Console.WriteLine("SAMSUNG process is null.");}
    else { Console.WriteLine("SAMSUNG process detected.");}

    Process p2 = Windows.GetProcessByWindowTitle("SideSync");

    if (p2 == null) { Console.WriteLine("SideSync process is null."); }
    else { Console.WriteLine("SideSync process detected."); }
}

Моя цель - показать Окно Б. Моя проблема в том, что это возможно только в том случае, если я нажму на него последним, что создает нежелательную зависимость Я хочу показать окно B независимо от порядка кликов.

1 Ответ

0 голосов
/ 01 ноября 2018

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

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

while (true)
{
    var processes = Process.GetProcesses();

    foreach (var process in processes)
    {
        if (process.ProcessName != "SideSync")
            continue;

        Console.WriteLine($"{process.ProcessName}, {process.MainWindowTitle}, {process.MainWindowHandle.ToString()}");
    }

    Thread.Sleep(1000);
}

В моем случае MainWindowTitle менялось между разными названиями, например:

SideSync, SideSync, 1313728
SideSync, SideSync, 1313728
SideSync, SideSync, 1313728
SideSync, SideSync, 1313728
SideSync, SideSync, 1313728
SideSync, Notifier, 1903168
SideSync, Notifier, 1903168
SideSync, Notifier, 1903168
SideSync, Notifier, 1903168
SideSync, Notifier, 1903168
SideSync, Notifier, 1903168
SideSync, Notifier, 1903168
SideSync, Notifier, 1903168
SideSync, Notifier, 1903168
SideSync, Samsung Galaxy S7, 3082852
SideSync, Samsung Galaxy S7, 3082852
SideSync, Samsung Galaxy S7, 3082852
SideSync, Samsung Galaxy S7, 3082852
SideSync, ToolTip, 3148196
SideSync, Samsung Galaxy S7, 3082852
SideSync, Samsung Galaxy S7, 3082852
SideSync, Samsung Galaxy S7, 3082852
SideSync, Samsung Galaxy S7, 3082852
SideSync, ToolTip, 3148196
SideSync, ToolTip, 3148196
SideSync, Samsung Galaxy S7, 3082852
SideSync, Samsung Galaxy S7, 3082852
SideSync, SideSync, 1313728
SideSync, SideSync, 1313728
SideSync, SideSync, 1313728
SideSync, SideSync, 1313728
SideSync, SideSync, 1313728
SideSync, SideSync, 1313728
SideSync, SideSync, 1313728
SideSync, SideSync, 1313728

Как вы можете видеть, выходные данные также включают заголовки, такие как Уведомитель или Подсказка , которые появляются, когда появляется Окно уведомлений или когда вы наводите указатель мыши на значок в приложение SideSync . Как можно видеть на выходе, MainWindowHandle активного окна тоже меняется.

Так что, по моему мнению, вам просто нужно получить SideSync процесс с Process.GetProcessesByName("SideSync"), больше ничего.

Надеюсь, это поможет; -)

Обновление:

Согласно комментарию ОП, ему нужен способ открыть одно конкретное окно процесса SideSync , независимого от которого он был открыт в последний раз. Чтобы добиться этого, первым шагом является поиск соответствующих дескрипторов окон, которые принадлежат процессу SideSync .

Я основал следующий код на коде ответа от cREcker , который основал свой ответ на ресурсе Получение списка всех открытых окон .

Метод GetOpenWindowsByProcessId следующего класса позволяет получить дескриптор всех окон, принадлежащих указанному идентификатору процесса.

public static class OpenWindowGetter
{
    public static IDictionary<string, IntPtr> GetOpenWindowsByProcessId(int processId)
    {
        IntPtr shellWindow = GetShellWindow();
        Dictionary<string, IntPtr> windows = new Dictionary<string, IntPtr>();

        EnumWindows(delegate (IntPtr hWnd, int lParam)
        {
            uint ownerProcessId;
            GetWindowThreadProcessId(hWnd, out ownerProcessId);

            if (ownerProcessId != processId)
                return true;

            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);
            windows[builder.ToString()] = hWnd;

            return true;

        }, 0);

        return windows;
    }

    private delegate bool EnumWindowsProc(IntPtr hWnd, int lParam);

    [DllImport("USER32.DLL")]
    private static extern bool EnumWindows(EnumWindowsProc enumFunc, int lParam);

    [DllImport("USER32.DLL")]
    private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);

    [DllImport("USER32.DLL")]
    private static extern int GetWindowTextLength(IntPtr hWnd);

    [DllImport("USER32.DLL")]
    private static extern bool IsWindowVisible(IntPtr hWnd);

    [DllImport("USER32.DLL")]
    private static extern IntPtr GetShellWindow();

    [DllImport("user32.dll", SetLastError = true)]
    private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
}

Кроме того, нам понадобится способ «показать» окно.

private const int SW_SHOWNORMAL = 1;
private const int SW_SHOWMINIMIZED = 2;
private const int SW_SHOWMAXIMIZED = 3;

[DllImport("user32.dll")]
private static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);

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

private static void ShowPhoneScreenWindow()
{
    const string PROCESSNAME = "sidesync";
    const string WINDOWTITLE = "samsung";

    var process = Process.GetProcessesByName(PROCESSNAME).FirstOrDefault();

    if (process == null)
        throw new InvalidOperationException($"No process with name {PROCESSNAME} running.");

    var windowHandles = OpenWindowGetter.GetOpenWindowsByProcessId(process.Id);
    IntPtr windowHandle = IntPtr.Zero;

    foreach (var key in windowHandles.Keys)
        if (key.ToLower().StartsWith(WINDOWTITLE))
        {
            windowHandle = windowHandles[key];
            break;
        }

    if (windowHandle == IntPtr.Zero)
        throw new InvalidOperationException($"No window with title {WINDOWTITLE} hosted.");

    ShowWindowAsync(windowHandle, SW_SHOWNORMAL);
}
...